]> git.proxmox.com Git - rustc.git/commitdiff
Merge tag 'upstream/1.7.0+dfsg1'
authorXimin Luo <infinity0@debian.org>
Sat, 5 Mar 2016 13:09:44 +0000 (14:09 +0100)
committerXimin Luo <infinity0@debian.org>
Sat, 5 Mar 2016 13:09:44 +0000 (14:09 +0100)
1123 files changed:
CONTRIBUTING.md
COPYRIGHT
LICENSE-MIT
README.md
RELEASES.md
configure
mk/cfg/arm-unknown-linux-gnueabi.mk
mk/cfg/i686-unknown-freebsd.mk
mk/cfg/le32-unknown-nacl.mk [new file with mode: 0644]
mk/cfg/powerpc64-unknown-linux-gnu.mk [new file with mode: 0644]
mk/cfg/powerpc64le-unknown-linux-gnu.mk [new file with mode: 0644]
mk/cfg/x86_64-unknown-bitrig.mk
mk/cfg/x86_64-unknown-openbsd.mk
mk/crates.mk
mk/llvm.mk
mk/main.mk
mk/platform.mk
mk/rt.mk
mk/target.mk
mk/tests.mk
src/compiletest/runtest.rs
src/compiletest/util.rs
src/doc/book/README.md
src/doc/book/SUMMARY.md
src/doc/book/associated-types.md
src/doc/book/bibliography.md
src/doc/book/casting-between-types.md
src/doc/book/choosing-your-guarantees.md
src/doc/book/closures.md
src/doc/book/concurrency.md
src/doc/book/crates-and-modules.md
src/doc/book/custom-allocators.md
src/doc/book/dining-philosophers.md [deleted file]
src/doc/book/documentation.md
src/doc/book/effective-rust.md
src/doc/book/enums.md
src/doc/book/error-handling.md
src/doc/book/ffi.md
src/doc/book/functions.md
src/doc/book/generics.md
src/doc/book/getting-started.md
src/doc/book/guessing-game.md
src/doc/book/iterators.md
src/doc/book/learn-rust.md
src/doc/book/lifetimes.md
src/doc/book/macros.md
src/doc/book/match.md
src/doc/book/method-syntax.md
src/doc/book/nightly-rust.md
src/doc/book/operators-and-overloading.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/rust-inside-other-languages.md [deleted file]
src/doc/book/strings.md
src/doc/book/structs.md
src/doc/book/syntax-and-semantics.md
src/doc/book/syntax-index.md
src/doc/book/testing.md
src/doc/book/the-stack-and-the-heap.md
src/doc/book/trait-objects.md
src/doc/book/traits.md
src/doc/book/unsafe.md
src/doc/book/unsized-types.md
src/doc/book/variable-bindings.md
src/doc/book/vectors.md
src/doc/complement-design-faq.md
src/doc/complement-lang-faq.md
src/doc/complement-project-faq.md
src/doc/index.md
src/doc/nomicon/lifetime-elision.md
src/doc/nomicon/lifetimes.md
src/doc/nomicon/subtyping.md
src/doc/nomicon/vec-dealloc.md
src/doc/nomicon/vec-final.md
src/doc/nomicon/vec-insert-remove.md
src/doc/nomicon/vec-zsts.md
src/doc/reference.md
src/etc/apple-darwin.supp
src/etc/dec2flt_table.py
src/etc/htmldocck.py
src/etc/maketest.py
src/etc/mklldeps.py
src/etc/unicode.py
src/etc/x86.supp
src/jemalloc/COPYING
src/jemalloc/ChangeLog
src/jemalloc/INSTALL
src/jemalloc/Makefile.in
src/jemalloc/VERSION [deleted file]
src/jemalloc/bin/jemalloc-config.in [new file with mode: 0644]
src/jemalloc/bin/jeprof.in [new file with mode: 0644]
src/jemalloc/bin/pprof [deleted file]
src/jemalloc/configure
src/jemalloc/configure.ac
src/jemalloc/doc/jemalloc.xml.in
src/jemalloc/include/jemalloc/internal/arena.h
src/jemalloc/include/jemalloc/internal/atomic.h
src/jemalloc/include/jemalloc/internal/base.h
src/jemalloc/include/jemalloc/internal/chunk.h
src/jemalloc/include/jemalloc/internal/chunk_dss.h
src/jemalloc/include/jemalloc/internal/chunk_mmap.h
src/jemalloc/include/jemalloc/internal/ctl.h
src/jemalloc/include/jemalloc/internal/extent.h
src/jemalloc/include/jemalloc/internal/hash.h
src/jemalloc/include/jemalloc/internal/huge.h
src/jemalloc/include/jemalloc/internal/jemalloc_internal.h.in
src/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h
src/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h.in
src/jemalloc/include/jemalloc/internal/mutex.h
src/jemalloc/include/jemalloc/internal/pages.h [new file with mode: 0644]
src/jemalloc/include/jemalloc/internal/private_symbols.txt
src/jemalloc/include/jemalloc/internal/prng.h
src/jemalloc/include/jemalloc/internal/prof.h
src/jemalloc/include/jemalloc/internal/ql.h
src/jemalloc/include/jemalloc/internal/qr.h
src/jemalloc/include/jemalloc/internal/quarantine.h
src/jemalloc/include/jemalloc/internal/rb.h
src/jemalloc/include/jemalloc/internal/rtree.h
src/jemalloc/include/jemalloc/internal/size_classes.sh
src/jemalloc/include/jemalloc/internal/stats.h
src/jemalloc/include/jemalloc/internal/tcache.h
src/jemalloc/include/jemalloc/internal/tsd.h
src/jemalloc/include/jemalloc/internal/util.h
src/jemalloc/include/jemalloc/jemalloc.sh
src/jemalloc/include/jemalloc/jemalloc_defs.h.in
src/jemalloc/include/jemalloc/jemalloc_macros.h.in
src/jemalloc/include/jemalloc/jemalloc_protos.h.in
src/jemalloc/include/jemalloc/jemalloc_typedefs.h.in
src/jemalloc/include/msvc_compat/C99/inttypes.h [deleted file]
src/jemalloc/include/msvc_compat/strings.h
src/jemalloc/include/msvc_compat/windows_extra.h [new file with mode: 0644]
src/jemalloc/jemalloc.pc.in
src/jemalloc/src/arena.c
src/jemalloc/src/base.c
src/jemalloc/src/chunk.c
src/jemalloc/src/chunk_dss.c
src/jemalloc/src/chunk_mmap.c
src/jemalloc/src/ckh.c
src/jemalloc/src/ctl.c
src/jemalloc/src/extent.c
src/jemalloc/src/huge.c
src/jemalloc/src/jemalloc.c
src/jemalloc/src/mutex.c
src/jemalloc/src/pages.c [new file with mode: 0644]
src/jemalloc/src/prof.c
src/jemalloc/src/quarantine.c
src/jemalloc/src/rtree.c
src/jemalloc/src/stats.c
src/jemalloc/src/tcache.c
src/jemalloc/src/tsd.c
src/jemalloc/src/util.c
src/jemalloc/src/zone.c
src/jemalloc/test/include/test/jemalloc_test.h.in
src/jemalloc/test/include/test/jemalloc_test_defs.h.in
src/jemalloc/test/include/test/math.h
src/jemalloc/test/include/test/mq.h
src/jemalloc/test/include/test/test.h
src/jemalloc/test/include/test/thd.h
src/jemalloc/test/include/test/timer.h
src/jemalloc/test/integration/chunk.c
src/jemalloc/test/integration/mallocx.c
src/jemalloc/test/integration/overflow.c [new file with mode: 0644]
src/jemalloc/test/integration/rallocx.c
src/jemalloc/test/integration/xallocx.c
src/jemalloc/test/src/mq.c [new file with mode: 0644]
src/jemalloc/test/src/test.c
src/jemalloc/test/src/timer.c
src/jemalloc/test/stress/microbench.c
src/jemalloc/test/unit/SFMT.c
src/jemalloc/test/unit/atomic.c
src/jemalloc/test/unit/bitmap.c
src/jemalloc/test/unit/ckh.c
src/jemalloc/test/unit/junk.c
src/jemalloc/test/unit/junk_alloc.c [new file with mode: 0644]
src/jemalloc/test/unit/junk_free.c [new file with mode: 0644]
src/jemalloc/test/unit/lg_chunk.c [new file with mode: 0644]
src/jemalloc/test/unit/mallctl.c
src/jemalloc/test/unit/mq.c
src/jemalloc/test/unit/prof_active.c
src/jemalloc/test/unit/prof_gdump.c
src/jemalloc/test/unit/prof_reset.c
src/jemalloc/test/unit/prof_thread_name.c
src/jemalloc/test/unit/rtree.c
src/jemalloc/test/unit/size_classes.c [new file with mode: 0644]
src/jemalloc/test/unit/stats.c
src/jemalloc/test/unit/tsd.c
src/jemalloc/test/unit/zero.c
src/liballoc/arc.rs
src/liballoc/boxed.rs
src/liballoc/lib.rs
src/liballoc/oom.rs [new file with mode: 0644]
src/liballoc/raw_vec.rs
src/liballoc/rc.rs
src/liballoc_jemalloc/lib.rs
src/liballoc_system/lib.rs
src/libarena/lib.rs
src/libbacktrace/ChangeLog
src/libbacktrace/ChangeLog.jit
src/libbacktrace/Makefile.am
src/libbacktrace/Makefile.in
src/libbacktrace/alloc.c
src/libbacktrace/ansidecl.h
src/libbacktrace/atomic.c
src/libbacktrace/backtrace-supported.h.in
src/libbacktrace/backtrace.c
src/libbacktrace/backtrace.h
src/libbacktrace/btest.c
src/libbacktrace/config.guess [deleted file]
src/libbacktrace/config.sub [changed mode: 0755->0644]
src/libbacktrace/configure
src/libbacktrace/configure.ac
src/libbacktrace/dwarf.c
src/libbacktrace/dwarf2.def
src/libbacktrace/dwarf2.h
src/libbacktrace/elf.c
src/libbacktrace/fileline.c
src/libbacktrace/filenames.h
src/libbacktrace/gstdint.h [deleted file]
src/libbacktrace/hashtab.h
src/libbacktrace/install-sh [changed mode: 0755->0644]
src/libbacktrace/internal.h
src/libbacktrace/ltmain.sh
src/libbacktrace/mmap.c
src/libbacktrace/mmapio.c
src/libbacktrace/nounwind.c
src/libbacktrace/pecoff.c
src/libbacktrace/posix.c
src/libbacktrace/print.c
src/libbacktrace/read.c
src/libbacktrace/simple.c
src/libbacktrace/sort.c
src/libbacktrace/state.c
src/libbacktrace/stest.c
src/libbacktrace/unknown.c
src/libcollections/binary_heap.rs
src/libcollections/borrow.rs
src/libcollections/btree/map.rs
src/libcollections/btree/mod.rs
src/libcollections/btree/node.rs
src/libcollections/btree/search.rs [new file with mode: 0644]
src/libcollections/btree/set.rs
src/libcollections/enum_set.rs
src/libcollections/fmt.rs
src/libcollections/lib.rs
src/libcollections/linked_list.rs
src/libcollections/range.rs
src/libcollections/slice.rs
src/libcollections/str.rs
src/libcollections/string.rs
src/libcollections/vec.rs
src/libcollections/vec_deque.rs
src/libcollectionstest/binary_heap.rs
src/libcollectionstest/btree/map.rs
src/libcollectionstest/btree/set.rs
src/libcollectionstest/str.rs
src/libcore/any.rs
src/libcore/char.rs
src/libcore/fmt/builders.rs
src/libcore/fmt/mod.rs
src/libcore/fmt/num.rs
src/libcore/hash/mod.rs
src/libcore/intrinsics.rs
src/libcore/iter.rs
src/libcore/lib.rs
src/libcore/macros.rs
src/libcore/marker.rs
src/libcore/mem.rs
src/libcore/nonzero.rs
src/libcore/num/bignum.rs
src/libcore/num/dec2flt/algorithm.rs
src/libcore/num/dec2flt/mod.rs
src/libcore/num/dec2flt/parse.rs
src/libcore/num/dec2flt/rawfp.rs
src/libcore/num/dec2flt/table.rs
src/libcore/num/f32.rs
src/libcore/num/f64.rs
src/libcore/num/float_macros.rs
src/libcore/num/flt2dec/mod.rs
src/libcore/num/int_macros.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/option.rs
src/libcore/ptr.rs
src/libcore/result.rs
src/libcore/simd.rs [deleted file]
src/libcore/simd_old.rs [deleted file]
src/libcore/slice.rs
src/libcore/str/mod.rs
src/libcoretest/any.rs
src/libcoretest/char.rs
src/libcoretest/iter.rs
src/libcoretest/lib.rs
src/libcoretest/num/dec2flt/mod.rs
src/libcoretest/num/flt2dec/mod.rs
src/libcoretest/num/mod.rs
src/libflate/lib.rs
src/libfmt_macros/lib.rs
src/libgetopts/lib.rs
src/libgraphviz/lib.rs
src/liblibc/.travis.yml
src/liblibc/Cargo.toml
src/liblibc/ci/cargo-config
src/liblibc/ci/mips/Dockerfile [new file with mode: 0644]
src/liblibc/ci/rumprun/Dockerfile [new file with mode: 0644]
src/liblibc/ci/run-travis.sh
src/liblibc/ci/run.sh
src/liblibc/libc-test/Cargo.lock [new file with mode: 0644]
src/liblibc/libc-test/build.rs
src/liblibc/src/dox.rs
src/liblibc/src/lib.rs
src/liblibc/src/macros.rs
src/liblibc/src/unix/bsd/apple/mod.rs
src/liblibc/src/unix/bsd/freebsdlike/dragonfly.rs
src/liblibc/src/unix/bsd/freebsdlike/freebsd.rs
src/liblibc/src/unix/bsd/freebsdlike/mod.rs
src/liblibc/src/unix/bsd/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/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.rs
src/liblibc/src/unix/notbsd/linux/other/b32/arm.rs
src/liblibc/src/unix/notbsd/linux/other/b32/mod.rs
src/liblibc/src/unix/notbsd/linux/other/b32/x86.rs
src/liblibc/src/unix/notbsd/linux/other/b64/aarch64.rs
src/liblibc/src/unix/notbsd/linux/other/b64/mod.rs
src/liblibc/src/unix/notbsd/linux/other/b64/powerpc64.rs [new file with mode: 0644]
src/liblibc/src/unix/notbsd/linux/other/b64/x86_64.rs
src/liblibc/src/unix/notbsd/linux/other/mod.rs
src/liblibc/src/unix/notbsd/mod.rs
src/liblog/lib.rs
src/librand/chacha.rs
src/librand/isaac.rs
src/librand/lib.rs
src/librand/reseeding.rs
src/librbml/leb128.rs [new file with mode: 0644]
src/librbml/lib.rs
src/librbml/opaque.rs [new file with mode: 0644]
src/librustc/dep_graph/README.md [new file with mode: 0644]
src/librustc/dep_graph/dep_tracking_map.rs [new file with mode: 0644]
src/librustc/dep_graph/edges.rs [new file with mode: 0644]
src/librustc/dep_graph/mod.rs [new file with mode: 0644]
src/librustc/dep_graph/query.rs [new file with mode: 0644]
src/librustc/dep_graph/raii.rs [new file with mode: 0644]
src/librustc/dep_graph/thread.rs [new file with mode: 0644]
src/librustc/diagnostics.rs
src/librustc/front/map/collector.rs
src/librustc/front/map/definitions.rs
src/librustc/front/map/mod.rs
src/librustc/lib.rs
src/librustc/lint/builtin.rs
src/librustc/lint/context.rs
src/librustc/lint/mod.rs
src/librustc/middle/cfg/construct.rs
src/librustc/middle/cfg/graphviz.rs
src/librustc/middle/check_const.rs
src/librustc/middle/check_match.rs
src/librustc/middle/check_no_asm.rs [deleted file]
src/librustc/middle/check_rvalues.rs
src/librustc/middle/check_static_recursion.rs
src/librustc/middle/const_eval.rs
src/librustc/middle/cstore.rs
src/librustc/middle/dataflow.rs
src/librustc/middle/dead.rs
src/librustc/middle/def.rs
src/librustc/middle/dependency_format.rs
src/librustc/middle/entry.rs
src/librustc/middle/expr_use_visitor.rs
src/librustc/middle/implicator.rs
src/librustc/middle/infer/combine.rs
src/librustc/middle/infer/error_reporting.rs
src/librustc/middle/infer/freshen.rs
src/librustc/middle/infer/higher_ranked/mod.rs
src/librustc/middle/infer/mod.rs
src/librustc/middle/infer/region_inference/mod.rs
src/librustc/middle/infer/resolve.rs
src/librustc/middle/infer/unify_key.rs
src/librustc/middle/intrinsicck.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/subst.rs
src/librustc/middle/traits/coherence.rs
src/librustc/middle/traits/error_reporting.rs
src/librustc/middle/traits/fulfill.rs
src/librustc/middle/traits/mod.rs
src/librustc/middle/traits/object_safety.rs
src/librustc/middle/traits/project.rs
src/librustc/middle/traits/select.rs
src/librustc/middle/traits/structural_impls.rs
src/librustc/middle/ty/adjustment.rs
src/librustc/middle/ty/contents.rs
src/librustc/middle/ty/context.rs
src/librustc/middle/ty/error.rs
src/librustc/middle/ty/fast_reject.rs
src/librustc/middle/ty/flags.rs
src/librustc/middle/ty/fold.rs
src/librustc/middle/ty/ivar.rs
src/librustc/middle/ty/maps.rs [new file with mode: 0644]
src/librustc/middle/ty/mod.rs
src/librustc/middle/ty/outlives.rs
src/librustc/middle/ty/relate.rs
src/librustc/middle/ty/structural_impls.rs
src/librustc/middle/ty/sty.rs
src/librustc/middle/ty/trait_def.rs [new file with mode: 0644]
src/librustc/middle/ty/util.rs
src/librustc/middle/ty/wf.rs
src/librustc/mir/repr.rs
src/librustc/mir/visit.rs
src/librustc/session/config.rs
src/librustc/session/mod.rs
src/librustc/session/search_paths.rs
src/librustc/util/common.rs
src/librustc/util/ppaux.rs
src/librustc_back/lib.rs
src/librustc_back/rpath.rs
src/librustc_back/svh.rs
src/librustc_back/target/android_base.rs
src/librustc_back/target/apple_base.rs
src/librustc_back/target/apple_ios_base.rs
src/librustc_back/target/linux_base.rs
src/librustc_back/target/mod.rs
src/librustc_back/target/powerpc64_unknown_linux_gnu.rs [new file with mode: 0644]
src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs [new file with mode: 0644]
src/librustc_bitflags/lib.rs
src/librustc_borrowck/borrowck/check_loans.rs
src/librustc_borrowck/borrowck/fragments.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/mod.rs
src/librustc_borrowck/borrowck/move_data.rs
src/librustc_borrowck/diagnostics.rs
src/librustc_borrowck/graphviz.rs
src/librustc_borrowck/lib.rs
src/librustc_data_structures/fnv.rs
src/librustc_data_structures/graph/mod.rs
src/librustc_data_structures/lib.rs
src/librustc_data_structures/tuple_slice.rs [new file with mode: 0644]
src/librustc_data_structures/unify/mod.rs
src/librustc_data_structures/veccell/mod.rs [new file with mode: 0644]
src/librustc_driver/driver.rs
src/librustc_driver/lib.rs
src/librustc_driver/pretty.rs
src/librustc_driver/test.rs
src/librustc_front/fold.rs
src/librustc_front/hir.rs
src/librustc_front/intravisit.rs
src/librustc_front/lib.rs
src/librustc_front/lowering.rs
src/librustc_front/print/pprust.rs
src/librustc_front/util.rs
src/librustc_lint/builtin.rs
src/librustc_lint/lib.rs
src/librustc_lint/types.rs
src/librustc_lint/unused.rs
src/librustc_llvm/lib.rs
src/librustc_metadata/astencode.rs
src/librustc_metadata/common.rs
src/librustc_metadata/creader.rs
src/librustc_metadata/csearch.rs
src/librustc_metadata/decoder.rs
src/librustc_metadata/diagnostics.rs
src/librustc_metadata/encoder.rs
src/librustc_metadata/lib.rs
src/librustc_metadata/loader.rs
src/librustc_metadata/tls_context.rs [new file with mode: 0644]
src/librustc_metadata/tydecode.rs
src/librustc_metadata/tyencode.rs
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_rvalue.rs
src/librustc_mir/build/expr/into.rs
src/librustc_mir/build/into.rs
src/librustc_mir/build/matches/mod.rs
src/librustc_mir/build/matches/test.rs
src/librustc_mir/build/misc.rs
src/librustc_mir/build/mod.rs
src/librustc_mir/build/scope.rs
src/librustc_mir/graphviz.rs [new file with mode: 0644]
src/librustc_mir/graphviz/mod.rs [deleted file]
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/cx/to_ref.rs
src/librustc_mir/hair/mod.rs
src/librustc_mir/lib.rs
src/librustc_mir/mir_map.rs
src/librustc_mir/pretty.rs [new file with mode: 0644]
src/librustc_mir/transform/erase_regions.rs
src/librustc_mir/transform/simplify_cfg.rs
src/librustc_mir/transform/util.rs
src/librustc_passes/const_fn.rs [new file with mode: 0644]
src/librustc_passes/diagnostics.rs [new file with mode: 0644]
src/librustc_passes/lib.rs [new file with mode: 0644]
src/librustc_passes/no_asm.rs [new file with mode: 0644]
src/librustc_platform_intrinsics/lib.rs
src/librustc_plugin/build.rs
src/librustc_plugin/lib.rs
src/librustc_privacy/diagnostics.rs
src/librustc_privacy/lib.rs
src/librustc_resolve/build_reduced_graph.rs
src/librustc_resolve/diagnostics.rs
src/librustc_resolve/lib.rs
src/librustc_resolve/record_exports.rs [deleted file]
src/librustc_resolve/resolve_imports.rs
src/librustc_trans/back/archive.rs
src/librustc_trans/back/link.rs
src/librustc_trans/back/linker.rs
src/librustc_trans/back/lto.rs
src/librustc_trans/back/msvc/registry.rs
src/librustc_trans/back/write.rs
src/librustc_trans/lib.rs
src/librustc_trans/save/dump_csv.rs
src/librustc_trans/save/mod.rs
src/librustc_trans/trans/_match.rs
src/librustc_trans/trans/adt.rs
src/librustc_trans/trans/asm.rs
src/librustc_trans/trans/assert_dep_graph.rs [new file with mode: 0644]
src/librustc_trans/trans/base.rs
src/librustc_trans/trans/cabi.rs
src/librustc_trans/trans/cabi_powerpc64.rs [new file with mode: 0644]
src/librustc_trans/trans/callee.rs
src/librustc_trans/trans/closure.rs
src/librustc_trans/trans/common.rs
src/librustc_trans/trans/consts.rs
src/librustc_trans/trans/datum.rs
src/librustc_trans/trans/debuginfo/create_scope_map.rs
src/librustc_trans/trans/debuginfo/gdb.rs
src/librustc_trans/trans/debuginfo/metadata.rs
src/librustc_trans/trans/debuginfo/mod.rs
src/librustc_trans/trans/debuginfo/type_names.rs
src/librustc_trans/trans/disr.rs [new file with mode: 0644]
src/librustc_trans/trans/expr.rs
src/librustc_trans/trans/foreign.rs
src/librustc_trans/trans/glue.rs
src/librustc_trans/trans/inline.rs
src/librustc_trans/trans/intrinsic.rs
src/librustc_trans/trans/meth.rs
src/librustc_trans/trans/mir/block.rs
src/librustc_trans/trans/mir/constant.rs
src/librustc_trans/trans/mir/did.rs [new file with mode: 0644]
src/librustc_trans/trans/mir/lvalue.rs
src/librustc_trans/trans/mir/mod.rs
src/librustc_trans/trans/mir/operand.rs
src/librustc_trans/trans/mir/rvalue.rs
src/librustc_trans/trans/mir/statement.rs
src/librustc_trans/trans/mod.rs
src/librustc_trans/trans/monomorphize.rs
src/librustc_trans/trans/tvec.rs
src/librustc_trans/trans/type_of.rs
src/librustc_typeck/astconv.rs
src/librustc_typeck/check/_match.rs
src/librustc_typeck/check/assoc.rs
src/librustc_typeck/check/callee.rs
src/librustc_typeck/check/cast.rs
src/librustc_typeck/check/closure.rs
src/librustc_typeck/check/coercion.rs
src/librustc_typeck/check/compare_method.rs
src/librustc_typeck/check/dropck.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/op.rs
src/librustc_typeck/check/regionck.rs
src/librustc_typeck/check/wf.rs [deleted file]
src/librustc_typeck/check/wfcheck.rs
src/librustc_typeck/check/writeback.rs
src/librustc_typeck/coherence/mod.rs
src/librustc_typeck/coherence/orphan.rs
src/librustc_typeck/coherence/overlap.rs
src/librustc_typeck/coherence/unsafety.rs
src/librustc_typeck/collect.rs
src/librustc_typeck/diagnostics.rs
src/librustc_typeck/lib.rs
src/librustc_typeck/variance.rs
src/librustc_unicode/char.rs
src/librustc_unicode/lib.rs
src/librustc_unicode/tables.rs
src/librustc_unicode/u_str.rs
src/librustdoc/clean/inline.rs
src/librustdoc/clean/mod.rs
src/librustdoc/core.rs
src/librustdoc/doctree.rs
src/librustdoc/fold.rs
src/librustdoc/html/render.rs
src/librustdoc/html/static/rustdoc.css
src/librustdoc/lib.rs
src/librustdoc/test.rs
src/librustdoc/visit_ast.rs
src/libserialize/collection_impls.rs
src/libserialize/lib.rs
src/libstd/collections/hash/bench.rs
src/libstd/collections/hash/map.rs
src/libstd/collections/hash/set.rs
src/libstd/collections/hash/state.rs
src/libstd/collections/hash/table.rs
src/libstd/collections/mod.rs
src/libstd/dynamic_lib.rs
src/libstd/env.rs
src/libstd/ffi/c_str.rs
src/libstd/ffi/os_str.rs
src/libstd/fs.rs
src/libstd/io/buffered.rs
src/libstd/io/cursor.rs
src/libstd/io/error.rs
src/libstd/io/impls.rs
src/libstd/io/mod.rs
src/libstd/io/prelude.rs
src/libstd/io/stdio.rs
src/libstd/io/util.rs
src/libstd/lib.rs
src/libstd/memchr.rs [new file with mode: 0644]
src/libstd/net/addr.rs
src/libstd/net/ip.rs
src/libstd/net/mod.rs
src/libstd/net/parser.rs
src/libstd/net/tcp.rs
src/libstd/net/udp.rs
src/libstd/num/f32.rs
src/libstd/num/f64.rs
src/libstd/num/mod.rs
src/libstd/os/linux/raw.rs
src/libstd/os/openbsd/raw.rs
src/libstd/os/raw.rs
src/libstd/panic.rs [new file with mode: 0644]
src/libstd/panicking.rs
src/libstd/path.rs
src/libstd/prelude/mod.rs
src/libstd/primitive_docs.rs
src/libstd/process.rs
src/libstd/rand/mod.rs
src/libstd/rand/os.rs
src/libstd/rand/reader.rs
src/libstd/rt.rs
src/libstd/rtdeps.rs
src/libstd/sync/condvar.rs
src/libstd/sync/mod.rs
src/libstd/sync/mpsc/mod.rs
src/libstd/sync/semaphore.rs
src/libstd/sys/common/io.rs
src/libstd/sys/common/libunwind.rs
src/libstd/sys/common/poison.rs
src/libstd/sys/common/remutex.rs
src/libstd/sys/common/unwind/mod.rs
src/libstd/sys/common/util.rs
src/libstd/sys/common/wtf8.rs
src/libstd/sys/unix/ext/fs.rs
src/libstd/sys/unix/ext/io.rs
src/libstd/sys/unix/fd.rs
src/libstd/sys/unix/fs.rs
src/libstd/sys/unix/mod.rs
src/libstd/sys/unix/os.rs
src/libstd/sys/unix/process.rs
src/libstd/sys/unix/stack_overflow.rs
src/libstd/sys/windows/c.rs
src/libstd/sys/windows/mod.rs
src/libstd/thread/local.rs
src/libstd/thread/mod.rs
src/libstd/thread/scoped_tls.rs
src/libstd/time/duration.rs
src/libstd/time/mod.rs
src/libsyntax/abi.rs
src/libsyntax/ast.rs
src/libsyntax/ast_util.rs
src/libsyntax/attr.rs
src/libsyntax/codemap.rs
src/libsyntax/config.rs
src/libsyntax/diagnostic.rs [deleted file]
src/libsyntax/diagnostics/macros.rs
src/libsyntax/diagnostics/plugin.rs
src/libsyntax/errors/emitter.rs [new file with mode: 0644]
src/libsyntax/errors/json.rs [new file with mode: 0644]
src/libsyntax/errors/mod.rs [new file with mode: 0644]
src/libsyntax/ext/asm.rs [deleted file]
src/libsyntax/ext/base.rs
src/libsyntax/ext/build.rs
src/libsyntax/ext/cfg.rs [deleted file]
src/libsyntax/ext/concat.rs [deleted file]
src/libsyntax/ext/concat_idents.rs [deleted file]
src/libsyntax/ext/deriving/bounds.rs [deleted file]
src/libsyntax/ext/deriving/clone.rs [deleted file]
src/libsyntax/ext/deriving/cmp/eq.rs [deleted file]
src/libsyntax/ext/deriving/cmp/ord.rs [deleted file]
src/libsyntax/ext/deriving/cmp/partial_eq.rs [deleted file]
src/libsyntax/ext/deriving/cmp/partial_ord.rs [deleted file]
src/libsyntax/ext/deriving/debug.rs [deleted file]
src/libsyntax/ext/deriving/decodable.rs [deleted file]
src/libsyntax/ext/deriving/default.rs [deleted file]
src/libsyntax/ext/deriving/encodable.rs [deleted file]
src/libsyntax/ext/deriving/generic/mod.rs [deleted file]
src/libsyntax/ext/deriving/generic/ty.rs [deleted file]
src/libsyntax/ext/deriving/hash.rs [deleted file]
src/libsyntax/ext/deriving/mod.rs [deleted file]
src/libsyntax/ext/deriving/primitive.rs [deleted file]
src/libsyntax/ext/env.rs [deleted file]
src/libsyntax/ext/expand.rs
src/libsyntax/ext/format.rs [deleted file]
src/libsyntax/ext/log_syntax.rs [deleted file]
src/libsyntax/ext/quote.rs
src/libsyntax/ext/source_util.rs
src/libsyntax/ext/trace_macros.rs [deleted file]
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/lib.rs
src/libsyntax/owned_slice.rs
src/libsyntax/parse/attr.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/pprust.rs
src/libsyntax/ptr.rs
src/libsyntax/show_span.rs
src/libsyntax/test.rs
src/libsyntax/util/lev_distance.rs
src/libsyntax/util/move_map.rs
src/libsyntax/util/parser.rs
src/libsyntax/util/parser_testing.rs
src/libsyntax/util/small_vector.rs
src/libsyntax/visit.rs
src/libsyntax_ext/asm.rs [new file with mode: 0644]
src/libsyntax_ext/cfg.rs [new file with mode: 0644]
src/libsyntax_ext/concat.rs [new file with mode: 0644]
src/libsyntax_ext/concat_idents.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/bounds.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/clone.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/cmp/eq.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/cmp/ord.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/cmp/partial_eq.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/cmp/partial_ord.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/debug.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/decodable.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/default.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/encodable.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/generic/mod.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/generic/ty.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/hash.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/mod.rs [new file with mode: 0644]
src/libsyntax_ext/deriving/primitive.rs [new file with mode: 0644]
src/libsyntax_ext/env.rs [new file with mode: 0644]
src/libsyntax_ext/format.rs [new file with mode: 0644]
src/libsyntax_ext/lib.rs [new file with mode: 0644]
src/libsyntax_ext/log_syntax.rs [new file with mode: 0644]
src/libsyntax_ext/trace_macros.rs [new file with mode: 0644]
src/libterm/lib.rs
src/libterm/terminfo/mod.rs
src/libterm/win.rs
src/libtest/lib.rs
src/libtest/stats.rs
src/rt/rust_android_dummy.c [deleted file]
src/rt/rust_android_dummy.h [deleted file]
src/rt/rust_builtin.c [deleted file]
src/rt/rust_test_helpers.c
src/rtstartup/rsbegin.rs
src/rtstartup/rsend.rs
src/rustbook/book.rs
src/rustbook/main.rs
src/snapshots.txt
src/test/auxiliary/associated-types-cc-lib.rs
src/test/auxiliary/custom_derive_plugin.rs
src/test/auxiliary/custom_derive_plugin_attr.rs
src/test/auxiliary/default_ty_param_cross_crate_crate.rs
src/test/auxiliary/deprecation-lint.rs [new file with mode: 0644]
src/test/auxiliary/empty-struct.rs [new file with mode: 0644]
src/test/auxiliary/extern_calling_convention.rs
src/test/auxiliary/issue-2526.rs
src/test/auxiliary/lint_stability.rs
src/test/auxiliary/mir_external_refs.rs [new file with mode: 0644]
src/test/auxiliary/static-methods-crate.rs
src/test/bench/shootout-mandelbrot.rs [deleted file]
src/test/bench/shootout-spectralnorm.rs [deleted file]
src/test/codegen/mir_zst_stores.rs [new file with mode: 0644]
src/test/compile-fail-fulldeps/qquote.rs
src/test/compile-fail/associated-const-array-len.rs [new file with mode: 0644]
src/test/compile-fail/associated-const-no-item.rs [new file with mode: 0644]
src/test/compile-fail/associated-const-type-parameter-arms.rs [new file with mode: 0644]
src/test/compile-fail/associated-const-type-parameter-arrays-2.rs [new file with mode: 0644]
src/test/compile-fail/associated-const-type-parameter-arrays.rs [new file with mode: 0644]
src/test/compile-fail/associated-const-type-parameters.rs [deleted file]
src/test/compile-fail/associated-types-coherence-failure.rs
src/test/compile-fail/associated-types-no-suitable-supertrait.rs
src/test/compile-fail/augmented-assignments-feature-gate-cross.rs
src/test/compile-fail/augmented-assignments-feature-gate.rs
src/test/compile-fail/bogus-tag.rs
src/test/compile-fail/builtin-superkinds-self-type.rs
src/test/compile-fail/coerce-expect-unsized-ascribed.rs [new file with mode: 0644]
src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs
src/test/compile-fail/coherence-default-trait-impl.rs
src/test/compile-fail/coherence-impl-trait-for-trait-object-safe.rs
src/test/compile-fail/coherence-impls-copy.rs
src/test/compile-fail/coherence-impls-sized.rs
src/test/compile-fail/coherence-overlap-messages.rs [new file with mode: 0644]
src/test/compile-fail/coherence-projection-conflict-orphan.rs [new file with mode: 0644]
src/test/compile-fail/coherence-projection-conflict.rs [new file with mode: 0644]
src/test/compile-fail/coherence-projection-ok-orphan.rs [new file with mode: 0644]
src/test/compile-fail/coherence-projection-ok.rs [new file with mode: 0644]
src/test/compile-fail/const-eval-overflow.rs
src/test/compile-fail/const-eval-overflow0.rs [new file with mode: 0644]
src/test/compile-fail/const-fn-error.rs [new file with mode: 0644]
src/test/compile-fail/const-fn-not-safe-for-const.rs
src/test/compile-fail/const-fn-not-safe-for-const2.rs [new file with mode: 0644]
src/test/compile-fail/const-pattern-not-const-evaluable.rs [new file with mode: 0644]
src/test/compile-fail/const-tup-index-span.rs [new file with mode: 0644]
src/test/compile-fail/cross-fn-cache-hole.rs
src/test/compile-fail/dep-graph-caller-callee.rs [new file with mode: 0644]
src/test/compile-fail/dep-graph-struct-signature.rs [new file with mode: 0644]
src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs [new file with mode: 0644]
src/test/compile-fail/dep-graph-trait-impl-two-traits.rs [new file with mode: 0644]
src/test/compile-fail/dep-graph-trait-impl.rs [new file with mode: 0644]
src/test/compile-fail/dep-graph-unrelated.rs [new file with mode: 0644]
src/test/compile-fail/deprecation-in-staged-api.rs [new file with mode: 0644]
src/test/compile-fail/deprecation-lint-2.rs [new file with mode: 0644]
src/test/compile-fail/deprecation-lint-3.rs [new file with mode: 0644]
src/test/compile-fail/deprecation-lint.rs [new file with mode: 0644]
src/test/compile-fail/deprecation-sanity.rs [new file with mode: 0644]
src/test/compile-fail/duplicate-type-parameter.rs
src/test/compile-fail/empty-struct-braces-expr.rs
src/test/compile-fail/empty-struct-braces-pat-1.rs
src/test/compile-fail/empty-struct-braces-pat-2.rs
src/test/compile-fail/empty-struct-braces-pat-3.rs
src/test/compile-fail/empty-struct-unit-expr.rs
src/test/compile-fail/empty-struct-unit-pat.rs
src/test/compile-fail/enum-discrim-too-small.rs
src/test/compile-fail/feature-gate-abi-vectorcall.rs [new file with mode: 0644]
src/test/compile-fail/feature-gate-negate-unsigned.rs
src/test/compile-fail/feature-gate-simd-ffi.rs
src/test/compile-fail/generic-no-mangle.rs
src/test/compile-fail/if-branch-types.rs
src/test/compile-fail/inner-static-type-parameter.rs
src/test/compile-fail/integer-literal-suffix-inference.rs
src/test/compile-fail/issue-12796.rs
src/test/compile-fail/issue-13359.rs
src/test/compile-fail/issue-13853-2.rs
src/test/compile-fail/issue-13853.rs
src/test/compile-fail/issue-14254.rs
src/test/compile-fail/issue-14853.rs
src/test/compile-fail/issue-17546.rs
src/test/compile-fail/issue-17740.rs
src/test/compile-fail/issue-17905.rs
src/test/compile-fail/issue-18118-2.rs [new file with mode: 0644]
src/test/compile-fail/issue-18118.rs
src/test/compile-fail/issue-18389.rs
src/test/compile-fail/issue-18959.rs
src/test/compile-fail/issue-19380.rs
src/test/compile-fail/issue-19883.rs
src/test/compile-fail/issue-20005.rs
src/test/compile-fail/issue-20427.rs
src/test/compile-fail/issue-20831-debruijn.rs
src/test/compile-fail/issue-21659-show-relevant-trait-impls-1.rs [new file with mode: 0644]
src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs [new file with mode: 0644]
src/test/compile-fail/issue-21659-show-relevant-trait-impls-3.rs [new file with mode: 0644]
src/test/compile-fail/issue-21974.rs
src/test/compile-fail/issue-22912.rs [deleted file]
src/test/compile-fail/issue-23041.rs
src/test/compile-fail/issue-23305.rs
src/test/compile-fail/issue-2356.rs
src/test/compile-fail/issue-26194.rs
src/test/compile-fail/issue-26656.rs [new file with mode: 0644]
src/test/compile-fail/issue-26812.rs
src/test/compile-fail/issue-28109.rs
src/test/compile-fail/issue-28568.rs
src/test/compile-fail/issue-3021-d.rs
src/test/compile-fail/issue-3021.rs
src/test/compile-fail/issue-30302.rs [new file with mode: 0644]
src/test/compile-fail/issue-30438-a.rs [new file with mode: 0644]
src/test/compile-fail/issue-30438-b.rs [new file with mode: 0644]
src/test/compile-fail/issue-30438-c.rs [new file with mode: 0644]
src/test/compile-fail/issue-30589.rs [new file with mode: 0644]
src/test/compile-fail/issue-30715.rs [new file with mode: 0644]
src/test/compile-fail/issue-3214.rs
src/test/compile-fail/issue-3477.rs
src/test/compile-fail/issue-3521.rs
src/test/compile-fail/issue-3907-2.rs
src/test/compile-fail/issue-3973.rs
src/test/compile-fail/issue-5100.rs
src/test/compile-fail/issue-5927.rs
src/test/compile-fail/issue-8761.rs
src/test/compile-fail/issue-9725.rs
src/test/compile-fail/lint-dead-code-1.rs
src/test/compile-fail/lint-exceeding-bitshifts.rs
src/test/compile-fail/lint-qualification.rs
src/test/compile-fail/lint-stability.rs
src/test/compile-fail/lint-type-limits.rs
src/test/compile-fail/lint-unused-imports.rs
src/test/compile-fail/lint-visible-private-types.rs [deleted file]
src/test/compile-fail/macro-context.rs
src/test/compile-fail/macro-follow.rs [new file with mode: 0644]
src/test/compile-fail/macro-input-future-proofing.rs
src/test/compile-fail/macro-parameter-span.rs [new file with mode: 0644]
src/test/compile-fail/macro-seq-followed-by-seq.rs [deleted file]
src/test/compile-fail/mod_file_correct_spans.rs
src/test/compile-fail/not-panic-safe-2.rs [new file with mode: 0644]
src/test/compile-fail/not-panic-safe-3.rs [new file with mode: 0644]
src/test/compile-fail/not-panic-safe-4.rs [new file with mode: 0644]
src/test/compile-fail/not-panic-safe-5.rs [new file with mode: 0644]
src/test/compile-fail/not-panic-safe-6.rs [new file with mode: 0644]
src/test/compile-fail/not-panic-safe.rs [new file with mode: 0644]
src/test/compile-fail/object-safety-generics.rs
src/test/compile-fail/object-safety-mentions-Self.rs
src/test/compile-fail/object-safety-no-static.rs
src/test/compile-fail/object-safety-sized-2.rs
src/test/compile-fail/object-safety-sized.rs
src/test/compile-fail/opt-in-copy.rs
src/test/compile-fail/pattern-error-continue.rs
src/test/compile-fail/pptypedef.rs
src/test/compile-fail/priv_in_pub_sig_priv_mod.rs [deleted file]
src/test/compile-fail/private-in-public-lint.rs [new file with mode: 0644]
src/test/compile-fail/private-in-public-warn.rs [new file with mode: 0644]
src/test/compile-fail/private-in-public.rs [new file with mode: 0644]
src/test/compile-fail/private-variant-reexport.rs [new file with mode: 0644]
src/test/compile-fail/pub-item-macro.rs [deleted file]
src/test/compile-fail/pub-method-macro.rs [deleted file]
src/test/compile-fail/region-borrow-params-issue-29793-big.rs [new file with mode: 0644]
src/test/compile-fail/region-borrow-params-issue-29793-small.rs [new file with mode: 0644]
src/test/compile-fail/regions-close-object-into-object-5.rs
src/test/compile-fail/regions-free-region-ordering-callee-4.rs [new file with mode: 0644]
src/test/compile-fail/regions-free-region-ordering-callee.rs
src/test/compile-fail/regions-implied-bounds-projection-gap-hr-1.rs
src/test/compile-fail/regions-wf-trait-object.rs
src/test/compile-fail/repeat_count.rs
src/test/compile-fail/resolve-inconsistent-binding-mode.rs
src/test/compile-fail/resolve-type-param-in-item-in-trait.rs
src/test/compile-fail/rfc1214-warn-and-error.rs [deleted file]
src/test/compile-fail/shadowed-use-visibility.rs [new file with mode: 0644]
src/test/compile-fail/shift-various-bad-types.rs
src/test/compile-fail/struct-fields-missing.rs
src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs [new file with mode: 0644]
src/test/compile-fail/syntax-extension-minor.rs
src/test/compile-fail/trait-bounds-impl-comparison-2.rs
src/test/compile-fail/trait-safety-trait-impl-cc.rs
src/test/compile-fail/trait-safety-trait-impl.rs
src/test/compile-fail/trait-test-2.rs
src/test/compile-fail/tutorial-suffix-inference-test.rs
src/test/compile-fail/type-ascription-feature-gate.rs [new file with mode: 0644]
src/test/compile-fail/type-ascription-precedence.rs [new file with mode: 0644]
src/test/compile-fail/type-ascription-soundness.rs [new file with mode: 0644]
src/test/compile-fail/type-mismatch-multiple.rs
src/test/compile-fail/type-parameter-invalid-lint.rs [new file with mode: 0644]
src/test/compile-fail/typeck-default-trait-impl-trait-where-clause-2.rs [new file with mode: 0644]
src/test/compile-fail/typeck-default-trait-impl-trait-where-clause.rs
src/test/compile-fail/ufcs-explicit-self-bad.rs
src/test/compile-fail/unresolved-import.rs
src/test/compile-fail/variance-btree-invariant-types.rs [new file with mode: 0644]
src/test/compile-fail/variance-invariant-self-trait-match.rs
src/test/compile-fail/visible-private-types-generics.rs [deleted file]
src/test/compile-fail/visible-private-types-supertrait.rs [deleted file]
src/test/compile-fail/wf-array-elem-sized.rs
src/test/compile-fail/wf-enum-bound.rs
src/test/compile-fail/wf-fn-where-clause.rs
src/test/compile-fail/wf-impl-associated-type-region.rs
src/test/compile-fail/wf-impl-associated-type-trait.rs
src/test/compile-fail/wf-in-fn-type-static.rs
src/test/compile-fail/wf-in-fn-where-clause.rs
src/test/compile-fail/wf-in-obj-type-static.rs
src/test/compile-fail/wf-inherent-impl-method-where-clause.rs
src/test/compile-fail/wf-inherent-impl-where-clause.rs
src/test/compile-fail/wf-outlives-ty-in-fn-or-trait.rs
src/test/compile-fail/wf-struct-bound.rs
src/test/compile-fail/wf-trait-associated-type-bound.rs
src/test/compile-fail/wf-trait-associated-type-region.rs
src/test/compile-fail/wf-trait-associated-type-trait.rs
src/test/compile-fail/wf-trait-bound.rs
src/test/compile-fail/wf-trait-default-fn-arg.rs
src/test/compile-fail/wf-trait-default-fn-ret.rs
src/test/compile-fail/wf-trait-default-fn-where-clause.rs
src/test/compile-fail/wf-trait-fn-arg.rs
src/test/compile-fail/wf-trait-fn-ret.rs
src/test/compile-fail/wf-trait-fn-where-clause.rs
src/test/compile-fail/wf-trait-superbound.rs
src/test/debuginfo/simd.rs
src/test/debuginfo/type-names.rs
src/test/parse-fail/assoc-oddities-3.rs [deleted file]
src/test/parse-fail/lex-bad-char-literals-1.rs [new file with mode: 0644]
src/test/parse-fail/lex-bad-char-literals-2.rs [new file with mode: 0644]
src/test/parse-fail/lex-bad-char-literals-3.rs [new file with mode: 0644]
src/test/parse-fail/lex-bad-char-literals-4.rs [new file with mode: 0644]
src/test/parse-fail/lex-bad-char-literals-5.rs [new file with mode: 0644]
src/test/parse-fail/lex-bad-char-literals.rs [deleted file]
src/test/parse-fail/lifetime-semicolon.rs [new file with mode: 0644]
src/test/parse-fail/obsolete-proc.rs
src/test/parse-fail/pub-item-macro.rs [new file with mode: 0644]
src/test/parse-fail/pub-macro-rules.rs [new file with mode: 0644]
src/test/parse-fail/pub-method-macro.rs [new file with mode: 0644]
src/test/parse-fail/struct-literal-in-for.rs
src/test/parse-fail/struct-literal-in-if.rs
src/test/parse-fail/struct-literal-in-while.rs
src/test/run-fail/mir_indexing_oob_1.rs [new file with mode: 0644]
src/test/run-fail/mir_indexing_oob_2.rs [new file with mode: 0644]
src/test/run-fail/mir_indexing_oob_3.rs [new file with mode: 0644]
src/test/run-fail/mir_trans_calls_converging_drops.rs [new file with mode: 0644]
src/test/run-fail/mir_trans_calls_converging_drops_2.rs [new file with mode: 0644]
src/test/run-fail/mir_trans_calls_diverging.rs [new file with mode: 0644]
src/test/run-fail/mir_trans_calls_diverging_drops.rs [new file with mode: 0644]
src/test/run-fail/mir_trans_no_landing_pads.rs [new file with mode: 0644]
src/test/run-fail/mir_trans_no_landing_pads_diverging.rs [new file with mode: 0644]
src/test/run-fail/panic-set-handler.rs [new file with mode: 0644]
src/test/run-fail/panic-set-unset-handler.rs [new file with mode: 0644]
src/test/run-fail/panic-take-handler-nop.rs [new file with mode: 0644]
src/test/run-make/emit/Makefile [new file with mode: 0644]
src/test/run-make/emit/test-24876.rs [new file with mode: 0644]
src/test/run-make/emit/test-26235.rs [new file with mode: 0644]
src/test/run-make/execution-engine/Makefile
src/test/run-make/execution-engine/test.rs
src/test/run-make/issue-19371/foo.rs
src/test/run-make/issue-28766/Makefile [new file with mode: 0644]
src/test/run-make/issue-28766/foo.rs [new file with mode: 0644]
src/test/run-make/issue-28766/main.rs [new file with mode: 0644]
src/test/run-make/json-errors/Makefile [new file with mode: 0644]
src/test/run-make/json-errors/foo.rs [new file with mode: 0644]
src/test/run-make/simd-ffi/Makefile
src/test/run-make/simd-ffi/simd.rs
src/test/run-make/symbols-include-type-name/Makefile [new file with mode: 0644]
src/test/run-make/symbols-include-type-name/lib.rs [new file with mode: 0644]
src/test/run-make/tools.mk
src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs
src/test/run-pass-fulldeps/compiler-calls.rs
src/test/run-pass-valgrind/exit-flushes.rs
src/test/run-pass/asm-indirect-memory.rs
src/test/run-pass/assoc-oddities-3.rs [new file with mode: 0644]
src/test/run-pass/associated-const-type-parameters.rs [new file with mode: 0644]
src/test/run-pass/binary-heap-panic-safe.rs
src/test/run-pass/bitwise.rs
src/test/run-pass/conditional-compile-arch.rs
src/test/run-pass/consts-in-patterns.rs
src/test/run-pass/cycle-generic-bound.rs
src/test/run-pass/cycle-trait-type-trait.rs
src/test/run-pass/default_ty_param_struct_and_type_alias.rs
src/test/run-pass/dst-irrefutable-bind.rs [new file with mode: 0644]
src/test/run-pass/empty-struct-braces.rs
src/test/run-pass/extern-vectorcall.rs [new file with mode: 0644]
src/test/run-pass/intrinsic-alignment.rs
src/test/run-pass/intrinsics-integer.rs
src/test/run-pass/issue-24956.rs [deleted file]
src/test/run-pass/issue-26873-multifile.rs [new file with mode: 0644]
src/test/run-pass/issue-26873-onefile.rs [new file with mode: 0644]
src/test/run-pass/issue-27889.rs [new file with mode: 0644]
src/test/run-pass/issue-28777.rs [new file with mode: 0644]
src/test/run-pass/issue-2895.rs
src/test/run-pass/issue-28983.rs
src/test/run-pass/issue-29092.rs [new file with mode: 0644]
src/test/run-pass/issue-29668.rs [new file with mode: 0644]
src/test/run-pass/issue-29844.rs [new file with mode: 0644]
src/test/run-pass/issue-30018-nopanic.rs [new file with mode: 0644]
src/test/run-pass/issue-30018-panic.rs [new file with mode: 0644]
src/test/run-pass/issue-30371.rs [new file with mode: 0644]
src/test/run-pass/issue-30490.rs [new file with mode: 0644]
src/test/run-pass/issue-30530.rs [new file with mode: 0644]
src/test/run-pass/issue-6898.rs
src/test/run-pass/issue29927-1.rs [new file with mode: 0644]
src/test/run-pass/issue29927.rs [new file with mode: 0644]
src/test/run-pass/issue_26873_multifile/A/B.rs [new file with mode: 0644]
src/test/run-pass/issue_26873_multifile/A/C.rs [new file with mode: 0644]
src/test/run-pass/issue_26873_multifile/A/mod.rs [new file with mode: 0644]
src/test/run-pass/issue_26873_multifile/mod.rs [new file with mode: 0644]
src/test/run-pass/macro-follow.rs [new file with mode: 0644]
src/test/run-pass/macro-pat-follow.rs
src/test/run-pass/macro-seq-followed-by-seq.rs [new file with mode: 0644]
src/test/run-pass/method-mut-self-modifies-mut-slice-lvalue.rs
src/test/run-pass/mir_adt_construction.rs [new file with mode: 0644]
src/test/run-pass/mir_build_match_comparisons.rs [new file with mode: 0644]
src/test/run-pass/mir_constval_adts.rs [new file with mode: 0644]
src/test/run-pass/mir_fat_ptr.rs
src/test/run-pass/mir_match_arm_guard.rs [new file with mode: 0644]
src/test/run-pass/mir_misc_casts.rs [new file with mode: 0644]
src/test/run-pass/mir_refs_correct.rs [new file with mode: 0644]
src/test/run-pass/mir_trans_call_converging.rs [new file with mode: 0644]
src/test/run-pass/mir_trans_calls.rs [new file with mode: 0644]
src/test/run-pass/mir_trans_calls_variadic.rs [new file with mode: 0644]
src/test/run-pass/mir_trans_switch.rs [new file with mode: 0644]
src/test/run-pass/mir_void_return.rs [new file with mode: 0644]
src/test/run-pass/mir_void_return_2.rs [new file with mode: 0644]
src/test/run-pass/num-wrapping.rs [new file with mode: 0644]
src/test/run-pass/panic-handler-chain.rs [new file with mode: 0644]
src/test/run-pass/panic-handler-flail-wildly.rs [new file with mode: 0644]
src/test/run-pass/panic-handler-set-twice.rs [new file with mode: 0644]
src/test/run-pass/panic-recover-propagate.rs [new file with mode: 0644]
src/test/run-pass/panic-safe.rs [new file with mode: 0644]
src/test/run-pass/rec-align-u32.rs
src/test/run-pass/rec-align-u64.rs
src/test/run-pass/regions-issue-22246.rs
src/test/run-pass/regions-no-variance-from-fn-generics.rs
src/test/run-pass/running-with-no-runtime.rs
src/test/run-pass/shadowed-use-visibility.rs [new file with mode: 0644]
src/test/run-pass/simd-issue-10604.rs [deleted file]
src/test/run-pass/struct-return.rs
src/test/run-pass/sync-send-iterators-in-libcore.rs
src/test/run-pass/tls-init-on-init.rs [new file with mode: 0644]
src/test/run-pass/trait-inheritance-num0.rs
src/test/run-pass/trait-inheritance-num1.rs
src/test/run-pass/trait-inheritance-num3.rs
src/test/run-pass/trait-inheritance-num5.rs
src/test/run-pass/trait-inheritance-self-in-supertype.rs
src/test/run-pass/trait-inheritance-self.rs
src/test/run-pass/trait-inheritance-subst.rs
src/test/run-pass/trait-inheritance-subst2.rs
src/test/run-pass/type-ascription.rs [new file with mode: 0644]
src/test/run-pass/unary-minus-suffix-inference.rs
src/test/run-pass/unsized2.rs
src/test/run-pass/visible-private-types-feature-gate.rs [deleted file]
src/test/run-pass/while-let.rs
src/test/run-pass/zero-sized-vec-deque-push.rs [new file with mode: 0644]
src/test/rustdoc/deprecated.rs [new file with mode: 0644]

index a11e9a7d68053f7bc7a582c03b02387e86398ffc..e864172e813320ff2167b69fbbd74f6dc4cfb92f 100644 (file)
@@ -174,7 +174,7 @@ labels to triage issues:
 * Yellow, **A**-prefixed labels state which **area** of the project an issue
   relates to.
 
-* Magenta, **B**-prefixed labels identify bugs which **belong** elsewhere.
+* Magenta, **B**-prefixed labels identify bugs which are **blockers**.
 
 * Green, **E**-prefixed labels explain the level of **experience** necessary
   to fix the issue.
@@ -238,7 +238,7 @@ are:
 * Don't be afraid to ask! The Rust community is friendly and helpful.
 
 [gdfrustc]: http://manishearth.github.io/rust-internals-docs/rustc/
-[gsearchdocs]: https://www.google.de/search?q=site:doc.rust-lang.org+your+query+here
+[gsearchdocs]: https://www.google.com/search?q=site:doc.rust-lang.org+your+query+here
 [rif]: http://internals.rust-lang.org
 [rr]: https://doc.rust-lang.org/book/README.html
 [tlgba]: http://tomlee.co/2014/04/03/a-more-detailed-tour-of-the-rust-compiler/
index 0eac7ac9de2c56c669ecb088fba2d64b941f1bae..5ab70b7120fd9e44a6b34d49595c68c5d9928cd0 100644 (file)
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -6,7 +6,7 @@ terms.
 
 Longer version:
 
-The Rust Project is copyright 2015, The Rust Project
+The Rust Project is copyright 2016, The Rust Project
 Developers (given in the file AUTHORS.txt).
 
 Licensed under the Apache License, Version 2.0
index e69282e381bc07152cc7598f21f3162e4bbb1f22..40b8817a47beba464cd86e3f81260cd41b5e70dd 100644 (file)
@@ -1,4 +1,4 @@
-Copyright (c) 2015 The Rust Project Developers
+Copyright (c) 2016 The Rust Project Developers
 
 Permission is hereby granted, free of charge, to any
 person obtaining a copy of this software and associated
index 8eb742f0a22fcbde7889e5b3929ab35218e1ef56..84fb8f3e5b089a22bbb1ec66cb619b58433d8a5b 100644 (file)
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ Read ["Installing Rust"] from [The Book].
 1. Make sure you have installed the dependencies:
 
    * `g++` 4.7 or `clang++` 3.x
-   * `python` 2.6 or later (but not 3.x)
+   * `python` 2.7 or later (but not 3.x)
    * GNU `make` 3.81 or later
    * `curl`
    * `git`
@@ -53,6 +53,16 @@ Read ["Installing Rust"] from [The Book].
 
 ### Building on Windows
 
+There are two prominent ABIs in use on Windows: the native (MSVC) ABI used by
+Visual Studio, and the GNU ABI used by the GCC toolchain. Which version of Rust
+you need depends largely on what C/C++ libraries you want to interoperate with:
+for interop with software produced by Visual Studio use the MSVC build of Rust;
+for interop with GNU software built using the MinGW/MSYS2 toolchain use the GNU
+build.
+
+
+#### MinGW
+
 [MSYS2](http://msys2.github.io/) can be used to easily build Rust on Windows:
 
 1. Grab the latest MSYS2 installer and go through the installer.
@@ -63,12 +73,15 @@ Read ["Installing Rust"] from [The Book].
    ```sh
    # Update package mirrors (may be needed if you have a fresh install of MSYS2)
    $ pacman -Sy pacman-mirrors
+   ```
 
-   # Choose one based on platform: 
-   # *** see the note below ***
-   $ pacman -S mingw-w64-i686-toolchain
-   $ pacman -S mingw-w64-x86_64-toolchain
+Download [MinGW from
+here](http://mingw-w64.org/doku.php/download/mingw-builds), and choose the
+`threads=win32,exceptions=dwarf/seh` flavor when installing. 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
 
@@ -84,16 +97,19 @@ Read ["Installing Rust"] from [The Book].
    $ ./configure
    $ make && make install
    ```
-> ***Note:*** gcc versions >= 5 currently have issues building LLVM on Windows
-> resulting in a segmentation fault when building Rust. In order to avoid this
-> it may be necessary to obtain an earlier version of gcc such as 4.9.x.  
-> Msys's `pacman` will install the latest version, so for the time being it is
-> recommended to skip gcc toolchain installation step above and use [Mingw-Builds]
-> project's installer instead.  Be sure to add gcc `bin` directory to the path
-> before running `configure`.  
-> For more information on this see issue #28260.
 
-[Mingw-Builds]: http://sourceforge.net/projects/mingw-w64/
+#### MSVC
+
+MSVC builds of Rust additionally require an installation of Visual Studio 2013
+(or later) so `rustc` can use its linker. Make sure to check the “C++ tools”
+option. In addition, `cmake` needs to be installed to build LLVM.
+
+With these dependencies installed, the build takes two steps:
+
+```sh
+$ ./configure
+$ make && make install
+```
 
 ## Building Documentation
 
@@ -135,7 +151,7 @@ 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 about 1.5 GiB of RAM to build without swapping; if it hits
+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].
index ee08ea5a44c3ffed8336ea4d42eb1568b1932b91..37a1158040324dab0427864dec434357f3d45567 100644 (file)
@@ -1,3 +1,205 @@
+Version 1.7.0 (2016-03-03)
+==========================
+
+Libraries
+---------
+
+* Stabilized APIs
+  * `Path`
+    * [`Path::strip_prefix`][] (renamed from relative_from)
+    * [`path::StripPrefixError`][] (new error type returned from strip_prefix)
+  * `Ipv4Addr`
+    * [`Ipv4Addr::is_loopback`]
+    * [`Ipv4Addr::is_private`]
+    * [`Ipv4Addr::is_link_local`]
+    * [`Ipv4Addr::is_multicast`]
+    * [`Ipv4Addr::is_broadcast`]
+    * [`Ipv4Addr::is_documentation`]
+  * `Ipv6Addr`
+    * [`Ipv6Addr::is_unspecified`]
+    * [`Ipv6Addr::is_loopback`]
+    * [`Ipv6Addr::is_multicast`]
+  * `Vec`
+    * [`Vec::as_slice`]
+    * [`Vec::as_mut_slice`]
+  * `String`
+    * [`String::as_str`]
+    * [`String::as_mut_str`]
+  * Slices
+    * `<[T]>::`[`clone_from_slice`], which now requires the two slices to
+    be the same length
+    * `<[T]>::`[`sort_by_key`]
+  * checked, saturated, and overflowing operations
+    * [`i32::checked_rem`], [`i32::checked_neg`], [`i32::checked_shl`], [`i32::checked_shr`]
+    * [`i32::saturating_mul`]
+    * [`i32::overflowing_add`], [`i32::overflowing_sub`], [`i32::overflowing_mul`], [`i32::overflowing_div`]
+    * [`i32::overflowing_rem`], [`i32::overflowing_neg`], [`i32::overflowing_shl`], [`i32::overflowing_shr`]
+    * [`u32::checked_rem`], [`u32::checked_neg`], [`u32::checked_shl`], [`u32::checked_shl`]
+    * [`u32::saturating_mul`]
+    * [`u32::overflowing_add`], [`u32::overflowing_sub`], [`u32::overflowing_mul`], [`u32::overflowing_div`]
+    * [`u32::overflowing_rem`], [`u32::overflowing_neg`], [`u32::overflowing_shl`], [`u32::overflowing_shr`]
+    * and checked, saturated, and overflowing operations for other primitive types
+  * FFI
+    * [`ffi::IntoStringError`]
+    * [`CString::into_string`]
+    * [`CString::into_bytes`]
+    * [`CString::into_bytes_with_nul`]
+    * `From<CString> for Vec<u8>`
+  * `IntoStringError`
+    * [`IntoStringError::into_cstring`]
+    * [`IntoStringError::utf8_error`]
+    * `Error for IntoStringError`
+  * Hashing
+    * [`std::hash::BuildHasher`]
+    * [`BuildHasher::Hasher`]
+    * [`BuildHasher::build_hasher`]
+    * [`std::hash::BuildHasherDefault`]
+    * [`HashMap::with_hasher`]
+    * [`HashMap::with_capacity_and_hasher`]
+    * [`HashSet::with_hasher`]
+    * [`HashSet::with_capacity_and_hasher`]
+    * [`std::collections::hash_map::RandomState`]
+    * [`RandomState::new`]
+* [Validating UTF-8 is faster by a factor of between 7 and 14x for
+  ASCII input][1.7utf8]. This means that creating `String`s and `str`s
+  from bytes is faster.
+* [The performance of `LineWriter` (and thus `io::stdout`) was
+  improved by using `memchr` to search for newlines][1.7m].
+* [`f32::to_degrees` and `f32::to_radians` are stable][1.7f]. The
+  `f64` variants were stabilized previously.
+* [`BTreeMap` was rewritten to use less memory and improve the performance
+  of insertion and iteration, the latter by as much as 5x][1.7bm].
+* [`BTreeSet` and its iterators, `Iter`, `IntoIter`, and `Range` are
+  covariant over their contained type][1.7bt].
+* [`LinkedList` and its iterators, `Iter` and `IntoIter` are covariant
+  over their contained type][1.7ll].
+* [`str::replace` now accepts a `Pattern`][1.7rp], like other string
+  searching methods.
+* [`Any` is implemented for unsized types][1.7a].
+* [`Hash` is implemented for `Duration`][1.7h].
+
+Misc
+----
+
+* [When running tests with `--test`, rustdoc will pass `--cfg`
+  arguments to the compiler][1.7dt].
+* [The compiler is built with RPATH information by default][1.7rpa].
+  This means that it will be possible to run `rustc` when installed in
+  unusual configurations without configuring the dynamic linker search
+  path explicitly.
+* [`rustc` passes `--enable-new-dtags` to GNU ld][1.7dta]. This makes
+  any RPATH entries (emitted with `-C rpath`) *not* take precedence
+  over `LD_LIBRARY_PATH`.
+
+Cargo
+-----
+
+* [`cargo rustc` accepts a `--profile` flag that runs `rustc` under
+  any of the compilation profiles, 'dev', 'bench', or 'test'][1.7cp].
+* [The `rerun-if-changed` build script directive no longer causes the
+  build script to incorrectly run twice in certain scenarios][1.7rr].
+
+Compatibility Notes
+-------------------
+
+* Soundness fixes to the interactions between associated types and
+  lifetimes, specified in [RFC 1214], [now generate errors][1.7sf] for
+  code that violates the new rules. This is a significant change that
+  is known to break existing code, so it has emitted warnings for the
+  new error cases since 1.4 to give crate authors time to adapt. The
+  details of what is changing are subtle; read the RFC for more.
+* [Several bugs in the compiler's visibility calculations were
+  fixed][1.7v]. Since this was found to break significant amounts of
+  code, the new errors will be emitted as warnings for several release
+  cycles, under the `private_in_public` lint.
+* Defaulted type parameters were accidentally accepted in positions
+  that were not intended. In this release, [defaulted type parameters
+  appearing outside of type definitions will generate a
+  warning][1.7d], which will become an error in future releases.
+* [Parsing "." as a float results in an error instead of
+  0][1.7p]. That is, `".".parse::<f32>()` returns `Err`, not `Ok(0)`.
+* [Borrows of closure parameters may not outlive the closure][1.7bc].
+
+[1.7a]: https://github.com/rust-lang/rust/pull/30928
+[1.7bc]: https://github.com/rust-lang/rust/pull/30341
+[1.7bm]: https://github.com/rust-lang/rust/pull/30426
+[1.7bt]: https://github.com/rust-lang/rust/pull/30998
+[1.7cp]: https://github.com/rust-lang/cargo/pull/2224
+[1.7d]: https://github.com/rust-lang/rust/pull/30724
+[1.7dt]: https://github.com/rust-lang/rust/pull/30372
+[1.7dta]: https://github.com/rust-lang/rust/pull/30394
+[1.7f]: https://github.com/rust-lang/rust/pull/30672
+[1.7h]: https://github.com/rust-lang/rust/pull/30818
+[1.7ll]: https://github.com/rust-lang/rust/pull/30663
+[1.7m]: https://github.com/rust-lang/rust/pull/30381
+[1.7p]: https://github.com/rust-lang/rust/pull/30681
+[1.7rp]: https://github.com/rust-lang/rust/pull/29498
+[1.7rpa]: https://github.com/rust-lang/rust/pull/30353
+[1.7rr]: https://github.com/rust-lang/cargo/pull/2279
+[1.7sf]: https://github.com/rust-lang/rust/pull/30389
+[1.7utf8]: https://github.com/rust-lang/rust/pull/30740
+[1.7v]: https://github.com/rust-lang/rust/pull/29973
+[RFC 1214]: https://github.com/rust-lang/rfcs/blob/master/text/1214-projections-lifetimes-and-wf.md
+[`BuildHasher::Hasher`]: http://doc.rust-lang.org/nightly/std/hash/trait.Hasher.html
+[`BuildHasher::build_hasher`]: http://doc.rust-lang.org/nightly/std/hash/trait.BuildHasher.html#tymethod.build_hasher
+[`CString::into_bytes_with_nul`]: http://doc.rust-lang.org/nightly/std/ffi/struct.CString.html#method.into_bytes_with_nul
+[`CString::into_bytes`]: http://doc.rust-lang.org/nightly/std/ffi/struct.CString.html#method.into_bytes
+[`CString::into_string`]: http://doc.rust-lang.org/nightly/std/ffi/struct.CString.html#method.into_string
+[`HashMap::with_capacity_and_hasher`]: http://doc.rust-lang.org/nightly/std/collections/struct.HashMap.html#method.with_capacity_and_hasher
+[`HashMap::with_hasher`]: http://doc.rust-lang.org/nightly/std/collections/struct.HashMap.html#method.with_hasher
+[`HashSet::with_capacity_and_hasher`]: http://doc.rust-lang.org/nightly/std/collections/struct.HashSet.html#method.with_capacity_and_hasher
+[`HashSet::with_hasher`]: http://doc.rust-lang.org/nightly/std/collections/struct.HashSet.html#method.with_hasher
+[`IntoStringError::into_cstring`]: http://doc.rust-lang.org/nightly/std/ffi/struct.IntoStringError.html#method.into_cstring
+[`IntoStringError::utf8_error`]: http://doc.rust-lang.org/nightly/std/ffi/struct.IntoStringError.html#method.utf8_error
+[`Ipv4Addr::is_broadcast`]: http://doc.rust-lang.org/nightly/std/net/struct.Ipv4Addr.html#method.is_broadcast
+[`Ipv4Addr::is_documentation`]: http://doc.rust-lang.org/nightly/std/net/struct.Ipv4Addr.html#method.is_documentation
+[`Ipv4Addr::is_link_local`]: http://doc.rust-lang.org/nightly/std/net/struct.Ipv4Addr.html#method.is_link_local
+[`Ipv4Addr::is_loopback`]: http://doc.rust-lang.org/nightly/std/net/struct.Ipv4Addr.html#method.is_loopback
+[`Ipv4Addr::is_multicast`]: http://doc.rust-lang.org/nightly/std/net/struct.Ipv4Addr.html#method.is_multicast
+[`Ipv4Addr::is_private`]: http://doc.rust-lang.org/nightly/std/net/struct.Ipv4Addr.html#method.is_private
+[`Ipv6Addr::is_loopback`]: http://doc.rust-lang.org/nightly/std/net/struct.Ipv6Addr.html#method.is_loopback
+[`Ipv6Addr::is_multicast`]: http://doc.rust-lang.org/nightly/std/net/struct.Ipv6Addr.html#method.is_multicast
+[`Ipv6Addr::is_unspecified`]: http://doc.rust-lang.org/nightly/std/net/struct.Ipv6Addr.html#method.is_unspecified
+[`Path::strip_prefix`]: http://doc.rust-lang.org/nightly/std/path/struct.Path.html#method.strip_prefix
+[`RandomState::new`]: http://doc.rust-lang.org/nightly/std/collections/hash_map/struct.RandomState.html#method.new
+[`String::as_mut_str`]: http://doc.rust-lang.org/nightly/std/string/struct.String.html#method.as_mut_str
+[`String::as_str`]: http://doc.rust-lang.org/nightly/std/string/struct.String.html#method.as_str
+[`Vec::as_mut_slice`]: http://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.as_mut_slice
+[`Vec::as_slice`]: http://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.as_slice
+[`clone_from_slice`]: http://doc.rust-lang.org/nightly/std/primitive.slice.html#method.clone_from_slice
+[`ffi::IntoStringError`]: http://doc.rust-lang.org/nightly/std/ffi/struct.IntoStringError.html
+[`i32::checked_neg`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.checked_neg
+[`i32::checked_rem`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.checked_rem
+[`i32::checked_shl`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.checked_shl
+[`i32::checked_shr`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.checked_shr
+[`i32::overflowing_add`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.overflowing_add
+[`i32::overflowing_div`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.overflowing_div
+[`i32::overflowing_mul`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.overflowing_mul
+[`i32::overflowing_neg`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.overflowing_neg
+[`i32::overflowing_rem`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.overflowing_rem
+[`i32::overflowing_shl`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.overflowing_shl
+[`i32::overflowing_shr`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.overflowing_shr
+[`i32::overflowing_sub`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.overflowing_sub
+[`i32::saturating_mul`]: http://doc.rust-lang.org/nightly/std/primitive.i32.html#method.saturating_mul
+[`path::StripPrefixError`]: http://doc.rust-lang.org/nightly/std/path/struct.StripPrefixError.html
+[`sort_by_key`]: http://doc.rust-lang.org/nightly/std/primitive.slice.html#method.sort_by_key
+[`std::collections::hash_map::RandomState`]: http://doc.rust-lang.org/nightly/std/collections/hash_map/struct.RandomState.html
+[`std::hash::BuildHasherDefault`]: http://doc.rust-lang.org/nightly/std/hash/struct.BuildHasherDefault.html
+[`std::hash::BuildHasher`]: http://doc.rust-lang.org/nightly/std/hash/trait.BuildHasher.html
+[`u32::checked_neg`]: http://doc.rust-lang.org/nightly/std/primitive.u32.html#method.checked_neg
+[`u32::checked_rem`]: http://doc.rust-lang.org/nightly/std/primitive.u32.html#method.checked_rem
+[`u32::checked_shl`]: http://doc.rust-lang.org/nightly/std/primitive.u32.html#method.checked_shl
+[`u32::overflowing_add`]: http://doc.rust-lang.org/nightly/std/primitive.u32.html#method.overflowing_add
+[`u32::overflowing_div`]: http://doc.rust-lang.org/nightly/std/primitive.u32.html#method.overflowing_div
+[`u32::overflowing_mul`]: http://doc.rust-lang.org/nightly/std/primitive.u32.html#method.overflowing_mul
+[`u32::overflowing_neg`]: http://doc.rust-lang.org/nightly/std/primitive.u32.html#method.overflowing_neg
+[`u32::overflowing_rem`]: http://doc.rust-lang.org/nightly/std/primitive.u32.html#method.overflowing_rem
+[`u32::overflowing_shl`]: http://doc.rust-lang.org/nightly/std/primitive.u32.html#method.overflowing_shl
+[`u32::overflowing_shr`]: http://doc.rust-lang.org/nightly/std/primitive.u32.html#method.overflowing_shr
+[`u32::overflowing_sub`]: http://doc.rust-lang.org/nightly/std/primitive.u32.html#method.overflowing_sub
+[`u32::saturating_mul`]: http://doc.rust-lang.org/nightly/std/primitive.u32.html#method.saturating_mul
+
+
 Version 1.6.0 (2016-01-21)
 ==========================
 
@@ -16,8 +218,9 @@ Libraries
 ---------
 
 * Stabilized APIs:
-  [`Read::read_exact`], [`ErrorKind::UnexpectedEof`] (renamed from
-  `UnexpectedEOF`), [`fs::DirBuilder`], [`fs::DirBuilder::new`],
+  [`Read::read_exact`],
+  [`ErrorKind::UnexpectedEof`][] (renamed from `UnexpectedEOF`),
+  [`fs::DirBuilder`], [`fs::DirBuilder::new`],
   [`fs::DirBuilder::recursive`], [`fs::DirBuilder::create`],
   [`os::unix::fs::DirBuilderExt`],
   [`os::unix::fs::DirBuilderExt::mode`], [`vec::Drain`],
@@ -29,10 +232,11 @@ Libraries
   [`collections::hash_set::HashSet::drain`],
   [`collections::binary_heap::Drain`],
   [`collections::binary_heap::BinaryHeap::drain`],
-  [`Vec::extend_from_slice`] (renamed from `push_all`),
+  [`Vec::extend_from_slice`][] (renamed from `push_all`),
   [`Mutex::get_mut`], [`Mutex::into_inner`], [`RwLock::get_mut`],
-  [`RwLock::into_inner`], [`Iterator::min_by_key`] (renamed from
-  `min_by`), [`Iterator::max_by_key`] (renamed from `max_by`).
+  [`RwLock::into_inner`],
+  [`Iterator::min_by_key`][] (renamed from `min_by`),
+  [`Iterator::max_by_key`][] (renamed from `max_by`).
 * The [core library][1.6co] is stable, as are most of its APIs.
 * [The `assert_eq!` macro supports arguments that don't implement
   `Sized`][1.6ae], such as arrays. In this way it behaves more like
@@ -40,7 +244,7 @@ Libraries
 * Several timer functions that take duration in milliseconds [are
   deprecated in favor of those that take `Duration`][1.6ms]. These
   include `Condvar::wait_timeout_ms`, `thread::sleep_ms`, and
-  `thread_park_timeout_ms`.
+  `thread::park_timeout_ms`.
 * The algorithm by which `Vec` reserves additional elements was
   [tweaked to not allocate excessive space][1.6a] while still growing
   exponentially.
@@ -67,9 +271,8 @@ Cargo
 * crates.io will reject publication of crates with dependencies that
   have a wildcard version constraint. Crates with wildcard
   dependencies were seen to cause a variety of problems, as described
-  in [RFC 1241]. Disallowing them will create more predictable
-  development experience and a more stable ecosystem. Since 1.5
-  publication of such crates has emitted a warning.
+  in [RFC 1241]. Since 1.5 publication of such crates has emitted a
+  warning.
 * `cargo clean` [accepts a `--release` flag][1.6cc] to clean the
   release folder.  A variety of artifacts that Cargo failed to clean
   are now correctly deleted.
@@ -94,10 +297,11 @@ Compatibility Notes
 * [A number of bugs were fixed in the privacy checker][1.6p] that
   could cause previously-accepted code to break.
 * [Modules and unit/tuple structs may not share the same name][1.6ts].
-* [Bugs in pattern matching unit structs were fixed][1.6us]: the tuple
-  struct pattern syntax (`Foo(..)`) no longer can be used with unit
-  structs; patterns that share the same name as a const are now an
-  error.
+* [Bugs in pattern matching unit structs were fixed][1.6us]. The tuple
+  struct pattern syntax (`Foo(..)`) can no longer be used to match
+  unit structs. This is a warning now, but will become an error in
+  future releases. Patterns that share the same name as a const are
+  now an error.
 * A bug was fixed that causes [rustc not to apply default type
   parameters][1.6xc] when resolving certain method implementations of
   traits defined in other crates.
index c505af5c4d3f0ba9237ba5c9aa3c6530202ff5b5..287b7b352195cdb77ec62240296da45e8d193625 100755 (executable)
--- a/configure
+++ b/configure
@@ -499,13 +499,18 @@ case $CFG_CPUTYPE in
         CFG_CPUTYPE=aarch64
         ;;
 
-    # At some point, when ppc64[le] support happens, this will need to do
-    # something clever. For now it's safe to assume that we're only ever
-    # interested in building 32 bit.
-    powerpc | ppc | ppc64)
+    powerpc | ppc)
         CFG_CPUTYPE=powerpc
         ;;
 
+    powerpc64 | ppc64)
+        CFG_CPUTYPE=powerpc64
+        ;;
+
+    powerpc64le | ppc64le)
+        CFG_CPUTYPE=powerpc64le
+        ;;
+
     x86_64 | x86-64 | x64 | amd64)
         CFG_CPUTYPE=x86_64
         ;;
@@ -521,15 +526,18 @@ then
     # if configure is running in an interactive bash shell. /usr/bin/env
     # exists *everywhere*.
     BIN_TO_PROBE="$SHELL"
-    if [ -z "$BIN_TO_PROBE" -a -e "/usr/bin/env" ]; then
-       BIN_TO_PROBE="/usr/bin/env"
+    if [ ! -r "$BIN_TO_PROBE" ]; then
+        if [ -r "/usr/bin/env" ]; then
+            BIN_TO_PROBE="/usr/bin/env"
+        else
+            warn "Cannot check if the userland is i686 or x86_64"
+        fi
+    fi
+    file -L "$BIN_TO_PROBE" | grep -q "x86[_-]64"
+    if [ $? != 0 ]; then
+        msg "i686 userland on x86_64 Linux kernel"
+        CFG_CPUTYPE=i686
     fi
-    if [ -n "$BIN_TO_PROBE" ]; then
-       file -L "$BIN_TO_PROBE" | grep -q "x86[_-]64"
-       if [ $? != 0 ]; then
-            CFG_CPUTYPE=i686
-       fi
-     fi
 fi
 
 
@@ -587,7 +595,7 @@ 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 llvm-static-stdcpp 0 "statically link to libstdc++ for LLVM"
-opt rpath 0 "build rpaths into rustc itself"
+opt rpath 1 "build rpaths into rustc itself"
 opt stage0-landing-pads 1 "enable landing pads during bootstrap with stage0"
 # This is used by the automation to produce single-target nightlies
 opt dist-host-only 0 "only install bins for the host architecture"
@@ -616,8 +624,10 @@ valopt android-cross-path "/opt/ndk_standalone" "Android NDK standalone path (de
 valopt i686-linux-android-ndk "" "i686-linux-android NDK standalone path"
 valopt arm-linux-androideabi-ndk "" "arm-linux-androideabi NDK standalone path"
 valopt aarch64-linux-android-ndk "" "aarch64-linux-android NDK standalone path"
+valopt nacl-cross-path  "" "NaCl SDK path (Pepper Canary is recommended). Must be absolute!"
 valopt release-channel "dev" "the name of the release channel to build"
 valopt musl-root "/usr/local" "MUSL root installation directory"
+valopt extra-filename "" "Additional data that is hashed and passed to the -C extra-filename flag"
 
 # Used on systems where "cc" and "ar" are unavailable
 valopt default-linker "cc" "the default linker"
@@ -939,6 +949,13 @@ then
     putvar CFG_ENABLE_CLANG
 fi
 
+if [ -z "$CFG_DISABLE_LIBCPP" -a -n "$CFG_ENABLE_CLANG" ]
+then
+    CFG_USING_LIBCPP="1"
+else
+    CFG_USING_LIBCPP="0"
+fi
+
 # Same with jemalloc.  save the setting here.
 if [ -n "$CFG_DISABLE_JEMALLOC" ]
 then
@@ -1018,7 +1035,7 @@ then
         if [ -n "$CFG_OSX_CLANG_VERSION" ]
         then
             case $CFG_OSX_CLANG_VERSION in
-                (7.0*)
+                (7.0* | 7.1* | 7.2*)
                 step_msg "found ok version of APPLE CLANG: $CFG_OSX_CLANG_VERSION"
                 ;;
                 (*)
@@ -1140,7 +1157,12 @@ do
                 fi
             done
             ;;
-
+        *-unknown-nacl)
+           if [ -z "$CFG_NACL_CROSS_PATH" ]
+           then
+               err "I need the NaCl SDK path! (use --nacl-cross-path)"
+           fi
+           ;;
         arm-apple-darwin)
             if [ $CFG_OSTYPE != apple-darwin ]
             then
@@ -1682,7 +1704,7 @@ do
         CXXFLAGS="$CXXFLAGS $LLVM_CXXFLAGS"
         LDFLAGS="$LDFLAGS $LLVM_LDFLAGS"
 
-        if [ -z "$CFG_DISABLE_LIBCPP" ] && [ -n "$CFG_USING_CLANG" ]; then
+        if [ "$CFG_USING_LIBCPP" != "0" ]; then
             LLVM_OPTS="$LLVM_OPTS --enable-libcpp"
         fi
 
@@ -1742,7 +1764,9 @@ putvar CFG_DISABLE_MANAGE_SUBMODULES
 putvar CFG_AARCH64_LINUX_ANDROID_NDK
 putvar CFG_ARM_LINUX_ANDROIDEABI_NDK
 putvar CFG_I686_LINUX_ANDROID_NDK
+putvar CFG_NACL_CROSS_PATH
 putvar CFG_MANDIR
+putvar CFG_USING_LIBCPP
 
 # Avoid spurious warnings from clang by feeding it original source on
 # ccache-miss rather than preprocessed input.
index 9a91097458e90fdd101dbafc0d51e777935b78ad..9244cc43650fe706901690107abc605256b16ef0 100644 (file)
@@ -8,8 +8,8 @@ CFG_LIB_NAME_arm-unknown-linux-gnueabi=lib$(1).so
 CFG_STATIC_LIB_NAME_arm-unknown-linux-gnueabi=lib$(1).a
 CFG_LIB_GLOB_arm-unknown-linux-gnueabi=lib$(1)-*.so
 CFG_LIB_DSYM_GLOB_arm-unknown-linux-gnueabi=lib$(1)-*.dylib.dSYM
-CFG_JEMALLOC_CFLAGS_arm-unknown-linux-gnueabi := -D__arm__ -mfpu=vfp $(CFLAGS)
-CFG_GCCISH_CFLAGS_arm-unknown-linux-gnueabi := -Wall -g -fPIC -D__arm__ -mfpu=vfp $(CFLAGS)
+CFG_JEMALLOC_CFLAGS_arm-unknown-linux-gnueabi := -D__arm__ -mfloat-abi=soft $(CFLAGS)
+CFG_GCCISH_CFLAGS_arm-unknown-linux-gnueabi := -Wall -g -fPIC -D__arm__ -mfloat-abi=soft $(CFLAGS)
 CFG_GCCISH_CXXFLAGS_arm-unknown-linux-gnueabi := -fno-rtti $(CXXFLAGS)
 CFG_GCCISH_LINK_FLAGS_arm-unknown-linux-gnueabi := -shared -fPIC -g
 CFG_GCCISH_DEF_FLAG_arm-unknown-linux-gnueabi := -Wl,--export-dynamic,--dynamic-list=
index 0ac0ca98a2f1eece8fbdb20c65d154776bf74806..bbc0c2d6f396ecad629d20fbbf06d03c76eb5108 100644 (file)
@@ -7,7 +7,7 @@ CFG_LIB_NAME_i686-unknown-freebsd=lib$(1).so
 CFG_STATIC_LIB_NAME_i686-unknown-freebsd=lib$(1).a
 CFG_LIB_GLOB_i686-unknown-freebsd=lib$(1)-*.so
 CFG_LIB_DSYM_GLOB_i686-unknown-freebsd=$(1)-*.dylib.dSYM
-CFG_JEMALLOC_CFLAGS_i686-unknown-freebsd := -m32 -arch i386 -I/usr/local/include $(CFLAGS)
+CFG_JEMALLOC_CFLAGS_i686-unknown-freebsd := -m32 -I/usr/local/include $(CFLAGS)
 CFG_GCCISH_CFLAGS_i686-unknown-freebsd := -Wall -Werror -g -fPIC -m32 -arch i386 -I/usr/local/include $(CFLAGS)
 CFG_GCCISH_LINK_FLAGS_i686-unknown-freebsd := -m32 -shared -fPIC -g -pthread -lrt
 CFG_GCCISH_DEF_FLAG_i686-unknown-freebsd := -Wl,--export-dynamic,--dynamic-list=
diff --git a/mk/cfg/le32-unknown-nacl.mk b/mk/cfg/le32-unknown-nacl.mk
new file mode 100644 (file)
index 0000000..a733672
--- /dev/null
@@ -0,0 +1,40 @@
+# le32-unknown-nacl (portable, PNaCl)
+ifneq ($(CFG_NACL_CROSS_PATH),)
+
+CC_le32-unknown-nacl=$(shell $(CFG_PYTHON) $(CFG_NACL_CROSS_PATH)/tools/nacl_config.py -t pnacl --tool cc)
+CXX_le32-unknown-nacl=$(shell $(CFG_PYTHON) $(CFG_NACL_CROSS_PATH)/tools/nacl_config.py -t pnacl --tool c++)
+CPP_le32-unknown-nacl=$(CXX_le32-unknown-nacl) -E
+AR_le32-unknown-nacl=$(shell $(CFG_PYTHON) $(CFG_NACL_CROSS_PATH)/tools/nacl_config.py -t pnacl --tool ar)
+
+CFG_PNACL_TOOLCHAIN := $(abspath $(dir $(AR_le32-unknown-nacl)/../))
+
+# Note: pso's aren't supported by PNaCl.
+CFG_LIB_NAME_le32-unknown-nacl=lib$(1).pso
+CFG_STATIC_LIB_NAME_le32-unknown-nacl=lib$(1).a
+CFG_LIB_GLOB_le32-unknown-nacl=lib$(1)-*.pso
+CFG_LIB_DSYM_GLOB_le32-unknown-nacl=lib$(1)-*.dylib.dSYM
+CFG_GCCISH_CFLAGS_le32-unknown-nacl := -Wall -Wno-unused-variable -Wno-unused-value $(shell $(CFG_PYTHON) $(CFG_NACL_CROSS_PATH)/tools/nacl_config.py -t pnacl --cflags) -D_YUGA_LITTLE_ENDIAN=1 -D_YUGA_BIG_ENDIAN=0
+CFG_GCCISH_CXXFLAGS_le32-unknown-nacl := -stdlib=libc++ $(CFG_GCCISH_CFLAGS_le32-unknown-nacl)
+CFG_GCCISH_LINK_FLAGS_le32-unknown-nacl := -static -pthread -lm
+CFG_GCCISH_DEF_FLAG_le32-unknown-nacl := -Wl,--export-dynamic,--dynamic-list=
+CFG_GCCISH_PRE_LIB_FLAGS_le32-unknown-nacl := -Wl,-no-whole-archive
+CFG_GCCISH_POST_LIB_FLAGS_le32-unknown-nacl :=
+CFG_DEF_SUFFIX_le32-unknown-nacl := .le32.nacl.def
+CFG_INSTALL_NAME_le32-unknown-nacl =
+CFG_EXE_SUFFIX_le32-unknown-nacl = .pexe
+CFG_WINDOWSY_le32-unknown-nacl :=
+CFG_UNIXY_le32-unknown-nacl := 1
+CFG_NACLY_le32-unknown-nacl := 1
+CFG_PATH_MUNGE_le32-unknown-nacl := true
+CFG_LDPATH_le32-unknown-nacl :=
+CFG_RUN_le32-unknown-nacl=$(2)
+CFG_RUN_TARG_le32-unknown-nacl=$(call CFG_RUN_le32-unknown-nacl,,$(2))
+RUSTC_FLAGS_le32-unknown-nacl:=
+RUSTC_CROSS_FLAGS_le32-unknown-nacl=-L $(CFG_NACL_CROSS_PATH)/lib/pnacl/Release -L $(CFG_PNACL_TOOLCHAIN)/lib/clang/3.7.0/lib/le32-nacl -L $(CFG_PNACL_TOOLCHAIN)/le32-nacl/usr/lib -L $(CFG_PNACL_TOOLCHAIN)/le32-nacl/lib
+CFG_GNU_TRIPLE_le32-unknown-nacl := le32-unknown-nacl
+
+# strdup isn't defined unless -std=gnu++11 is used :/
+LLVM_FILTER_CXXFLAGS_le32-unknown-nacl := -std=c++11
+LLVM_EXTRA_CXXFLAGS_le32-unknown-nacl := -std=gnu++11
+
+endif
diff --git a/mk/cfg/powerpc64-unknown-linux-gnu.mk b/mk/cfg/powerpc64-unknown-linux-gnu.mk
new file mode 100644 (file)
index 0000000..a9e8585
--- /dev/null
@@ -0,0 +1,24 @@
+# powerpc64-unknown-linux-gnu configuration
+CROSS_PREFIX_powerpc64-unknown-linux-gnu=powerpc64-linux-gnu-
+CC_powerpc64-unknown-linux-gnu=$(CC)
+CXX_powerpc64-unknown-linux-gnu=$(CXX)
+CPP_powerpc64-unknown-linux-gnu=$(CPP)
+AR_powerpc64-unknown-linux-gnu=$(AR)
+CFG_LIB_NAME_powerpc64-unknown-linux-gnu=lib$(1).so
+CFG_STATIC_LIB_NAME_powerpc64-unknown-linux-gnu=lib$(1).a
+CFG_LIB_GLOB_powerpc64-unknown-linux-gnu=lib$(1)-*.so
+CFG_LIB_DSYM_GLOB_powerpc64-unknown-linux-gnu=lib$(1)-*.dylib.dSYM
+CFG_CFLAGS_powerpc64-unknown-linux-gnu := -m64 $(CFLAGS)
+CFG_GCCISH_CFLAGS_powerpc64-unknown-linux-gnu := -Wall -Werror -g -fPIC -m64 $(CFLAGS)
+CFG_GCCISH_CXXFLAGS_powerpc64-unknown-linux-gnu := -fno-rtti $(CXXFLAGS)
+CFG_GCCISH_LINK_FLAGS_powerpc64-unknown-linux-gnu := -shared -fPIC -ldl -pthread  -lrt -g -m64
+CFG_GCCISH_DEF_FLAG_powerpc64-unknown-linux-gnu := -Wl,--export-dynamic,--dynamic-list=
+CFG_LLC_FLAGS_powerpc64-unknown-linux-gnu :=
+CFG_INSTALL_NAME_powerpc64-unknown-linux-gnu =
+CFG_EXE_SUFFIX_powerpc64-unknown-linux-gnu =
+CFG_WINDOWSY_powerpc64-unknown-linux-gnu :=
+CFG_UNIXY_powerpc64-unknown-linux-gnu := 1
+CFG_LDPATH_powerpc64-unknown-linux-gnu :=
+CFG_RUN_powerpc64-unknown-linux-gnu=$(2)
+CFG_RUN_TARG_powerpc64-unknown-linux-gnu=$(call CFG_RUN_powerpc64-unknown-linux-gnu,,$(2))
+CFG_GNU_TRIPLE_powerpc64-unknown-linux-gnu := powerpc64-unknown-linux-gnu
diff --git a/mk/cfg/powerpc64le-unknown-linux-gnu.mk b/mk/cfg/powerpc64le-unknown-linux-gnu.mk
new file mode 100644 (file)
index 0000000..a204933
--- /dev/null
@@ -0,0 +1,24 @@
+# powerpc64le-unknown-linux-gnu configuration
+CROSS_PREFIX_powerpc64le-unknown-linux-gnu=powerpc64le-linux-gnu-
+CC_powerpc64le-unknown-linux-gnu=$(CC)
+CXX_powerpc64le-unknown-linux-gnu=$(CXX)
+CPP_powerpc64le-unknown-linux-gnu=$(CPP)
+AR_powerpc64le-unknown-linux-gnu=$(AR)
+CFG_LIB_NAME_powerpc64le-unknown-linux-gnu=lib$(1).so
+CFG_STATIC_LIB_NAME_powerpc64le-unknown-linux-gnu=lib$(1).a
+CFG_LIB_GLOB_powerpc64le-unknown-linux-gnu=lib$(1)-*.so
+CFG_LIB_DSYM_GLOB_powerpc64le-unknown-linux-gnu=lib$(1)-*.dylib.dSYM
+CFG_CFLAGS_powerpc64le-unknown-linux-gnu := -m64 $(CFLAGS)
+CFG_GCCISH_CFLAGS_powerpc64le-unknown-linux-gnu := -Wall -Werror -g -fPIC -m64 $(CFLAGS)
+CFG_GCCISH_CXXFLAGS_powerpc64le-unknown-linux-gnu := -fno-rtti $(CXXFLAGS)
+CFG_GCCISH_LINK_FLAGS_powerpc64le-unknown-linux-gnu := -shared -fPIC -ldl -pthread  -lrt -g -m64
+CFG_GCCISH_DEF_FLAG_powerpc64le-unknown-linux-gnu := -Wl,--export-dynamic,--dynamic-list=
+CFG_LLC_FLAGS_powerpc64le-unknown-linux-gnu :=
+CFG_INSTALL_NAME_powerpc64le-unknown-linux-gnu =
+CFG_EXE_SUFFIX_powerpc64le-unknown-linux-gnu =
+CFG_WINDOWSY_powerpc64le-unknown-linux-gnu :=
+CFG_UNIXY_powerpc64le-unknown-linux-gnu := 1
+CFG_LDPATH_powerpc64le-unknown-linux-gnu :=
+CFG_RUN_powerpc64le-unknown-linux-gnu=$(2)
+CFG_RUN_TARG_powerpc64le-unknown-linux-gnu=$(call CFG_RUN_powerpc64le-unknown-linux-gnu,,$(2))
+CFG_GNU_TRIPLE_powerpc64le-unknown-linux-gnu := powerpc64le-unknown-linux-gnu
index dd6d19f7491bb688d8f7c588392afd18c9a6a4c5..afffec1a53a913a398f3c34da36e475314f0a845 100644 (file)
@@ -8,7 +8,7 @@ CFG_STATIC_LIB_NAME_x86_64-unknown-bitrig=lib$(1).a
 CFG_LIB_GLOB_x86_64-unknown-bitrig=lib$(1)-*.so
 CFG_LIB_DSYM_GLOB_x86_64-unknown-bitrig=$(1)-*.dylib.dSYM
 CFG_JEMALLOC_CFLAGS_x86_64-unknown-bitrig := -m64 -I/usr/include $(CFLAGS)
-CFG_GCCISH_CFLAGS_x86_64-unknown-bitrig := -Wall -Werror -fPIC -m64 -I/usr/include $(CFLAGS)
+CFG_GCCISH_CFLAGS_x86_64-unknown-bitrig := -Wall -Werror -fPIE -fPIC -m64 -I/usr/include $(CFLAGS)
 CFG_GCCISH_LINK_FLAGS_x86_64-unknown-bitrig := -shared -pic -pthread -m64 $(LDFLAGS)
 CFG_GCCISH_DEF_FLAG_x86_64-unknown-bitrig := -Wl,--export-dynamic,--dynamic-list=
 CFG_LLC_FLAGS_x86_64-unknown-bitrig :=
index 261616ecf1fdaea8c14cff20c9bf49c7c44d1c30..e6f28482284e0977642aa0f8107f8deb421b289f 100644 (file)
@@ -20,3 +20,4 @@ CFG_LDPATH_x86_64-unknown-openbsd :=
 CFG_RUN_x86_64-unknown-openbsd=$(2)
 CFG_RUN_TARG_x86_64-unknown-openbsd=$(call CFG_RUN_x86_64-unknown-openbsd,,$(2))
 CFG_GNU_TRIPLE_x86_64-unknown-openbsd := x86_64-unknown-openbsd
+RUSTC_FLAGS_x86_64-unknown-openbsd=-C linker=$(call FIND_COMPILER,$(CC))
index 8ce0a41d9786c01c3eee35a481c71cc2b0574118..5cc8a46878439740273abc44fc2629219da25819 100644 (file)
@@ -57,8 +57,8 @@ TARGET_CRATES := libc std flate arena term \
 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_front rustc_platform_intrinsics \
-                rustc_plugin rustc_metadata
-HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc fmt_macros
+                rustc_plugin rustc_metadata rustc_passes
+HOST_CRATES := syntax syntax_ext $(RUSTC_CRATES) rustdoc fmt_macros
 TOOLS := compiletest rustdoc rustc rustbook error-index-generator
 
 DEPS_core :=
@@ -71,7 +71,7 @@ DEPS_rustc_bitflags := core
 DEPS_rustc_unicode := core
 
 DEPS_std := core libc rand alloc collections rustc_unicode \
-       native:rust_builtin native:backtrace \
+       native:backtrace \
        alloc_system
 DEPS_arena := std
 DEPS_glob := std
@@ -86,9 +86,10 @@ DEPS_serialize := std log
 DEPS_term := std log
 DEPS_test := std getopts serialize rbml term native:rust_test_helpers
 
-DEPS_syntax := std term serialize log fmt_macros arena libc rustc_bitflags
+DEPS_syntax := std term serialize log arena libc rustc_bitflags
+DEPS_syntax_ext := syntax fmt_macros
 
-DEPS_rustc := syntax flate arena serialize getopts rustc_front\
+DEPS_rustc := syntax fmt_macros flate arena serialize getopts rbml rustc_front\
               log graphviz rustc_llvm rustc_back rustc_data_structures
 DEPS_rustc_back := std syntax rustc_llvm rustc_front flate log libc
 DEPS_rustc_borrowck := rustc rustc_front log graphviz syntax
@@ -96,13 +97,14 @@ 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_front rustc_plugin \
-                     rustc_metadata
+                     rustc_metadata syntax_ext rustc_passes
 DEPS_rustc_front := std syntax log serialize
 DEPS_rustc_lint := rustc log syntax
 DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags
 DEPS_rustc_metadata := rustc rustc_front syntax rbml
+DEPS_rustc_passes := syntax rustc core
 DEPS_rustc_mir := rustc rustc_front syntax
-DEPS_rustc_resolve := rustc rustc_front log syntax
+DEPS_rustc_resolve := arena rustc rustc_front log syntax
 DEPS_rustc_platform_intrinsics := rustc rustc_llvm
 DEPS_rustc_plugin := rustc rustc_metadata syntax
 DEPS_rustc_privacy := rustc rustc_front log syntax
@@ -174,9 +176,5 @@ endef
 
 $(foreach crate,$(TOOLS),$(eval $(call RUST_TOOL,$(crate))))
 
-ifdef CFG_DISABLE_ELF_TLS
-RUSTFLAGS_std := --cfg no_elf_tls
-endif
-
 CRATEFILE_libc := $(SREL)src/liblibc/src/lib.rs
 RUSTFLAGS_libc := --cfg stdbuild
index b2a90f583f7b2bf445cd823095a7ca0f4e947454..a4174efa5efa948381de65507131ee5a29b12e2c 100644 (file)
@@ -80,10 +80,16 @@ endif
 
 
 # LLVM linkage:
+# Note: Filter with llvm-config so that optional targets which aren't present
+# don't cause errors (ie PNaCl's target is only present within PNaCl's LLVM
+# fork).
 LLVM_LINKAGE_PATH_$(1):=$$(abspath $$(RT_OUTPUT_DIR_$(1))/llvmdeps.rs)
 $$(LLVM_LINKAGE_PATH_$(1)): $(S)src/etc/mklldeps.py $$(LLVM_CONFIG_$(1))
-       $(Q)$(CFG_PYTHON) "$$<" "$$@" "$$(LLVM_COMPONENTS)" "$$(CFG_ENABLE_LLVM_STATIC_STDCPP)" \
-               $$(LLVM_CONFIG_$(1)) "$(CFG_STDCPP_NAME)"
+       $(Q)$(CFG_PYTHON) "$$<" "$$@" "$$(filter $$(shell \
+                               $$(LLVM_CONFIG_$(1)) --components), \
+                        $(LLVM_OPTIONAL_COMPONENTS)) $(LLVM_REQUIRED_COMPONENTS)" \
+               "$$(CFG_ENABLE_LLVM_STATIC_STDCPP)" $$(LLVM_CONFIG_$(1)) \
+               "$(CFG_STDCPP_NAME)" "$$(CFG_USING_LIBCPP)"
 endef
 
 $(foreach host,$(CFG_HOST), \
@@ -95,6 +101,8 @@ $(foreach host,$(CFG_HOST), \
 # This can't be done in target.mk because it's included before this file.
 define LLVM_LINKAGE_DEPS
 $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.rustc_llvm: $$(LLVM_LINKAGE_PATH_$(2))
+RUSTFLAGS$(1)_rustc_llvm_T_$(2) += $$(shell echo $$(LLVM_ALL_COMPONENTS_$(2)) | tr '-' '_' |\
+       sed -e 's/^ //;s/\([^ ]*\)/\-\-cfg have_component_\1/g')
 endef
 
 $(foreach source,$(CFG_HOST), \
index 7108824b49f56c748f143fde53ce16ce1bd33e88..963c12f7750e551e8d5c8d6a5811aa7bbd28e3da 100644 (file)
@@ -13,7 +13,7 @@
 ######################################################################
 
 # The version number
-CFG_RELEASE_NUM=1.6.0
+CFG_RELEASE_NUM=1.7.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
@@ -22,7 +22,7 @@ CFG_PRERELEASE_VERSION=.4
 
 # Append a version-dependent hash to each library, so we can install different
 # versions in the same place
-CFG_FILENAME_EXTRA=$(shell printf '%s' $(CFG_RELEASE) | $(CFG_HASH_COMMAND))
+CFG_FILENAME_EXTRA=$(shell printf '%s' $(CFG_RELEASE)$(CFG_EXTRA_FILENAME) | $(CFG_HASH_COMMAND))
 
 ifeq ($(CFG_RELEASE_CHANNEL),stable)
 # This is the normal semver version string, e.g. "0.12.0", "0.12.0-nightly"
@@ -131,11 +131,7 @@ endif
 
 ifdef CFG_ENABLE_DEBUGINFO
   $(info cfg: enabling debuginfo (CFG_ENABLE_DEBUGINFO))
-  # FIXME: Re-enable -g in stage0 after new snapshot
-  #CFG_RUSTC_FLAGS += -g
-  RUSTFLAGS_STAGE1 += -g
-  RUSTFLAGS_STAGE2 += -g
-  RUSTFLAGS_STAGE3 += -g
+  CFG_RUSTC_FLAGS += -g
 endif
 
 ifdef SAVE_TEMPS
@@ -153,7 +149,7 @@ endif
 ifdef TRACE
   CFG_RUSTC_FLAGS += -Z trace
 endif
-ifdef CFG_ENABLE_RPATH
+ifndef CFG_DISABLE_RPATH
 CFG_RUSTC_FLAGS += -C rpath
 endif
 
@@ -276,9 +272,18 @@ endif
 # LLVM macros
 ######################################################################
 
-LLVM_COMPONENTS=x86 arm aarch64 mips powerpc ipo bitreader bitwriter linker asmparser mcjit \
+LLVM_OPTIONAL_COMPONENTS=x86 arm aarch64 mips powerpc pnacl
+LLVM_REQUIRED_COMPONENTS=ipo bitreader bitwriter linker asmparser mcjit \
                 interpreter instrumentation
 
+ifneq ($(CFG_LLVM_ROOT),)
+# Ensure we only try to link targets that the installed LLVM actually has:
+LLVM_COMPONENTS := $(filter $(shell $(CFG_LLVM_ROOT)/bin/llvm-config$(X_$(CFG_BUILD)) --components),\
+                       $(LLVM_OPTIONAL_COMPONENTS)) $(LLVM_REQUIRED_COMPONENTS)
+else
+LLVM_COMPONENTS := $(LLVM_OPTIONAL_COMPONENTS) $(LLVM_REQUIRED_COMPONENTS)
+endif
+
 # Only build these LLVM tools
 LLVM_TOOLS=bugpoint llc llvm-ar llvm-as llvm-dis llvm-mc opt llvm-extract
 
@@ -314,6 +319,8 @@ LLVM_HOST_TRIPLE_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --host-target)
 LLVM_AS_$(1)=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-as$$(X_$(1))
 LLC_$(1)=$$(CFG_LLVM_INST_DIR_$(1))/bin/llc$$(X_$(1))
 
+LLVM_ALL_COMPONENTS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --components)
+
 endef
 
 $(foreach host,$(CFG_HOST), \
@@ -476,7 +483,7 @@ endif
 endif
 
 LD_LIBRARY_PATH_ENV_HOSTDIR$(1)_T_$(2)_H_$(3) := \
-    $$(CURDIR)/$$(HLIB$(1)_H_$(3))
+    $$(CURDIR)/$$(HLIB$(1)_H_$(3)):$$(CFG_LLVM_INST_DIR_$(3))/lib
 LD_LIBRARY_PATH_ENV_TARGETDIR$(1)_T_$(2)_H_$(3) := \
     $$(CURDIR)/$$(TLIB1_T_$(2)_H_$(CFG_BUILD))
 
index fd8416e8a6e61b8399e73e42acc98538fc702690..5239086a6552ca26739e280736c4e0e3b37f6cd1 100644 (file)
@@ -64,14 +64,18 @@ define DEF_GOOD_VALGRIND
   ifeq ($(OSTYPE_$(1)),unknown-linux-gnu)
     GOOD_VALGRIND_$(1) = 1
   endif
-  ifneq (,$(filter $(OSTYPE_$(1)),darwin freebsd))
-    ifeq (HOST_$(1),x86_64)
+  ifneq (,$(filter $(OSTYPE_$(1)),apple-darwin freebsd))
+    ifeq ($(HOST_$(1)),x86_64)
       GOOD_VALGRIND_$(1) = 1
     endif
   endif
+  ifdef GOOD_VALGRIND_$(t)
+    $$(info cfg: have good valgrind for $(t))
+  else
+    $$(info cfg: no good valgrind for $(t))
+  endif
 endef
 $(foreach t,$(CFG_TARGET),$(eval $(call DEF_GOOD_VALGRIND,$(t))))
-$(foreach t,$(CFG_TARGET),$(info cfg: good valgrind for $(t) is $(GOOD_VALGRIND_$(t))))
 
 ifneq ($(findstring linux,$(CFG_OSTYPE)),)
   ifdef CFG_PERF
@@ -215,16 +219,6 @@ define CFG_MAKE_TOOLCHAIN
 
   ifeq ($$(findstring $(HOST_$(1)),arm aarch64 mips mipsel powerpc),)
 
-  # On OpenBSD, we need to pass the path of libstdc++.so to the linker
-  # (use path of libstdc++.a which is a known name for the same path)
-  ifeq ($(OSTYPE_$(1)),unknown-openbsd)
-    STDCPP_LIBDIR_RUSTFLAGS_$(1)= \
-        -L "$$(dir $$(shell $$(CC_$(1)) $$(CFG_GCCISH_CFLAGS_$(1)) \
-        -print-file-name=lib$(CFG_STDCPP_NAME).a))"
-  else
-    STDCPP_LIBDIR_RUSTFLAGS_$(1)=
-  endif
-
   # On Bitrig, we need the relocation model to be PIC for everything
   ifeq (,$(filter $(OSTYPE_$(1)),bitrig))
     LLVM_MC_RELOCATION_MODEL="pic"
index 8433b588e6ba464982c8f78a81ea2f9ff586153b..9dbbcbebb979d34a870b47e63c2c1572267f8e27 100644 (file)
--- a/mk/rt.mk
+++ b/mk/rt.mk
@@ -35,7 +35,7 @@
 # that's per-target so you're allowed to conditionally add files based on the
 # target.
 ################################################################################
-NATIVE_LIBS := rust_builtin hoedown miniz rust_test_helpers
+NATIVE_LIBS := hoedown miniz rust_test_helpers
 
 # $(1) is the target triple
 define NATIVE_LIBRARIES
@@ -50,8 +50,6 @@ NATIVE_DEPS_hoedown_$(1) := hoedown/src/autolink.c \
                        hoedown/src/stack.c \
                        hoedown/src/version.c
 NATIVE_DEPS_miniz_$(1) = miniz.c
-NATIVE_DEPS_rust_builtin_$(1) := rust_builtin.c \
-                       rust_android_dummy.c
 NATIVE_DEPS_rust_test_helpers_$(1) := rust_test_helpers.c
 
 ################################################################################
@@ -128,12 +126,25 @@ define DEF_THIRD_PARTY_TARGETS
 
 # $(1) is the target triple
 
-ifeq ($$(CFG_WINDOWSY_$(1)), 1)
-  # This isn't necessarily a desired option, but it's harmless and works around
-  # what appears to be a mingw-w64 bug.
+ifeq ($$(CFG_WINDOWSY_$(1)),1)
+  # A bit of history here, this used to be --enable-lazy-lock added in #14006
+  # which was filed with jemalloc in jemalloc/jemalloc#83 which was also
+  # reported to MinGW: http://sourceforge.net/p/mingw-w64/bugs/395/
+  #
+  # When updating jemalloc to 4.0, however, it was found that binaries would
+  # exit with the status code STATUS_RESOURCE_NOT_OWNED indicating that a thread
+  # was unlocking a mutex it never locked. Disabling this "lazy lock" option
+  # seems to fix the issue, but it was enabled by default for MinGW targets in
+  # 13473c7 for jemalloc.
+  #
+  # As a result of all that, force disabling lazy lock on Windows, and after
+  # reading some code it at least *appears* that the initialization of mutexes
+  # is otherwise ok in jemalloc, so shouldn't cause problems hopefully...
   #
-  # https://sourceforge.net/p/mingw-w64/bugs/395/
-  JEMALLOC_ARGS_$(1) := --enable-lazy-lock
+  # tl;dr: make windows behave like other platforms by disabling lazy locking,
+  #        but requires passing an option due to a historical default with
+  #        jemalloc.
+  JEMALLOC_ARGS_$(1) := --disable-lazy-lock
 else ifeq ($(OSTYPE_$(1)), apple-ios)
   JEMALLOC_ARGS_$(1) := --disable-tls
 else ifeq ($(findstring android, $(OSTYPE_$(1))), android)
index f1b5f9e315569a97d515ace943622d9a1eccd412..f90b09479c9858c338c7264be72f3773a0380285 100644 (file)
@@ -95,7 +95,6 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \
                $$(RUSTFLAGS_$(4)) \
                $$(RUSTFLAGS$(1)_$(4)) \
                $$(RUSTFLAGS$(1)_$(4)_T_$(2)) \
-               $$(STDCPP_LIBDIR_RUSTFLAGS_$(2)) \
                --out-dir $$(@D) \
                -C extra-filename=-$$(CFG_FILENAME_EXTRA) \
                $$<
@@ -130,7 +129,7 @@ $$(TBIN$(1)_T_$(2)_H_$(3))/$(4)$$(X_$(2)): \
                | $$(TBIN$(1)_T_$(2)_H_$(3))/
        @$$(call E, rustc: $$@)
        $$(STAGE$(1)_T_$(2)_H_$(3)) \
-               $$(STDCPP_LIBDIR_RUSTFLAGS_$(2)) \
+               $$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \
                -o $$@ $$< --cfg $(4)
 
 endef
index b41eaba190085dc6acda5fe7455fb94293260bf2..0f30ff8711e5e26fc8063e8155f69a6727c2bbc8 100644 (file)
@@ -393,8 +393,7 @@ $(3)/stage$(1)/test/$(4)test-$(2)$$(X_$(2)): \
            $$(subst @,,$$(STAGE$(1)_T_$(2)_H_$(3))) -o $$@ $$< --test \
                -L "$$(RT_OUTPUT_DIR_$(2))" \
                $$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \
-               $$(RUSTFLAGS_$(4)) \
-               $$(STDCPP_LIBDIR_RUSTFLAGS_$(2))
+               $$(RUSTFLAGS_$(4))
 
 endef
 
@@ -664,9 +663,9 @@ CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3) := \
         --android-cross-path=$(CFG_ANDROID_CROSS_PATH) \
         --adb-path=$(CFG_ADB) \
         --adb-test-dir=$(CFG_ADB_TEST_DIR) \
-        --host-rustcflags "$(RUSTC_FLAGS_$(3)) $$(CTEST_RUSTC_FLAGS) -L $$(RT_OUTPUT_DIR_$(3)) $$(STDCPP_LIBDIR_RUSTFLAGS_$(3))" \
+        --host-rustcflags "$(RUSTC_FLAGS_$(3)) $$(CTEST_RUSTC_FLAGS) -L $$(RT_OUTPUT_DIR_$(3))" \
         --lldb-python-dir=$(CFG_LLDB_PYTHON_DIR) \
-        --target-rustcflags "$(RUSTC_FLAGS_$(2)) $$(CTEST_RUSTC_FLAGS) -L $$(RT_OUTPUT_DIR_$(2)) $$(STDCPP_LIBDIR_RUSTFLAGS_$(2))" \
+        --target-rustcflags "$(RUSTC_FLAGS_$(2)) $$(CTEST_RUSTC_FLAGS) -L $$(RT_OUTPUT_DIR_$(2))" \
         $$(CTEST_TESTARGS)
 
 ifdef CFG_VALGRIND_RPASS
@@ -1072,7 +1071,9 @@ $(3)/test/run-make/%-$(1)-T-$(2)-H-$(3).ok: \
            "$$(LD_LIBRARY_PATH_ENV_TARGETDIR$(1)_T_$(2)_H_$(3))" \
            $(1) \
            $$(S) \
-           $(3)
+           $(3) \
+           "$$(LLVM_LIBDIR_RUSTFLAGS_$(3))" \
+           "$$(LLVM_ALL_COMPONENTS_$(3))"
        @touch -r $$@.start_time $$@ && rm $$@.start_time
 else
 # FIXME #11094 - The above rule doesn't work right for multiple targets
index 1b1f5115b603356acee918998755fede69f2cce7..459b43b4ffe5d8ca23ffaa60a0ead444d88d871e 100644 (file)
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use self::TargetLocation::*;
-
 use common::Config;
 use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
 use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc};
index fbafee102e4b2a59e3ba05934f1c9be865560c74..103ca463f7a58e2755a50e198070ecdfd8507073 100644 (file)
@@ -38,6 +38,8 @@ const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[
     ("mips", "mips"),
     ("msp430", "msp430"),
     ("powerpc", "powerpc"),
+    ("powerpc64", "powerpc64"),
+    ("powerpc64le", "powerpc64le"),
     ("s390x", "systemz"),
     ("sparc", "sparc"),
     ("x86_64", "x86_64"),
index 565f143a291fb03fdb32bc6eb6dbb807fe5b0337..9f9b6a9b043dde75197310bf764f1338f75acc29 100644 (file)
@@ -14,31 +14,25 @@ Even then, Rust still allows precise control like a low-level language would.
 
 [rust]: https://www.rust-lang.org
 
-“The Rust Programming Language” is split into eight sections. This introduction
+“The Rust Programming Language” is split into chapters. This introduction
 is the first. After this:
 
 * [Getting started][gs] - Set up your computer for Rust development.
-* [Learn Rust][lr] - Learn Rust programming through small projects.
-* [Effective Rust][er] - Higher-level concepts for writing excellent Rust code.
+* [Tutorial: Guessing Game][gg] - Learn some Rust with a small project.
 * [Syntax and Semantics][ss] - Each bit of Rust, broken down into small chunks.
+* [Effective Rust][er] - Higher-level concepts for writing excellent Rust code.
 * [Nightly Rust][nr] - Cutting-edge features that aren’t in stable builds yet.
 * [Glossary][gl] - A reference of terms used in the book.
 * [Bibliography][bi] - Background on Rust's influences, papers about Rust.
 
 [gs]: getting-started.html
-[lr]: learn-rust.html
+[gg]: guessing-game.html
 [er]: effective-rust.html
 [ss]: syntax-and-semantics.html
 [nr]: nightly-rust.html
 [gl]: glossary.html
 [bi]: bibliography.html
 
-After reading this introduction, you’ll want to dive into either ‘Learn Rust’ or
-‘Syntax and Semantics’, depending on your preference: ‘Learn Rust’ if you want
-to dive in with a project, or ‘Syntax and Semantics’ if you prefer to start
-small, and learn a single concept thoroughly before moving onto the next.
-Copious cross-linking connects these parts together.
-
 ### Contributing
 
 The source files from which this book is generated can be found on
index 05723779b340e761f4ff3ebfd54e9e910f89ccf4..fe5e1c3990c5c8412a41fd3efe9c6b7835531c03 100644 (file)
@@ -1,10 +1,7 @@
 # Summary
 
 * [Getting Started](getting-started.md)
-* [Learn Rust](learn-rust.md)
-    * [Guessing Game](guessing-game.md)
-    * [Dining Philosophers](dining-philosophers.md)
-    * [Rust Inside Other Languages](rust-inside-other-languages.md)
+* [Tutorial: Guessing Game](guessing-game.md)
 * [Syntax and Semantics](syntax-and-semantics.md)
     * [Variable Bindings](variable-bindings.md)
     * [Functions](functions.md)
index fe4f27b9d954cb6e3b341c148f3e8b259cf445e2..a0676a33996fc34bf83a380806db5a3acaa11373 100644 (file)
@@ -24,7 +24,7 @@ fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> u32 { ... }
 ```
 
 Our distance calculation works regardless of our `Edge` type, so the `E` stuff in
-this signature is just a distraction.
+this signature is a distraction.
 
 What we really want to say is that a certain `E`dge and `N`ode type come together
 to form each kind of `Graph`. We can do that with associated types:
@@ -118,10 +118,10 @@ impl Graph for MyGraph {
 This silly implementation always returns `true` and an empty `Vec<Edge>`, but it
 gives you an idea of how to implement this kind of thing. We first need three
 `struct`s, one for the graph, one for the node, and one for the edge. If it made
-more sense to use a different type, that would work as well, we’re just going to
+more sense to use a different type, that would work as well, we’re going to
 use `struct`s for all three here.
 
-Next is the `impl` line, which is just like implementing any other trait.
+Next is the `impl` line, which is an implementation like any other trait.
 
 From here, we use `=` to define our associated types. The name the trait uses
 goes on the left of the `=`, and the concrete type we’re `impl`ementing this
index ba02053b6b8944761e85f9b9cd5feded040cebed..d32b1a91944e46366a5100f62808386619a959ef 100644 (file)
@@ -33,7 +33,7 @@ Rust, as well as publications about Rust.
 * [Non-blocking steal-half work queues](http://www.cs.bgu.ac.il/%7Ehendlerd/papers/p280-hendler.pdf)
 * [Reagents: expressing and composing fine-grained concurrency](http://www.mpi-sws.org/~turon/reagents.pdf)
 * [Algorithms for scalable synchronization of shared-memory multiprocessors](https://www.cs.rochester.edu/u/scott/papers/1991_TOCS_synch.pdf)
-* [Epoc-based reclamation](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf).
+* [Epoch-based reclamation](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf).
 
 ### Others
 
index 7108d957eddccccd6fe573fd34b8a73551c184d5..5cafe1693690ddf1a3b9a1524295b5f9906b42ad 100644 (file)
@@ -154,7 +154,7 @@ implemented. For this, we need something more dangerous.
 The `transmute` function is provided by a [compiler intrinsic][intrinsics], and
 what it does is very simple, but very scary. It tells Rust to treat a value of
 one type as though it were another type. It does this regardless of the
-typechecking system, and just completely trusts you.
+typechecking system, and completely trusts you.
 
 [intrinsics]: intrinsics.html
 
index d9e92de8d9abcbf5b21f16166b28e5b49e562a6f..f2b92e6dec4ddd664d48b0be1834353557783709 100644 (file)
@@ -52,7 +52,7 @@ These pointers cannot be copied in such a way that they outlive the lifetime ass
 
 ## `*const T` and `*mut T`
 
-These are C-like raw pointers with no lifetime or ownership attached to them. They just point to
+These are C-like raw pointers with no lifetime or ownership attached to them. They point to
 some location in memory with no other restrictions. The only guarantee that these provide is that
 they cannot be dereferenced except in code marked `unsafe`.
 
@@ -255,7 +255,7 @@ major ones will be covered below.
 
 ## `Arc<T>`
 
-[`Arc<T>`][arc] is just a version of `Rc<T>` that uses an atomic reference count (hence, "Arc").
+[`Arc<T>`][arc] is a version of `Rc<T>` that uses an atomic reference count (hence, "Arc").
 This can be sent freely between threads.
 
 C++'s `shared_ptr` is similar to `Arc`, however in the case of C++ the inner data is always mutable.
@@ -340,11 +340,11 @@ With the former, the `RefCell<T>` is wrapping the `Vec<T>`, so the `Vec<T>` in i
 mutable. At the same time, there can only be one mutable borrow of the whole `Vec` at a given time.
 This means that your code cannot simultaneously work on different elements of the vector from
 different `Rc` handles. However, we are able to push and pop from the `Vec<T>` at will. This is
-similar to an `&mut Vec<T>` with the borrow checking done at runtime.
+similar to a `&mut Vec<T>` with the borrow checking done at runtime.
 
 With the latter, the borrowing is of individual elements, but the overall vector is immutable. Thus,
 we can independently borrow separate elements, but we cannot push or pop from the vector. This is
-similar to an `&mut [T]`[^3], but, again, the borrow checking is at runtime.
+similar to a `&mut [T]`[^3], but, again, the borrow checking is at runtime.
 
 In concurrent programs, we have a similar situation with `Arc<Mutex<T>>`, which provides shared
 mutability and ownership.
index 7d4452a4c847047d9478913034bcef43148ff17b..237545edc05bbf9231b516116bffdb1cba4a6f89 100644 (file)
@@ -208,7 +208,7 @@ different.
 
 Rust’s implementation of closures is a bit different than other languages. They
 are effectively syntax sugar for traits. You’ll want to make sure to have read
-the [traits chapter][traits] before this one, as well as the chapter on [trait
+the [traits][traits] section before this one, as well as the section on [trait
 objects][trait-objects].
 
 [traits]: traits.html
@@ -253,7 +253,7 @@ use it.
 # Taking closures as arguments
 
 Now that we know that closures are traits, we already know how to accept and
-return closures: just like any other trait!
+return closures: the same as any other trait!
 
 This also means that we can choose static vs dynamic dispatch as well. First,
 let’s write a function which takes something callable, calls it, and returns
@@ -271,7 +271,7 @@ let answer = call_with_one(|x| x + 2);
 assert_eq!(3, answer);
 ```
 
-We pass our closure, `|x| x + 2`, to `call_with_one`. It just does what it
+We pass our closure, `|x| x + 2`, to `call_with_one`. It does what it
 suggests: it calls the closure, giving it `1` as an argument.
 
 Let’s examine the signature of `call_with_one` in more depth:
@@ -448,7 +448,7 @@ This error is letting us know that we don’t have a `&'static Fn(i32) -> i32`,
 we have a `[closure@<anon>:7:9: 7:20]`. Wait, what?
 
 Because each closure generates its own environment `struct` and implementation
-of `Fn` and friends, these types are anonymous. They exist just solely for
+of `Fn` and friends, these types are anonymous. They exist solely for
 this closure. So Rust shows them as `closure@<anon>`, rather than some
 autogenerated name.
 
index 8ea6f4f6fcc2864cbbd32d4cf00979f644226a79..44569a04b98243e0f3b3ce2bb87bcf63f30f63a9 100644 (file)
@@ -305,10 +305,10 @@ fn main() {
 }
 ```
 
-We use the `mpsc::channel()` method to construct a new channel. We just `send`
+We use the `mpsc::channel()` method to construct a new channel. We `send`
 a simple `()` down the channel, and then wait for ten of them to come back.
 
-While this channel is just sending a generic signal, we can send any data that
+While this channel is sending a generic signal, we can send any data that
 is `Send` over the channel!
 
 ```rust
index 4a4648c7b563f4fe4bbcab815e54dbc9da7d6eb5..849c5f1212a571f8da422c5c775b9b174b84ed5a 100644 (file)
@@ -2,7 +2,7 @@
 
 When a project starts getting large, it’s considered good software
 engineering practice to split it up into a bunch of smaller pieces, and then
-fit them together. Its also important to have a well-defined interface, so
+fit them together. It is also important to have a well-defined interface, so
 that some of your functionality is private, and some is public. To facilitate
 these kinds of things, Rust has a module system.
 
@@ -222,7 +222,7 @@ fn hello() -> String {
 }
 ```
 
-Of course, you can copy and paste this from this web page, or just type
+Of course, you can copy and paste this from this web page, or type
 something else. It’s not important that you actually put ‘konnichiwa’ to learn
 about the module system.
 
@@ -299,7 +299,7 @@ depth.
 Rust allows you to precisely control which aspects of your interface are
 public, and so private is the default. To make things public, you use the `pub`
 keyword. Let’s focus on the `english` module first, so let’s reduce our `src/main.rs`
-to just this:
+to only this:
 
 ```rust,ignore
 extern crate phrases;
@@ -447,7 +447,7 @@ use phrases::english::{greetings, farewells};
 
 ## Re-exporting with `pub use`
 
-You don’t just use `use` to shorten identifiers. You can also use it inside of your crate
+You don’t only use `use` to shorten identifiers. You can also use it inside of your crate
 to re-export a function inside another module. This allows you to present an external
 interface that may not directly map to your internal code organization.
 
@@ -584,5 +584,5 @@ use sayings::english::farewells as en_farewells;
 ```
 
 As you can see, the curly brackets compress `use` statements for several items
-under the same path, and in this context `self` just refers back to that path.
+under the same path, and in this context `self` refers back to that path.
 Note: The curly brackets cannot be nested or mixed with star globbing.
index 65626c11462365995369692962387952161f4fe8..d69ef6cf7e83acfd24fb741a34cba8a2ad5f3199 100644 (file)
@@ -13,7 +13,7 @@ own allocator up and running.
 
 The compiler currently ships two default allocators: `alloc_system` and
 `alloc_jemalloc` (some targets don't have jemalloc, however). These allocators
-are just normal Rust crates and contain an implementation of the routines to
+are normal Rust crates and contain an implementation of the routines to
 allocate and deallocate memory. The standard library is not compiled assuming
 either one, and the compiler will decide which allocator is in use at
 compile-time depending on the type of output artifact being produced.
@@ -134,7 +134,7 @@ pub extern fn __rust_usable_size(size: usize, _align: usize) -> usize {
     size
 }
 
-# // just needed to get rustdoc to test this
+# // only needed to get rustdoc to test this
 # fn main() {}
 # #[lang = "panic_fmt"] fn panic_fmt() {}
 # #[lang = "eh_personality"] fn eh_personality() {}
diff --git a/src/doc/book/dining-philosophers.md b/src/doc/book/dining-philosophers.md
deleted file mode 100644 (file)
index a0f629c..0000000
+++ /dev/null
@@ -1,723 +0,0 @@
-% Dining Philosophers
-
-For our second project, let’s look at a classic concurrency problem. It’s
-called ‘the dining philosophers’. It was originally conceived by Dijkstra in
-1965, but we’ll use a lightly adapted version from [this paper][paper] by Tony
-Hoare in 1985.
-
-[paper]: http://www.usingcsp.com/cspbook.pdf
-
-> In ancient times, a wealthy philanthropist endowed a College to accommodate
-> five eminent philosophers. Each philosopher had a room in which they could
-> engage in their professional activity of thinking; there was also a common
-> dining room, furnished with a circular table, surrounded by five chairs, each
-> labelled by the name of the philosopher who was to sit in it. They sat
-> anticlockwise around the table. To the left of each philosopher there was
-> laid a golden fork, and in the center stood a large bowl of spaghetti, which
-> was constantly replenished. A philosopher was expected to spend most of
-> their time thinking; but when they felt hungry, they went to the dining
-> room, sat down in their own chair, picked up their own fork on their left,
-> and plunged it into the spaghetti. But such is the tangled nature of
-> spaghetti that a second fork is required to carry it to the mouth. The
-> philosopher therefore had also to pick up the fork on their right. When
-> they were finished they would put down both their forks, get up from their
-> chair, and continue thinking. Of course, a fork can be used by only one
-> philosopher at a time. If the other philosopher wants it, they just have
-> to wait until the fork is available again.
-
-This classic problem shows off a few different elements of concurrency. The
-reason is that it's actually slightly tricky to implement: a simple
-implementation can deadlock. For example, let's consider a simple algorithm
-that would solve this problem:
-
-1. A philosopher picks up the fork on their left.
-2. They then pick up the fork on their right.
-3. They eat.
-4. They return the forks.
-
-Now, let’s imagine this sequence of events:
-
-1. Philosopher 1 begins the algorithm, picking up the fork on their left.
-2. Philosopher 2 begins the algorithm, picking up the fork on their left.
-3. Philosopher 3 begins the algorithm, picking up the fork on their left.
-4. Philosopher 4 begins the algorithm, picking up the fork on their left.
-5. Philosopher 5 begins the algorithm, picking up the fork on their left.
-6. ... ? All the forks are taken, but nobody can eat!
-
-There are different ways to solve this problem. We’ll get to our solution in
-the tutorial itself. For now, let’s get started and create a new project with
-`cargo`:
-
-```bash
-$ cd ~/projects
-$ cargo new dining_philosophers --bin
-$ cd dining_philosophers
-```
-
-Now we can start modeling the problem itself. We’ll start with the philosophers
-in `src/main.rs`:
-
-```rust
-struct Philosopher {
-    name: String,
-}
-
-impl Philosopher {
-    fn new(name: &str) -> Philosopher {
-        Philosopher {
-            name: name.to_string(),
-        }
-    }
-}
-
-fn main() {
-    let p1 = Philosopher::new("Judith Butler");
-    let p2 = Philosopher::new("Gilles Deleuze");
-    let p3 = Philosopher::new("Karl Marx");
-    let p4 = Philosopher::new("Emma Goldman");
-    let p5 = Philosopher::new("Michel Foucault");
-}
-```
-
-Here, we make a [`struct`][struct] to represent a philosopher. For now,
-a name is all we need. We choose the [`String`][string] type for the name,
-rather than `&str`. Generally speaking, working with a type which owns its
-data is easier than working with one that uses references.
-
-[struct]: structs.html
-[string]: strings.html
-
-Let’s continue:
-
-```rust
-# struct Philosopher {
-#     name: String,
-# }
-impl Philosopher {
-    fn new(name: &str) -> Philosopher {
-        Philosopher {
-            name: name.to_string(),
-        }
-    }
-}
-```
-
-This `impl` block lets us define things on `Philosopher` structs. In this case,
-we define an ‘associated function’ called `new`. The first line looks like this:
-
-```rust
-# struct Philosopher {
-#     name: String,
-# }
-# impl Philosopher {
-fn new(name: &str) -> Philosopher {
-#         Philosopher {
-#             name: name.to_string(),
-#         }
-#     }
-# }
-```
-
-We take one argument, a `name`, of type `&str`. This is a reference to another
-string. It returns an instance of our `Philosopher` struct.
-
-```rust
-# struct Philosopher {
-#     name: String,
-# }
-# impl Philosopher {
-#    fn new(name: &str) -> Philosopher {
-Philosopher {
-    name: name.to_string(),
-}
-#     }
-# }
-```
-
-This creates a new `Philosopher`, and sets its `name` to our `name` argument.
-Not just the argument itself, though, as we call `.to_string()` on it. This
-will create a copy of the string that our `&str` points to, and give us a new
-`String`, which is the type of the `name` field of `Philosopher`.
-
-Why not accept a `String` directly? It’s nicer to call. If we took a `String`,
-but our caller had a `&str`, they’d have to call this method themselves. The
-downside of this flexibility is that we _always_ make a copy. For this small
-program, that’s not particularly important, as we know we’ll just be using
-short strings anyway.
-
-One last thing you’ll notice: we just define a `Philosopher`, and seemingly
-don’t do anything with it. Rust is an ‘expression based’ language, which means
-that almost everything in Rust is an expression which returns a value. This is
-true of functions as well — the last expression is automatically returned. Since
-we create a new `Philosopher` as the last expression of this function, we end
-up returning it.
-
-This name, `new()`, isn’t anything special to Rust, but it is a convention for
-functions that create new instances of structs. Before we talk about why, let’s
-look at `main()` again:
-
-```rust
-# struct Philosopher {
-#     name: String,
-# }
-#
-# impl Philosopher {
-#     fn new(name: &str) -> Philosopher {
-#         Philosopher {
-#             name: name.to_string(),
-#         }
-#     }
-# }
-#
-fn main() {
-    let p1 = Philosopher::new("Judith Butler");
-    let p2 = Philosopher::new("Gilles Deleuze");
-    let p3 = Philosopher::new("Karl Marx");
-    let p4 = Philosopher::new("Emma Goldman");
-    let p5 = Philosopher::new("Michel Foucault");
-}
-```
-
-Here, we create five variable bindings with five new philosophers.
-If we _didn’t_ define
-that `new()` function, it would look like this:
-
-```rust
-# struct Philosopher {
-#     name: String,
-# }
-fn main() {
-    let p1 = Philosopher { name: "Judith Butler".to_string() };
-    let p2 = Philosopher { name: "Gilles Deleuze".to_string() };
-    let p3 = Philosopher { name: "Karl Marx".to_string() };
-    let p4 = Philosopher { name: "Emma Goldman".to_string() };
-    let p5 = Philosopher { name: "Michel Foucault".to_string() };
-}
-```
-
-That’s much noisier. Using `new` has other advantages too, but even in
-this simple case, it ends up being nicer to use.
-
-Now that we’ve got the basics in place, there’s a number of ways that we can
-tackle the broader problem here. I like to start from the end first: let’s
-set up a way for each philosopher to finish eating. As a tiny step, let’s make
-a method, and then loop through all the philosophers, calling it:
-
-```rust
-struct Philosopher {
-    name: String,
-}
-
-impl Philosopher {
-    fn new(name: &str) -> Philosopher {
-        Philosopher {
-            name: name.to_string(),
-        }
-    }
-
-    fn eat(&self) {
-        println!("{} is done eating.", self.name);
-    }
-}
-
-fn main() {
-    let philosophers = vec![
-        Philosopher::new("Judith Butler"),
-        Philosopher::new("Gilles Deleuze"),
-        Philosopher::new("Karl Marx"),
-        Philosopher::new("Emma Goldman"),
-        Philosopher::new("Michel Foucault"),
-    ];
-
-    for p in &philosophers {
-        p.eat();
-    }
-}
-```
-
-Let’s look at `main()` first. Rather than have five individual variable
-bindings for our philosophers, we make a `Vec<T>` of them instead. `Vec<T>` is
-also called a ‘vector’, and it’s a growable array type. We then use a
-[`for`][for] loop to iterate through the vector, getting a reference to each
-philosopher in turn.
-
-[for]: loops.html#for
-
-In the body of the loop, we call `p.eat()`, which is defined above:
-
-```rust,ignore
-fn eat(&self) {
-    println!("{} is done eating.", self.name);
-}
-```
-
-In Rust, methods take an explicit `self` parameter. That’s why `eat()` is a
-method, but `new` is an associated function: `new()` has no `self`. For our
-first version of `eat()`, we just print out the name of the philosopher, and
-mention they’re done eating. Running this program should give you the following
-output:
-
-```text
-Judith Butler is done eating.
-Gilles Deleuze is done eating.
-Karl Marx is done eating.
-Emma Goldman is done eating.
-Michel Foucault is done eating.
-```
-
-Easy enough, they’re all done! We haven’t actually implemented the real problem
-yet, though, so we’re not done yet!
-
-Next, we want to make our philosophers not just finish eating, but actually
-eat. Here’s the next version:
-
-```rust
-use std::thread;
-use std::time::Duration;
-
-struct Philosopher {
-    name: String,
-}
-
-impl Philosopher {
-    fn new(name: &str) -> Philosopher {
-        Philosopher {
-            name: name.to_string(),
-        }
-    }
-
-    fn eat(&self) {
-        println!("{} is eating.", self.name);
-
-        thread::sleep(Duration::from_millis(1000));
-
-        println!("{} is done eating.", self.name);
-    }
-}
-
-fn main() {
-    let philosophers = vec![
-        Philosopher::new("Judith Butler"),
-        Philosopher::new("Gilles Deleuze"),
-        Philosopher::new("Karl Marx"),
-        Philosopher::new("Emma Goldman"),
-        Philosopher::new("Michel Foucault"),
-    ];
-
-    for p in &philosophers {
-        p.eat();
-    }
-}
-```
-
-Just a few changes. Let’s break it down.
-
-```rust,ignore
-use std::thread;
-```
-
-`use` brings names into scope. We’re going to start using the `thread` module
-from the standard library, and so we need to `use` it.
-
-```rust,ignore
-    fn eat(&self) {
-        println!("{} is eating.", self.name);
-
-        thread::sleep(Duration::from_millis(1000));
-
-        println!("{} is done eating.", self.name);
-    }
-```
-
-We now print out two messages, with a `sleep` in the middle. This will
-simulate the time it takes a philosopher to eat.
-
-If you run this program, you should see each philosopher eat in turn:
-
-```text
-Judith Butler is eating.
-Judith Butler is done eating.
-Gilles Deleuze is eating.
-Gilles Deleuze is done eating.
-Karl Marx is eating.
-Karl Marx is done eating.
-Emma Goldman is eating.
-Emma Goldman is done eating.
-Michel Foucault is eating.
-Michel Foucault is done eating.
-```
-
-Excellent! We’re getting there. There’s just one problem: we aren’t actually
-operating in a concurrent fashion, which is a core part of the problem!
-
-To make our philosophers eat concurrently, we need to make a small change.
-Here’s the next iteration:
-
-```rust
-use std::thread;
-use std::time::Duration;
-
-struct Philosopher {
-    name: String,
-}
-
-impl Philosopher {
-    fn new(name: &str) -> Philosopher {
-        Philosopher {
-            name: name.to_string(),
-        }
-    }
-
-    fn eat(&self) {
-        println!("{} is eating.", self.name);
-
-        thread::sleep(Duration::from_millis(1000));
-
-        println!("{} is done eating.", self.name);
-    }
-}
-
-fn main() {
-    let philosophers = vec![
-        Philosopher::new("Judith Butler"),
-        Philosopher::new("Gilles Deleuze"),
-        Philosopher::new("Karl Marx"),
-        Philosopher::new("Emma Goldman"),
-        Philosopher::new("Michel Foucault"),
-    ];
-
-    let handles: Vec<_> = philosophers.into_iter().map(|p| {
-        thread::spawn(move || {
-            p.eat();
-        })
-    }).collect();
-
-    for h in handles {
-        h.join().unwrap();
-    }
-}
-```
-
-All we’ve done is change the loop in `main()`, and added a second one! Here’s the
-first change:
-
-```rust,ignore
-let handles: Vec<_> = philosophers.into_iter().map(|p| {
-    thread::spawn(move || {
-        p.eat();
-    })
-}).collect();
-```
-
-While this is only five lines, they’re a dense five. Let’s break it down.
-
-```rust,ignore
-let handles: Vec<_> =
-```
-
-We introduce a new binding, called `handles`. We’ve given it this name because
-we are going to make some new threads, and that will return some handles to those
-threads that let us control their operation. We need to explicitly annotate
-the type here, though, due to an issue we’ll talk about later. The `_` is
-a type placeholder. We’re saying “`handles` is a vector of something, but you
-can figure out what that something is, Rust.”
-
-```rust,ignore
-philosophers.into_iter().map(|p| {
-```
-
-We take our list of philosophers and call `into_iter()` on it. This creates an
-iterator that takes ownership of each philosopher. We need to do this to pass
-them to our threads. We take that iterator and call `map` on it, which takes a
-closure as an argument and calls that closure on each element in turn.
-
-```rust,ignore
-    thread::spawn(move || {
-        p.eat();
-    })
-```
-
-Here’s where the concurrency happens. The `thread::spawn` function takes a closure
-as an argument and executes that closure in a new thread. This closure needs
-an extra annotation, `move`, to indicate that the closure is going to take
-ownership of the values it’s capturing. In this case, it's the `p` variable of the
-`map` function.
-
-Inside the thread, all we do is call `eat()` on `p`. Also note that
-the call to `thread::spawn` lacks a trailing semicolon, making this an
-expression. This distinction is important, yielding the correct return
-value. For more details, read [Expressions vs. Statements][es].
-
-[es]: functions.html#expressions-vs-statements
-
-```rust,ignore
-}).collect();
-```
-
-Finally, we take the result of all those `map` calls and collect them up.
-`collect()` will make them into a collection of some kind, which is why we
-needed to annotate the return type: we want a `Vec<T>`. The elements are the
-return values of the `thread::spawn` calls, which are handles to those threads.
-Whew!
-
-```rust,ignore
-for h in handles {
-    h.join().unwrap();
-}
-```
-
-At the end of `main()`, we loop through the handles and call `join()` on them,
-which blocks execution until the thread has completed execution. This ensures
-that the threads complete their work before the program exits.
-
-If you run this program, you’ll see that the philosophers eat out of order!
-We have multi-threading!
-
-```text
-Judith Butler is eating.
-Gilles Deleuze is eating.
-Karl Marx is eating.
-Emma Goldman is eating.
-Michel Foucault is eating.
-Judith Butler is done eating.
-Gilles Deleuze is done eating.
-Karl Marx is done eating.
-Emma Goldman is done eating.
-Michel Foucault is done eating.
-```
-
-But what about the forks? We haven’t modeled them at all yet.
-
-To do that, let’s make a new `struct`:
-
-```rust
-use std::sync::Mutex;
-
-struct Table {
-    forks: Vec<Mutex<()>>,
-}
-```
-
-This `Table` has a vector of `Mutex`es. A mutex is a way to control
-concurrency: only one thread can access the contents at once. This is exactly
-the property we need with our forks. We use an empty tuple, `()`, inside the
-mutex, since we’re not actually going to use the value, just hold onto it.
-
-Let’s modify the program to use the `Table`:
-
-```rust
-use std::thread;
-use std::time::Duration;
-use std::sync::{Mutex, Arc};
-
-struct Philosopher {
-    name: String,
-    left: usize,
-    right: usize,
-}
-
-impl Philosopher {
-    fn new(name: &str, left: usize, right: usize) -> Philosopher {
-        Philosopher {
-            name: name.to_string(),
-            left: left,
-            right: right,
-        }
-    }
-
-    fn eat(&self, table: &Table) {
-        let _left = table.forks[self.left].lock().unwrap();
-        thread::sleep(Duration::from_millis(150));
-        let _right = table.forks[self.right].lock().unwrap();
-
-        println!("{} is eating.", self.name);
-
-        thread::sleep(Duration::from_millis(1000));
-
-        println!("{} is done eating.", self.name);
-    }
-}
-
-struct Table {
-    forks: Vec<Mutex<()>>,
-}
-
-fn main() {
-    let table = Arc::new(Table { forks: vec![
-        Mutex::new(()),
-        Mutex::new(()),
-        Mutex::new(()),
-        Mutex::new(()),
-        Mutex::new(()),
-    ]});
-
-    let philosophers = vec![
-        Philosopher::new("Judith Butler", 0, 1),
-        Philosopher::new("Gilles Deleuze", 1, 2),
-        Philosopher::new("Karl Marx", 2, 3),
-        Philosopher::new("Emma Goldman", 3, 4),
-        Philosopher::new("Michel Foucault", 0, 4),
-    ];
-
-    let handles: Vec<_> = philosophers.into_iter().map(|p| {
-        let table = table.clone();
-
-        thread::spawn(move || {
-            p.eat(&table);
-        })
-    }).collect();
-
-    for h in handles {
-        h.join().unwrap();
-    }
-}
-```
-
-Lots of changes! However, with this iteration, we’ve got a working program.
-Let’s go over the details:
-
-```rust,ignore
-use std::sync::{Mutex, Arc};
-```
-
-We’re going to use another structure from the `std::sync` package: `Arc<T>`.
-We’ll talk more about it when we use it.
-
-```rust,ignore
-struct Philosopher {
-    name: String,
-    left: usize,
-    right: usize,
-}
-```
-
-We need to add two more fields to our `Philosopher`. Each philosopher is going
-to have two forks: the one on their left, and the one on their right.
-We’ll use the `usize` type to indicate them, as it’s the type that you index
-vectors with. These two values will be the indexes into the `forks` our `Table`
-has.
-
-```rust,ignore
-fn new(name: &str, left: usize, right: usize) -> Philosopher {
-    Philosopher {
-        name: name.to_string(),
-        left: left,
-        right: right,
-    }
-}
-```
-
-We now need to construct those `left` and `right` values, so we add them to
-`new()`.
-
-```rust,ignore
-fn eat(&self, table: &Table) {
-    let _left = table.forks[self.left].lock().unwrap();
-    thread::sleep(Duration::from_millis(150));
-    let _right = table.forks[self.right].lock().unwrap();
-
-    println!("{} is eating.", self.name);
-
-    thread::sleep(Duration::from_millis(1000));
-
-    println!("{} is done eating.", self.name);
-}
-```
-
-We have three new lines. We’ve added an argument, `table`. We access the
-`Table`’s list of forks, and then use `self.left` and `self.right` to access
-the fork at that particular index. That gives us access to the `Mutex` at that
-index, and we call `lock()` on it. If the mutex is currently being accessed by
-someone else, we’ll block until it becomes available. We have also a call to
-`thread::sleep` between the moment the first fork is picked and the moment the
-second forked is picked, as the process of picking up the fork is not
-immediate.
-
-The call to `lock()` might fail, and if it does, we want to crash. In this
-case, the error that could happen is that the mutex is [‘poisoned’][poison],
-which is what happens when the thread panics while the lock is held. Since this
-shouldn’t happen, we just use `unwrap()`.
-
-[poison]: ../std/sync/struct.Mutex.html#poisoning
-
-One other odd thing about these lines: we’ve named the results `_left` and
-`_right`. What’s up with that underscore? Well, we aren’t planning on
-_using_ the value inside the lock. We just want to acquire it. As such,
-Rust will warn us that we never use the value. By using the underscore,
-we tell Rust that this is what we intended, and it won’t throw a warning.
-
-What about releasing the lock? Well, that will happen when `_left` and
-`_right` go out of scope, automatically.
-
-```rust,ignore
-    let table = Arc::new(Table { forks: vec![
-        Mutex::new(()),
-        Mutex::new(()),
-        Mutex::new(()),
-        Mutex::new(()),
-        Mutex::new(()),
-    ]});
-```
-
-Next, in `main()`, we make a new `Table` and wrap it in an `Arc<T>`.
-‘arc’ stands for ‘atomic reference count’, and we need that to share
-our `Table` across multiple threads. As we share it, the reference
-count will go up, and when each thread ends, it will go back down.
-
-
-```rust,ignore
-let philosophers = vec![
-    Philosopher::new("Judith Butler", 0, 1),
-    Philosopher::new("Gilles Deleuze", 1, 2),
-    Philosopher::new("Karl Marx", 2, 3),
-    Philosopher::new("Emma Goldman", 3, 4),
-    Philosopher::new("Michel Foucault", 0, 4),
-];
-```
-
-We need to pass in our `left` and `right` values to the constructors for our
-`Philosopher`s. But there’s one more detail here, and it’s _very_ important. If
-you look at the pattern, it’s all consistent until the very end. Monsieur
-Foucault should have `4, 0` as arguments, but instead, has `0, 4`. This is what
-prevents deadlock, actually: one of our philosophers is left handed! This is
-one way to solve the problem, and in my opinion, it’s the simplest. If you
-change the order of the parameters, you will be able to observe the deadlock
-taking place.
-
-```rust,ignore
-let handles: Vec<_> = philosophers.into_iter().map(|p| {
-    let table = table.clone();
-
-    thread::spawn(move || {
-        p.eat(&table);
-    })
-}).collect();
-```
-
-Finally, inside of our `map()`/`collect()` loop, we call `table.clone()`. The
-`clone()` method on `Arc<T>` is what bumps up the reference count, and when it
-goes out of scope, it decrements the count. This is needed so that we know how
-many references to `table` exist across our threads. If we didn’t have a count,
-we wouldn’t know how to deallocate it.
-
-You’ll notice we can introduce a new binding to `table` here, and it will
-shadow the old one. This is often used so that you don’t need to come up with
-two unique names.
-
-With this, our program works! Only two philosophers can eat at any one time,
-and so you’ll get some output like this:
-
-```text
-Gilles Deleuze is eating.
-Emma Goldman is eating.
-Emma Goldman is done eating.
-Gilles Deleuze is done eating.
-Judith Butler is eating.
-Karl Marx is eating.
-Judith Butler is done eating.
-Michel Foucault is eating.
-Karl Marx is done eating.
-Michel Foucault is done eating.
-```
-
-Congrats! You’ve implemented a classic concurrency problem in Rust.
index 0d0fd8cf1d0a641e1c4f526be1f79181532e3f58..4053e5776e39f89982e5e2cacde47d52adb3245f 100644 (file)
@@ -73,7 +73,7 @@ hello.rs:4 }
 ```
 
 This [unfortunate error](https://github.com/rust-lang/rust/issues/22547) is
-correct: documentation comments apply to the thing after them, and there's
+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
@@ -193,7 +193,7 @@ If you want something that's not Rust code, you can add an annotation:
 ```
 
 This will highlight according to whatever language you're showing off.
-If you're just showing plain text, choose `text`.
+If you're only showing plain text, choose `text`.
 
 It's important to choose the correct annotation here, because `rustdoc` uses it
 in an interesting way: It can be used to actually test your examples in a
@@ -273,7 +273,7 @@ be hidden from the output, but will be used when compiling your code. You
 can use this to your advantage. In this case, documentation comments need
 to apply to some kind of function, so if I want to show you just a
 documentation comment, I need to add a little function definition below
-it. At the same time, it's just there to satisfy the compiler, so hiding
+it. At the same time, it's only there to satisfy the compiler, so hiding
 it makes the example more clear. You can use this technique to explain
 longer examples in detail, while still preserving the testability of your
 documentation.
@@ -385,7 +385,7 @@ error handling. Lets say you want the following,
 
 ```rust,ignore
 /// use std::io;
-/// let mut input = String::new(); 
+/// let mut input = String::new();
 /// try!(io::stdin().read_line(&mut input));
 ```
 
@@ -398,7 +398,7 @@ don't return anything so this will give a mismatched types error.
 /// ```
 /// use std::io;
 /// # fn foo() -> io::Result<()> {
-/// let mut input = String::new(); 
+/// let mut input = String::new();
 /// try!(io::stdin().read_line(&mut input));
 /// # Ok(())
 /// # }
@@ -512,7 +512,7 @@ the documentation with comments. For example:
 # fn foo() {}
 ```
 
-is just
+is:
 
 ~~~markdown
 # Examples
index 582ed3b7e65c56a256c60f8a76e0fc3629328f5d..65873c80e55e4364e13ac2107463088b2760d673 100644 (file)
@@ -3,6 +3,6 @@
 So you’ve learned how to write some Rust code. But there’s a difference between
 writing *any* Rust code and writing *good* Rust code.
 
-This section consists of relatively independent tutorials which show you how to
+This chapter consists of relatively independent tutorials which show you how to
 take your Rust to the next level. Common patterns and standard library features
 will be introduced. Read these sections in any order of your choosing.
index 8ad4eeedd187f46585a55e70935236c423067ef9..5e05b4ebbdfa9a004c40c24aff106937fbf06da1 100644 (file)
@@ -1,7 +1,8 @@
 % Enums
 
-An `enum` in Rust is a type that represents data that could be one of
-several possible variants:
+An `enum` in Rust is a type that represents data that is one of
+several possible variants. Each variant in the `enum` can optionally
+have data associated with it:
 
 ```rust
 enum Message {
@@ -12,9 +13,8 @@ enum Message {
 }
 ```
 
-Each variant can optionally have data associated with it. The syntax for
-defining variants resembles the syntaxes used to define structs: you can
-have variants with no data (like unit-like structs), variants with named
+The syntax for defining variants resembles the syntaxes used to define structs:
+you can have variants with no data (like unit-like structs), variants with named
 data, and variants with unnamed data (like tuple structs). Unlike
 separate struct definitions, however, an `enum` is a single type. A
 value of the enum can match any of the variants. For this reason, an
@@ -41,7 +41,7 @@ let y: BoardGameTurn = BoardGameTurn::Move { squares: 1 };
 Both variants are named `Move`, but since they’re scoped to the name of
 the enum, they can both be used without conflict.
 
-A value of an enum type contains information about which variant it is,
+A value of an `enum` type contains information about which variant it is,
 in addition to any data associated with that variant. This is sometimes
 referred to as a ‘tagged union’, since the data includes a ‘tag’
 indicating what type it is. The compiler uses this information to
@@ -62,12 +62,11 @@ learn in the next section. We don’t know enough about Rust to implement
 equality yet, but we’ll find out in the [`traits`][traits] section.
 
 [match]: match.html
-[if-let]: if-let.html
 [traits]: traits.html
 
 # Constructors as functions
 
-An enum’s constructors can also be used like functions. For example:
+An `enum` constructor can also be used like a function. For example:
 
 ```rust
 # enum Message {
@@ -76,7 +75,7 @@ An enum’s constructors can also be used like functions. For example:
 let m = Message::Write("Hello, world".to_string());
 ```
 
-Is the same as
+is the same as
 
 ```rust
 # enum Message {
index 57389bdc493cb731a092e3ba6a1b211d4e57cccc..9b1d16170b97f1c879e6c6c85169f8f6f807d761 100644 (file)
@@ -5,18 +5,18 @@ errors in a particular way. Generally speaking, error handling is divided into
 two broad categories: exceptions and return values. Rust opts for return
 values.
 
-In this chapter, we intend to provide a comprehensive treatment of how to deal
+In this section, we intend to provide a comprehensive treatment of how to deal
 with errors in Rust. More than that, we will attempt to introduce error handling
 one piece at a time so that you'll come away with a solid working knowledge of
 how everything fits together.
 
 When done naïvely, error handling in Rust can be verbose and annoying. This
-chapter will explore those stumbling blocks and demonstrate how to use the
+section will explore those stumbling blocks and demonstrate how to use the
 standard library to make error handling concise and ergonomic.
 
 # Table of Contents
 
-This chapter is very long, mostly because we start at the very beginning with
+This section is very long, mostly because we start at the very beginning with
 sum types and combinators, and try to motivate the way Rust does error handling
 incrementally. As such, programmers with experience in other expressive type
 systems may want to jump around.
@@ -117,8 +117,8 @@ the first example. This is because the
 panic is embedded in the calls to `unwrap`.
 
 To “unwrap” something in Rust is to say, “Give me the result of the
-computation, and if there was an error, just panic and stop the program.”
-It would be better if we just showed the code for unwrapping because it is so
+computation, and if there was an error, panic and stop the program.”
+It would be better if we showed the code for unwrapping because it is so
 simple, but to do that, we will first need to explore the `Option` and `Result`
 types. Both of these types have a method called `unwrap` defined on them.
 
@@ -154,7 +154,7 @@ fn find(haystack: &str, needle: char) -> Option<usize> {
 }
 ```
 
-Notice that when this function finds a matching character, it doesn't just
+Notice that when this function finds a matching character, it doesn't only
 return the `offset`. Instead, it returns `Some(offset)`. `Some` is a variant or
 a *value constructor* for the `Option` type. You can think of it as a function
 with the type `fn<T>(value: T) -> Option<T>`. Correspondingly, `None` is also a
@@ -182,7 +182,7 @@ analysis is the only way to get at the value stored inside an `Option<T>`. This
 means that you, as the programmer, must handle the case when an `Option<T>` is
 `None` instead of `Some(t)`.
 
-But wait, what about `unwrap`,which we used [`previously`](#code-unwrap-double)?
+But wait, what about `unwrap`, which we used [previously](#code-unwrap-double)?
 There was no case analysis there! Instead, the case analysis was put inside the
 `unwrap` method for you. You could define it yourself if you want:
 
@@ -216,7 +216,7 @@ we saw how to use `find` to discover the extension in a file name. Of course,
 not all file names have a `.` in them, so it's possible that the file name has
 no extension. This *possibility of absence* is encoded into the types using
 `Option<T>`. In other words, the compiler will force us to address the
-possibility that an extension does not exist. In our case, we just print out a
+possibility that an extension does not exist. In our case, we only print out a
 message saying as such.
 
 Getting the extension of a file name is a pretty common operation, so it makes
@@ -248,7 +248,7 @@ tiresome.
 
 In fact, the case analysis in `extension_explicit` follows a very common
 pattern: *map* a function on to the value inside of an `Option<T>`, unless the
-option is `None`, in which case, just return `None`.
+option is `None`, in which case, return `None`.
 
 Rust has parametric polymorphism, so it is very easy to define a combinator
 that abstracts this pattern:
@@ -350,7 +350,7 @@ fn file_name(file_path: &str) -> Option<&str> {
 }
 ```
 
-You might think that we could just use the `map` combinator to reduce the case
+You might think that we could use the `map` combinator to reduce the case
 analysis, but its type doesn't quite fit. Namely, `map` takes a function that
 does something only with the inner value. The result of that function is then
 *always* [rewrapped with `Some`](#code-option-map). Instead, we need something
@@ -636,7 +636,7 @@ Thus far, we've looked at error handling where everything was either an
 `Option` and a `Result`? Or what if you have a `Result<T, Error1>` and a
 `Result<T, Error2>`? Handling *composition of distinct error types* is the next
 challenge in front of us, and it will be the major theme throughout the rest of
-this chapter.
+this section.
 
 ## Composing `Option` and `Result`
 
@@ -648,7 +648,7 @@ Of course, in real code, things aren't always as clean. Sometimes you have a
 mix of `Option` and `Result` types. Must we resort to explicit case analysis,
 or can we continue using combinators?
 
-For now, let's revisit one of the first examples in this chapter:
+For now, let's revisit one of the first examples in this section:
 
 ```rust,should_panic
 use std::env;
@@ -670,7 +670,7 @@ The tricky aspect here is that `argv.nth(1)` produces an `Option` while
 with both an `Option` and a `Result`, the solution is *usually* to convert the
 `Option` to a `Result`. In our case, the absence of a command line parameter
 (from `env::args()`) means the user didn't invoke the program correctly. We
-could just use a `String` to describe the error. Let's try:
+could use a `String` to describe the error. Let's try:
 
 <span id="code-error-double-string"></span>
 
@@ -709,7 +709,7 @@ fn ok_or<T, E>(option: Option<T>, err: E) -> Result<T, E> {
 
 The other new combinator used here is
 [`Result::map_err`](../std/result/enum.Result.html#method.map_err).
-This is just like `Result::map`, except it maps a function on to the *error*
+This is like `Result::map`, except it maps a function on to the *error*
 portion of a `Result` value. If the `Result` is an `Ok(...)` value, then it is
 returned unmodified.
 
@@ -841,7 +841,7 @@ example, the very last call to `map` multiplies the `Ok(...)` value (which is
 an `i32`) by `2`. If an error had occurred before that point, this operation
 would have been skipped because of how `map` is defined.
 
-`map_err` is the trick that makes all of this work. `map_err` is just like
+`map_err` is the trick that makes all of this work. `map_err` is like
 `map`, except it applies a function to the `Err(...)` value of a `Result`. In
 this case, we want to convert all of our errors to one type: `String`. Since
 both `io::Error` and `num::ParseIntError` implement `ToString`, we can call the
@@ -887,7 +887,7 @@ fn main() {
 }
 ```
 
-Reasonable people can disagree over whether this code is better that the code
+Reasonable people can disagree over whether this code is better than the code
 that uses combinators, but if you aren't familiar with the combinator approach,
 this code looks simpler to read to me. It uses explicit case analysis with
 `match` and `if let`. If an error occurs, it simply stops executing the
@@ -901,7 +901,7 @@ reduce explicit case analysis. Combinators aren't the only way.
 ## The `try!` macro
 
 A cornerstone of error handling in Rust is the `try!` macro. The `try!` macro
-abstracts case analysis just like combinators, but unlike combinators, it also
+abstracts case analysis like combinators, but unlike combinators, it also
 abstracts *control flow*. Namely, it can abstract the *early return* pattern
 seen above.
 
@@ -1319,7 +1319,7 @@ and [`cause`](../std/error/trait.Error.html#method.cause), but the
 limitation remains: `Box<Error>` is opaque. (N.B. This isn't entirely
 true because Rust does have runtime reflection, which is useful in
 some scenarios that are [beyond the scope of this
-chapter](https://crates.io/crates/error).)
+section](https://crates.io/crates/error).)
 
 It's time to revisit our custom `CliError` type and tie everything together.
 
@@ -1461,7 +1461,7 @@ expose its representation (like
 [`ErrorKind`](../std/io/enum.ErrorKind.html)) or keep it hidden (like
 [`ParseIntError`](../std/num/struct.ParseIntError.html)). Regardless
 of how you do it, it's usually good practice to at least provide some
-information about the error beyond just its `String`
+information about the error beyond its `String`
 representation. But certainly, this will vary depending on use cases.
 
 At a minimum, you should probably implement the
@@ -1486,7 +1486,7 @@ and [`fmt::Result`](../std/fmt/type.Result.html).
 
 # Case study: A program to read population data
 
-This chapter was long, and depending on your background, it might be
+This section was long, and depending on your background, it might be
 rather dense. While there is plenty of example code to go along with
 the prose, most of it was specifically designed to be pedagogical. So,
 we're going to do something new: a case study.
@@ -1499,7 +1499,7 @@ that can go wrong!
 The data we'll be using comes from the [Data Science
 Toolkit][11]. I've prepared some data from it for this exercise. You
 can either grab the [world population data][12] (41MB gzip compressed,
-145MB uncompressed) or just the [US population data][13] (2.2MB gzip
+145MB uncompressed) or only the [US population data][13] (2.2MB gzip
 compressed, 7.2MB uncompressed).
 
 Up until now, we've kept the code limited to Rust's standard library. For a real
@@ -1512,7 +1512,7 @@ and [`rustc-serialize`](https://crates.io/crates/rustc-serialize) crates.
 
 We're not going to spend a lot of time on setting up a project with
 Cargo because it is already covered well in [the Cargo
-chapter](../book/hello-cargo.html) and [Cargo's documentation][14].
+section](../book/hello-cargo.html) and [Cargo's documentation][14].
 
 To get started from scratch, run `cargo new --bin city-pop` and make sure your
 `Cargo.toml` looks something like this:
@@ -1573,11 +1573,11 @@ fn main() {
 
     let matches = match opts.parse(&args[1..]) {
         Ok(m)  => { m }
-       Err(e) => { panic!(e.to_string()) }
+        Err(e) => { panic!(e.to_string()) }
     };
     if matches.opt_present("h") {
         print_usage(&program, opts);
-       return;
+        return;
     }
     let data_path = args[1].clone();
     let city = args[2].clone();
@@ -1613,6 +1613,9 @@ CSV data given to us and print out a field in matching rows. Let's do it. (Make
 sure to add `extern crate csv;` to the top of your file.)
 
 ```rust,ignore
+use std::fs::File;
+use std::path::Path;
+
 // This struct represents the data in each row of the CSV file.
 // Type based decoding absolves us of a lot of the nitty gritty error
 // handling, like parsing strings as integers or floats.
@@ -1656,7 +1659,7 @@ fn main() {
        let data_path = Path::new(&data_file);
        let city = args[2].clone();
 
-       let file = fs::File::open(data_path).unwrap();
+       let file = File::open(data_path).unwrap();
        let mut rdr = csv::Reader::from_reader(file);
 
        for row in rdr.decode::<Row>() {
@@ -1674,7 +1677,7 @@ fn main() {
 Let's outline the errors. We can start with the obvious: the three places that
 `unwrap` is called:
 
-1. [`fs::File::open`](../std/fs/struct.File.html#method.open)
+1. [`File::open`](../std/fs/struct.File.html#method.open)
    can return an
    [`io::Error`](../std/io/struct.Error.html).
 2. [`csv::Reader::decode`](http://burntsushi.net/rustdoc/csv/struct.Reader.html#method.decode)
@@ -1703,7 +1706,7 @@ compiler can no longer reason about its underlying type.
 
 [Previously](#the-limits-of-combinators) we started refactoring our code by
 changing the type of our function from `T` to `Result<T, OurErrorType>`. In
-this case, `OurErrorType` is just `Box<Error>`. But what's `T`? And can we add
+this case, `OurErrorType` is only `Box<Error>`. But what's `T`? And can we add
 a return type to `main`?
 
 The answer to the second question is no, we can't. That means we'll need to
@@ -1734,7 +1737,7 @@ fn print_usage(program: &str, opts: Options) {
 
 fn search<P: AsRef<Path>>(file_path: P, city: &str) -> Vec<PopulationCount> {
     let mut found = vec![];
-    let file = fs::File::open(file_path).unwrap();
+    let file = File::open(file_path).unwrap();
     let mut rdr = csv::Reader::from_reader(file);
     for row in rdr.decode::<Row>() {
         let row = row.unwrap();
@@ -1792,11 +1795,15 @@ To convert this to proper error handling, we need to do the following:
 Let's try it:
 
 ```rust,ignore
+use std::error::Error;
+
+// The rest of the code before this is unchanged
+
 fn search<P: AsRef<Path>>
          (file_path: P, city: &str)
          -> Result<Vec<PopulationCount>, Box<Error+Send+Sync>> {
     let mut found = vec![];
-    let file = try!(fs::File::open(file_path));
+    let file = try!(File::open(file_path));
     let mut rdr = csv::Reader::from_reader(file);
     for row in rdr.decode::<Row>() {
         let row = try!(row);
@@ -1900,8 +1907,13 @@ let city = if !matches.free.is_empty() {
        return;
 };
 
-for pop in search(&data_file, &city) {
-       println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+match search(&data_file, &city) {
+    Ok(pops) => {
+        for pop in pops {
+            println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
+        }
+    }
+    Err(err) => println!("{}", err)
 }
 ...
 ```
@@ -1921,16 +1933,20 @@ parser out of
 But how can we use the same code over both types? There's actually a
 couple ways we could go about this. One way is to write `search` such
 that it is generic on some type parameter `R` that satisfies
-`io::Read`. Another way is to just use trait objects:
+`io::Read`. Another way is to use trait objects:
 
 ```rust,ignore
+use std::io;
+
+// The rest of the code before this is unchanged
+
 fn search<P: AsRef<Path>>
          (file_path: &Option<P>, city: &str)
          -> Result<Vec<PopulationCount>, Box<Error+Send+Sync>> {
     let mut found = vec![];
     let input: Box<io::Read> = match *file_path {
         None => Box::new(io::stdin()),
-        Some(ref file_path) => Box::new(try!(fs::File::open(file_path))),
+        Some(ref file_path) => Box::new(try!(File::open(file_path))),
     };
     let mut rdr = csv::Reader::from_reader(input);
     // The rest remains unchanged!
@@ -2017,7 +2033,7 @@ fn search<P: AsRef<Path>>
     let mut found = vec![];
     let input: Box<io::Read> = match *file_path {
         None => Box::new(io::stdin()),
-        Some(ref file_path) => Box::new(try!(fs::File::open(file_path))),
+        Some(ref file_path) => Box::new(try!(File::open(file_path))),
     };
     let mut rdr = csv::Reader::from_reader(input);
     for row in rdr.decode::<Row>() {
@@ -2078,7 +2094,7 @@ opts.optflag("q", "quiet", "Silences errors and warnings.");
 ...
 ```
 
-Now we just need to implement our “quiet” functionality. This requires us to
+Now we only need to implement our “quiet” functionality. This requires us to
 tweak the case analysis in `main`:
 
 ```rust,ignore
@@ -2105,13 +2121,13 @@ handling.
 
 # The Short Story
 
-Since this chapter is long, it is useful to have a quick summary for error
+Since this section is long, it is useful to have a quick summary for error
 handling in Rust. These are some good “rules of thumb." They are emphatically
 *not* commandments. There are probably good reasons to break every one of these
 heuristics!
 
 * If you're writing short example code that would be overburdened by error
-  handling, it's probably just fine to use `unwrap` (whether that's
+  handling, it's probably fine to use `unwrap` (whether that's
   [`Result::unwrap`](../std/result/enum.Result.html#method.unwrap),
   [`Option::unwrap`](../std/option/enum.Option.html#method.unwrap)
   or preferably
index c3896e4e9c55737ad35d2b1d24046c34f54bcb61..6aec8d2a048aa8e95bdbcb84d91f6b3f4ef57209 100644 (file)
@@ -367,7 +367,7 @@ artifact.
 A few examples of how this model can be used are:
 
 * A native build dependency. Sometimes some C/C++ glue is needed when writing
-  some Rust code, but distribution of the C/C++ code in a library format is just
+  some Rust code, but distribution of the C/C++ code in a library format is
   a burden. In this case, the code will be archived into `libfoo.a` and then the
   Rust crate would declare a dependency via `#[link(name = "foo", kind =
   "static")]`.
@@ -478,6 +478,8 @@ are:
 * `aapcs`
 * `cdecl`
 * `fastcall`
+* `vectorcall`
+This is currently hidden behind the `abi_vectorcall` gate and is subject to change.
 * `Rust`
 * `rust-intrinsic`
 * `system`
@@ -490,7 +492,7 @@ interoperating with the target's libraries. For example, on win32 with a x86
 architecture, this means that the abi used would be `stdcall`. On x86_64,
 however, windows uses the `C` calling convention, so `C` would be used. This
 means that in our previous example, we could have used `extern "system" { ... }`
-to define a block for all windows systems, not just x86 ones.
+to define a block for all windows systems, not only x86 ones.
 
 # Interoperability with foreign code
 
index 84cea5dabc3b528edacdcb1be014dbe821e8d235..be905599c64415684110f730bd1e4e3ec0149646 100644 (file)
@@ -124,7 +124,7 @@ statement `x + 1;` doesn’t return a value. There are two kinds of statements i
 Rust: ‘declaration statements’ and ‘expression statements’. Everything else is
 an expression. Let’s talk about declaration statements first.
 
-In some languages, variable bindings can be written as expressions, not just
+In some languages, variable bindings can be written as expressions, not
 statements. Like Ruby:
 
 ```ruby
@@ -145,7 +145,7 @@ Note that assigning to an already-bound variable (e.g. `y = 5`) is still an
 expression, although its value is not particularly useful. Unlike other
 languages where an assignment evaluates to the assigned value (e.g. `5` in the
 previous example), in Rust the value of an assignment is an empty tuple `()`
-because the assigned value can have [just one owner](ownership.html), and any
+because the assigned value can have [only one owner](ownership.html), and any
 other returned value would be too surprising:
 
 ```rust
index 347c1f5757c62e041740557d5535da9e37ead042..9ab601419cd7cc1e08ce130232aed9f923aff89b 100644 (file)
@@ -37,7 +37,7 @@ let x: Option<f64> = Some(5);
 // found `core::option::Option<_>` (expected f64 but found integral variable)
 ```
 
-That doesn’t mean we can’t make `Option<T>`s that hold an `f64`! They just have
+That doesn’t mean we can’t make `Option<T>`s that hold an `f64`! They have
 to match up:
 
 ```rust
@@ -118,7 +118,7 @@ let float_origin = Point { x: 0.0, y: 0.0 };
 Similar to functions, the `<T>` is where we declare the generic parameters,
 and we then use `x: T` in the type declaration, too.
 
-When you want to add an implementation for the generic `struct`, you just
+When you want to add an implementation for the generic `struct`, you
 declare the type parameter after the `impl`:
 
 ```rust
index 5b1040dd05ee19dbc0b9c267cfaa91e8b28a04b6..e9d271e7537685b299cc710ecf8ed9fc1c5460b1 100644 (file)
@@ -1,13 +1,13 @@
 % Getting Started
 
-This first section of the book will get us going with Rust and its tooling.
+This first chapter of the book will get us going with Rust and its tooling.
 First, we’ll install Rust. Then, the classic ‘Hello World’ program. Finally,
 we’ll talk about Cargo, Rust’s build system and package manager.
 
 # Installing Rust
 
 The first step to using Rust is to install it. Generally speaking, you’ll need
-an Internet connection to run the commands in this chapter, as we’ll be
+an Internet connection to run the commands in this section, as we’ll be
 downloading Rust from the internet.
 
 We’ll be showing off a number of commands using a terminal, and those lines all
@@ -63,6 +63,13 @@ these platforms are required to have each of the following:
 |  Target                       | std |rustc|cargo| notes                      |
 |-------------------------------|-----|-----|-----|----------------------------|
 | `i686-pc-windows-msvc`        |  ✓  |  ✓  |  ✓  | 32-bit MSVC (Windows 7+)   |
+| `x86_64-unknown-linux-musl`   |  ✓  |     |     | 64-bit Linux with MUSL     |
+| `arm-linux-androideabi`       |  ✓  |     |     | ARM Android                |
+| `arm-unknown-linux-gnueabi`   |  ✓  |  ✓  |     | ARM Linux (2.6.18+)        |
+| `arm-unknown-linux-gnueabihf` |  ✓  |  ✓  |     | ARM Linux (2.6.18+)        |
+| `aarch64-unknown-linux-gnu`   |  ✓  |     |     | ARM64 Linux (2.6.18+)      |
+| `mips-unknown-linux-gnu`      |  ✓  |     |     | MIPS Linux (2.6.18+)       |
+| `mipsel-unknown-linux-gnu`    |  ✓  |     |     | MIPS (LE) Linux (2.6.18+)  |
 
 ### Tier 3
 
@@ -75,15 +82,8 @@ unofficial locations.
 
 |  Target                       | std |rustc|cargo| notes                      |
 |-------------------------------|-----|-----|-----|----------------------------|
-| `x86_64-unknown-linux-musl`   |  ✓  |     |     | 64-bit Linux with MUSL     |
-| `arm-linux-androideabi`       |  ✓  |     |     | ARM Android                |
 | `i686-linux-android`          |  ✓  |     |     | 32-bit x86 Android         |
 | `aarch64-linux-android`       |  ✓  |     |     | ARM64 Android              |
-| `arm-unknown-linux-gnueabi`   |  ✓  |  ✓  |     | ARM Linux (2.6.18+)        |
-| `arm-unknown-linux-gnueabihf` |  ✓  |  ✓  |     | ARM Linux (2.6.18+)        |
-| `aarch64-unknown-linux-gnu`   |  ✓  |     |     | ARM64 Linux (2.6.18+)      |
-| `mips-unknown-linux-gnu`      |  ✓  |     |     | MIPS Linux (2.6.18+)       |
-| `mipsel-unknown-linux-gnu`    |  ✓  |     |     | MIPS (LE) Linux (2.6.18+)  |
 | `powerpc-unknown-linux-gnu`   |  ✓  |     |     | PowerPC Linux (2.6.18+)    |
 | `i386-apple-ios`              |  ✓  |     |     | 32-bit x86 iOS             |
 | `x86_64-apple-ios`            |  ✓  |     |     | 64-bit x86 iOS             |
@@ -127,7 +127,7 @@ not want the script to run ‘sudo’ then pass it the --disable-sudo flag.
 You may uninstall later by running /usr/local/lib/rustlib/uninstall.sh,
 or by running this script again with the --uninstall flag.
 
-Continue? (y/N) 
+Continue? (y/N)
 ```
 
 From here, press `y` for ‘yes’, and then follow the rest of the prompts.
@@ -140,7 +140,7 @@ If you're on Windows, please download the appropriate [installer][install-page].
 
 ## Uninstalling
 
-Uninstalling Rust is as easy as installing it. On Linux or Mac, just run
+Uninstalling Rust is as easy as installing it. On Linux or Mac, run
 the uninstall script:
 
 ```bash
@@ -188,11 +188,11 @@ was installed.
 Now that you have Rust installed, we'll help you write your first Rust program.
 It's traditional when learning a new language to write a little program to
 print the text “Hello, world!” to the screen, and in this section, we'll follow
-that tradition. 
+that tradition.
 
 The nice thing about starting with such a simple program is that you can
 quickly verify that your compiler is installed, and that it's working properly.
-Printing information to the screen is also just a pretty common thing to do, so
+Printing information to the screen is also a pretty common thing to do, so
 practicing it early on is good.
 
 > Note: This book assumes basic familiarity with the command line. Rust itself
@@ -202,7 +202,7 @@ practicing it early on is good.
 > There are a number of extensions in development by the community, and the
 > Rust team ships plugins for [various editors]. Configuring your editor or
 > IDE is out of the scope of this tutorial, so check the documentation for your
-> specific setup. 
+> specific setup.
 
 [SolidOak]: https://github.com/oakes/SolidOak
 [various editors]: https://github.com/rust-lang/rust/blob/master/src/etc/CONFIGS.md
@@ -244,14 +244,14 @@ following commands:
 
 ```bash
 $ rustc main.rs
-$ ./main 
+$ ./main
 Hello, world!
 ```
 
-In Windows, just replace `main` with `main.exe`. Regardless of your operating
+In Windows, replace `main` with `main.exe`. Regardless of your operating
 system, you should see the string `Hello, world!` print to the terminal. If you
 did, then congratulations! You've officially written a Rust program. That makes
-you a Rust programmer! Welcome. 
+you a Rust programmer! Welcome.
 
 ## Anatomy of a Rust Program
 
@@ -285,13 +285,13 @@ Inside the `main()` function:
 This line does all of the work in this little program: it prints text to the
 screen. There are a number of details that are important here. The first is
 that it’s indented with four spaces, not tabs.
+
 The second important part is the `println!()` line. This is calling a Rust
 *[macro]*, which is how metaprogramming is done in Rust. If it were calling a
 function instead, it would look like this: `println()` (without the !). We'll
-discuss Rust macros in more detail later, but for now you just need to
+discuss Rust macros in more detail later, but for now you only need to
 know that when you see a `!` that means that you’re calling a macro instead of
-a normal function. 
+a normal function.
 
 
 [macro]: macros.html
@@ -303,17 +303,17 @@ prints the string to the screen. Easy enough!
 
 [statically allocated]: the-stack-and-the-heap.html
 
-The line ends with a semicolon (`;`). Rust is an *[expression oriented]*
-language, which means that most things are expressions, rather than statements.
-The `;` indicates that this expression is over, and the next one is ready to
-begin. Most lines of Rust code end with a `;`.
+The line ends with a semicolon (`;`). Rust is an *[expression-oriented
+language]*, which means that most things are expressions, rather than
+statements. The `;` indicates that this expression is over, and the next one is
+ready to begin. Most lines of Rust code end with a `;`.
 
 [expression-oriented language]: glossary.html#expression-oriented-language
 
 ## Compiling and Running Are Separate Steps
 
 In "Writing and Running a Rust Program", we showed you how to run a newly
-created program. We'll break that process down and examine each step now. 
+created program. We'll break that process down and examine each step now.
 
 Before running a Rust program, you have to compile it. You can use the Rust
 compiler by entering the `rustc` command and passing it the name of your source
@@ -395,7 +395,7 @@ in which you installed Rust, to determine if Cargo is separate.
 ## Converting to Cargo
 
 Let’s convert the Hello World program to Cargo. To Cargo-fy a project, you need
-to do three things: 
+to do three things:
 
 1. Put your source file in the right directory.
 2. Get rid of the old executable (`main.exe` on Windows, `main` everywhere else)
@@ -419,7 +419,7 @@ Cargo expects your source files to live inside a *src* directory, so do that
 first. This leaves the top-level project directory (in this case,
 *hello_world*) for READMEs, license information, and anything else not related
 to your code. In this way, using Cargo helps you keep your projects nice and
-tidy. There's a place for everything, and everything is in its place. 
+tidy. There's a place for everything, and everything is in its place.
 
 Now, copy *main.rs* to the *src* directory, and delete the compiled file you
 created with `rustc`. As usual, replace `main` with `main.exe` if you're on
@@ -428,7 +428,7 @@ Windows.
 This example retains `main.rs` as the source filename because it's creating an
 executable. If you wanted to make a library instead, you'd name the file
 `lib.rs`. This convention is used by Cargo to successfully compile your
-projects, but it can be overridden if you wish. 
+projects, but it can be overridden if you wish.
 
 ### Creating a Configuration File
 
@@ -436,7 +436,7 @@ Next, create a new file inside your *hello_world* directory, and call it
 `Cargo.toml`.
 
 Make sure to capitalize the `C` in `Cargo.toml`, or Cargo won't know what to do
-with the configuration file. 
+with the configuration file.
 
 This file is in the *[TOML]* (Tom's Obvious, Minimal Language) format. TOML is
 similar to INI, but has some extra goodies, and is used as Cargo’s
@@ -456,7 +456,7 @@ authors = [ "Your name <you@example.com>" ]
 
 The first line, `[package]`, indicates that the following statements are
 configuring a package. As we add more information to this file, we’ll add other
-sections, but for now, we just have the package configuration.
+sections, but for now, we only have the package configuration.
 
 The other three lines set the three bits of configuration that Cargo needs to
 know to compile your program: its name, what version it is, and who wrote it.
@@ -464,7 +464,7 @@ know to compile your program: its name, what version it is, and who wrote it.
 Once you've added this information to the *Cargo.toml* file, save it to finish
 creating the configuration file.
 
-## Building and Running a Cargo Project 
+## Building and Running a Cargo Project
 
 With your *Cargo.toml* file in place in your project's root directory, you
 should be ready to build and run your Hello World program! To do so, enter the
@@ -477,7 +477,7 @@ $ ./target/debug/hello_world
 Hello, world!
 ```
 
-Bam! If all goes well, `Hello, world!` should print to the terminal once more. 
+Bam! If all goes well, `Hello, world!` should print to the terminal once more.
 
 You just built a project with `cargo build` and ran it with
 `./target/debug/hello_world`, but you can actually do both in one step with
@@ -505,9 +505,11 @@ Cargo checks to see if any of your project’s files have been modified, and onl
 rebuilds your project if they’ve changed since the last time you built it.
 
 With simple projects, Cargo doesn't bring a whole lot over just using `rustc`,
-but it will become useful in future. With complex projects composed of multiple
-crates, it’s much easier to let Cargo coordinate the build. With Cargo, you can
-just run `cargo build`, and it should work the right way.
+but it will become useful in future. This is especially true when you start
+using crates; these are synonymous with a ‘library’ or ‘package’ in other
+programming languages. For complex projects composed of multiple crates, it’s
+much easier to let Cargo coordinate the build. Using Cargo, you can run `cargo
+build`, and it should work the right way.
 
 ## Building for Release
 
@@ -532,7 +534,7 @@ doesn't have dependencies, so the file is a bit sparse. Realistically, you
 won't ever need to touch this file yourself; just let Cargo handle it.
 
 That’s it! If you've been following along, you should have successfully built
-`hello_world` with Cargo. 
+`hello_world` with Cargo.
 
 Even though the project is simple, it now uses much of the real tooling you’ll
 use for the rest of your Rust career. In fact, you can expect to start
@@ -587,7 +589,7 @@ fn main() {
 }
 ```
 
-Cargo has generated a "Hello World!" for you, and you’re ready to start coding! 
+Cargo has generated a "Hello World!" for you, and you’re ready to start coding!
 
 > Note: If you want to look at Cargo in more detail, check out the official [Cargo
 guide], which covers all of its features.
@@ -598,7 +600,7 @@ guide], which covers all of its features.
 
 This chapter covered the basics that will serve you well through the rest of
 this book, and the rest of your time with Rust. Now that you’ve got the tools
-down, we'll cover more about the Rust language itself. 
+down, we'll cover more about the Rust language itself.
 
 You have two options: Dive into a project with ‘[Learn Rust][learnrust]’, or
 start from the bottom and work your way up with ‘[Syntax and
index 8cb00f26ba526fe0da95613a0b49751b71ca31aa..2e315333565c743a375e48eb59bdae8737b22c59 100644 (file)
@@ -1,10 +1,14 @@
 % Guessing Game
 
-For our first project, we’ll implement a classic beginner programming problem:
-the guessing game. Here’s how it works: Our program will generate a random
-integer between one and a hundred. It will then prompt us to enter a guess.
-Upon entering our guess, it will tell us if we’re too low or too high. Once we
-guess correctly, it will congratulate us. Sounds good?
+Let’s learn some Rust! For our first project, we’ll implement a classic
+beginner programming problem: the guessing game. Here’s how it works: Our
+program will generate a random integer between one and a hundred. It will then
+prompt us to enter a guess. Upon entering our guess, it will tell us if we’re
+too low or too high. Once we guess correctly, it will congratulate us. Sounds
+good?
+
+Along the way, we’ll learn a little bit about Rust. The next chapter, ‘Syntax
+and Semantics’, will dive deeper into each part.
 
 # Set up
 
@@ -64,7 +68,7 @@ Hello, world!
 ```
 
 Great! The `run` command comes in handy when you need to rapidly iterate on a
-project. Our game is just such a project, we need to quickly test each
+project. Our game is such a project, we need to quickly test each
 iteration before moving on to the next one.
 
 # Processing a Guess
@@ -290,12 +294,12 @@ src/main.rs:10     io::stdin().read_line(&mut guess);
 Rust warns us that we haven’t used the `Result` value. This warning comes from
 a special annotation that `io::Result` has. Rust is trying to tell you that
 you haven’t handled a possible error. The right way to suppress the error is
-to actually write error handling. Luckily, if we just want to crash if there’s
+to actually write error handling. Luckily, if we want to crash if there’s
 a problem, we can use these two little methods. If we can recover from the
 error somehow, we’d do something else, but we’ll save that for a future
 project.
 
-There’s just one line of this first example left:
+There’s only one line of this first example left:
 
 ```rust,ignore
     println!("You guessed: {}", guess);
@@ -404,7 +408,7 @@ $ cargo build
 That’s right, no output! Cargo knows that our project has been built, and that
 all of its dependencies are built, and so there’s no reason to do all that
 stuff. With nothing to do, it simply exits. If we open up `src/main.rs` again,
-make a trivial change, and then save it again, we’ll just see one line:
+make a trivial change, and then save it again, we’ll only see one line:
 
 ```bash
 $ cargo build
@@ -500,7 +504,7 @@ so we need `1` and `101` to get a number ranging from one to a hundred.
 
 [concurrency]: concurrency.html
 
-The second line just prints out the secret number. This is useful while
+The second line prints out the secret number. This is useful while
 we’re developing our program, so we can easily test it out. But we’ll be
 deleting it for the final version. It’s not much of a game if it prints out
 the answer when you start it up!
@@ -701,7 +705,7 @@ input in it. The `trim()` method on `String`s will eliminate any white space at
 the beginning and end of our string. This is important, as we had to press the
 ‘return’ key to satisfy `read_line()`. This means that if we type `5` and hit
 return, `guess` looks like this: `5\n`. The `\n` represents ‘newline’, the
-enter key. `trim()` gets rid of this, leaving our string with just the `5`. The
+enter key. `trim()` gets rid of this, leaving our string with only the `5`. The
 [`parse()` method on strings][parse] parses a string into some kind of number.
 Since it can parse a variety of numbers, we need to give Rust a hint as to the
 exact type of number we want. Hence, `let guess: u32`. The colon (`:`) after
@@ -849,8 +853,8 @@ fn main() {
 
 By adding the `break` line after the `You win!`, we’ll exit the loop when we
 win. Exiting the loop also means exiting the program, since it’s the last
-thing in `main()`. We have just one more tweak to make: when someone inputs a
-non-number, we don’t want to quit, we just want to ignore it. We can do that
+thing in `main()`. We have only one more tweak to make: when someone inputs a
+non-number, we don’t want to quit, we want to ignore it. We can do that
 like this:
 
 ```rust,ignore
@@ -904,12 +908,12 @@ let guess: u32 = match guess.trim().parse() {
 ```
 
 This is how you generally move from ‘crash on error’ to ‘actually handle the
-returned by `parse()` is an `enum` just like `Ordering`, but in this case, each
+returned by `parse()` is an `enum`  like `Ordering`, but in this case, each
 variant has some data associated with it: `Ok` is a success, and `Err` is a
 failure. Each contains more information: the successfully parsed integer, or an
 error type. In this case, we `match` on `Ok(num)`, which sets the inner value
-of the `Ok` to the name `num`, and then we just return it on the right-hand
-side. In the `Err` case, we don’t care what kind of error it is, so we just
+of the `Ok` to the name `num`, and then we  return it on the right-hand
+side. In the `Err` case, we don’t care what kind of error it is, so we
 use `_` instead of a name. This ignores the error, and `continue` causes us
 to go to the next iteration of the `loop`.
 
index c444f9f2fe53d420e5cbae1bbb63007e2c51d965..5622326d20c31913323f7001e940afaa155c080d 100644 (file)
@@ -37,7 +37,7 @@ which gives us a reference to the next value of the iterator. `next` returns an
 `None`, we `break` out of the loop.
 
 This code sample is basically the same as our `for` loop version. The `for`
-loop is just a handy way to write this `loop`/`match`/`break` construct.
+loop is a handy way to write this `loop`/`match`/`break` construct.
 
 `for` loops aren't the only thing that uses iterators, however. Writing your
 own iterator involves implementing the `Iterator` trait. While doing that is
@@ -94,8 +94,8 @@ Now we're explicitly dereferencing `num`. Why does `&nums` give us
 references?  Firstly, because we explicitly asked it to with
 `&`. Secondly, if it gave us the data itself, we would have to be its
 owner, which would involve making a copy of the data and giving us the
-copy. With references, we're just borrowing a reference to the data,
-and so it's just passing a reference, without needing to do the move.
+copy. With references, we're only borrowing a reference to the data,
+and so it's only passing a reference, without needing to do the move.
 
 So, now that we've established that ranges are often not what you want, let's
 talk about what you do want instead.
@@ -278,7 +278,7 @@ doesn't print any numbers:
 ```
 
 If you are trying to execute a closure on an iterator for its side effects,
-just use `for` instead.
+use `for` instead.
 
 There are tons of interesting iterator adaptors. `take(n)` will return an
 iterator over the next `n` elements of the original iterator. Let's try it out
index 1a02bc95e9d9c84247ff01c674415697457a0b04..7be7fa4f039a710dece1e34f1507f17757bbc286 100644 (file)
@@ -1,6 +1,6 @@
 % Learn Rust
 
-Welcome! This section has a few tutorials that teach you Rust through building
+Welcome! This chapter has a few tutorials that teach you Rust through building
 projects. You’ll get a high-level overview, but we’ll skim over the details.
 
 If you’d prefer a more ‘from the ground up’-style experience, check
index 68bbd0c98993dad5e3ac960a73cdfb936efb8bfc..4193c93c894c4fec4a97b19aa3a5ce1ccf1b0509 100644 (file)
@@ -84,7 +84,7 @@ We previously talked a little about [function syntax][functions], but we didn’
 discuss the `<>`s after a function’s name. A function can have ‘generic
 parameters’ between the `<>`s, of which lifetimes are one kind. We’ll discuss
 other kinds of generics [later in the book][generics], but for now, let’s
-just focus on the lifetimes aspect.
+focus on the lifetimes aspect.
 
 [functions]: functions.html
 [generics]: generics.html
@@ -103,13 +103,13 @@ Then in our parameter list, we use the lifetimes we’ve named:
 ...(x: &'a i32)
 ```
 
-If we wanted an `&mut` reference, we’d do this:
+If we wanted a `&mut` reference, we’d do this:
 
 ```rust,ignore
 ...(x: &'a mut i32)
 ```
 
-If you compare `&mut i32` to `&'a mut i32`, they’re the same, it’s just that
+If you compare `&mut i32` to `&'a mut i32`, they’re the same, it’s that
 the lifetime `'a` has snuck in between the `&` and the `mut i32`. We read `&mut
 i32` as ‘a mutable reference to an `i32`’ and `&'a mut i32` as ‘a mutable
 reference to an `i32` with the lifetime `'a`’.
@@ -175,7 +175,7 @@ fn main() {
 ```
 
 As you can see, we need to declare a lifetime for `Foo` in the `impl` line. We repeat
-`'a` twice, just like on functions: `impl<'a>` defines a lifetime `'a`, and `Foo<'a>`
+`'a` twice, like on functions: `impl<'a>` defines a lifetime `'a`, and `Foo<'a>`
 uses it.
 
 ## Multiple lifetimes
@@ -353,8 +353,8 @@ fn frob<'a, 'b>(s: &'a str, t: &'b str) -> &str; // Expanded: Output lifetime is
 fn get_mut(&mut self) -> &mut T; // elided
 fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded
 
-fn args<T:ToCStr>(&mut self, args: &[T]) -> &mut Command; // elided
-fn args<'a, 'b, T:ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command; // expanded
+fn args<T: ToCStr>(&mut self, args: &[T]) -> &mut Command; // elided
+fn args<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command; // expanded
 
 fn new(buf: &mut [u8]) -> BufWriter; // elided
 fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a>; // expanded
index f7f27858cd24042a5342ca292da7b93b331223d4..188abb316ab0b316677301a2aa9a92f872e93d30 100644 (file)
@@ -285,9 +285,11 @@ This expands to
 
 ```text
 const char *state = "reticulating splines";
-int state = get_log_state();
-if (state > 0) {
-    printf("log(%d): %s\n", state, state);
+{
+    int state = get_log_state();
+    if (state > 0) {
+        printf("log(%d): %s\n", state, state);
+    }
 }
 ```
 
@@ -476,19 +478,19 @@ which syntactic form it matches.
 
 There are additional rules regarding the next token after a metavariable:
 
-* `expr` variables may only be followed by one of: `=> , ;`
-* `ty` and `path` variables may only be followed by one of: `=> , : = > as`
-* `pat` variables may only be followed by one of: `=> , = if in`
+* `expr` and `stmt` variables may only be followed by one of: `=> , ;`
+* `ty` and `path` variables may only be followed by one of: `=> , = | ; : > [ { as where`
+* `pat` variables may only be followed by one of: `=> , = if in`
 * Other variables may be followed by any token.
 
 These rules provide some flexibility for Rust’s syntax to evolve without
 breaking existing macros.
 
 The macro system does not deal with parse ambiguity at all. For example, the
-grammar `$($t:ty)* $e:expr` will always fail to parse, because the parser would
-be forced to choose between parsing `$t` and parsing `$e`. Changing the
+grammar `$($i:ident)* $e:expr` will always fail to parse, because the parser would
+be forced to choose between parsing `$i` and parsing `$e`. Changing the
 invocation syntax to put a distinctive token in front can solve the problem. In
-this case, you can write `$(T $t:ty)* E $e:exp`.
+this case, you can write `$(I $i:ident)* E $e:expr`.
 
 [item]: ../reference.html#items
 
@@ -611,8 +613,7 @@ to define a single macro that works both inside and outside our library. The
 function name will expand to either `::increment` or `::mylib::increment`.
 
 To keep this system simple and correct, `#[macro_use] extern crate ...` may
-only appear at the root of your crate, not inside `mod`. This ensures that
-`$crate` is a single identifier.
+only appear at the root of your crate, not inside `mod`.
 
 # The deep end
 
index 113e218883b34c4b089d02eaf82fd7863f4e1848..acffaf4544b10182466cc619365a42d62015969e 100644 (file)
@@ -23,26 +23,24 @@ match x {
 `match` takes an expression and then branches based on its value. Each ‘arm’ of
 the branch is of the form `val => expression`. When the value matches, that arm’s
 expression will be evaluated. It’s called `match` because of the term ‘pattern
-matching’, which `match` is an implementation of. There’s an [entire section on
+matching’, which `match` is an implementation of. There’s a [separate section on
 patterns][patterns] that covers all the patterns that are possible here.
 
 [patterns]: patterns.html
 
-So what’s the big advantage? Well, there are a few. First of all, `match`
-enforces ‘exhaustiveness checking’. Do you see that last arm, the one with the
-underscore (`_`)? If we remove that arm, Rust will give us an error:
+One of the many advantages of `match` is it enforces ‘exhaustiveness checking’. 
+For example if we remove the last arm with the underscore `_`, the compiler will 
+give us an error:
 
 ```text
 error: non-exhaustive patterns: `_` not covered
 ```
 
-In other words, Rust is trying to tell us we forgot a value. Because `x` is an
-integer, Rust knows that it can have a number of different values – for
-example, `6`. Without the `_`, however, there is no arm that could match, and
-so Rust refuses to compile the code. `_` acts like a ‘catch-all arm’. If none
-of the other arms match, the arm with `_` will, and since we have this
-catch-all arm, we now have an arm for every possible value of `x`, and so our
-program will compile successfully.
+Rust is telling us that we forgot a value. The compiler infers from `x` that it
+can have any positive 32bit value; for example 1 to 2,147,483,647. The `_` acts 
+as a 'catch-all', and will catch all possible values that *aren't* specified in 
+an arm of `match`. As you can see with the previous example, we provide `match` 
+arms for integers 1-5, if `x` is 6 or any other value, then it is caught by `_`.
 
 `match` is also an expression, which means we can use it on the right-hand
 side of a `let` binding or directly where an expression is used:
@@ -60,7 +58,8 @@ let number = match x {
 };
 ```
 
-Sometimes it’s a nice way of converting something from one type to another.
+Sometimes it’s a nice way of converting something from one type to another; in 
+this example the integers are converted to `String`.
 
 # Matching on enums
 
@@ -91,7 +90,8 @@ fn process_message(msg: Message) {
 
 Again, the Rust compiler checks exhaustiveness, so it demands that you
 have a match arm for every variant of the enum. If you leave one off, it
-will give you a compile-time error unless you use `_`.
+will give you a compile-time error unless you use `_` or provide all possible 
+arms.
 
 Unlike the previous uses of `match`, you can’t use the normal `if`
 statement to do this. You can use the [`if let`][if-let] statement,
index 41c134b29f3d18109ba3089cd941ce6072c979d9..b2532663339ff26aa1d7d6103b64359ddf25270e 100644 (file)
@@ -49,11 +49,11 @@ and inside it, define a method, `area`.
 Methods take a special first parameter, of which there are three variants:
 `self`, `&self`, and `&mut self`. You can think of this first parameter as
 being the `foo` in `foo.bar()`. The three variants correspond to the three
-kinds of things `foo` could be: `self` if it’s just a value on the stack,
+kinds of things `foo` could be: `self` if it’s a value on the stack,
 `&self` if it’s a reference, and `&mut self` if it’s a mutable reference.
-Because we took the `&self` parameter to `area`, we can use it just like any
+Because we took the `&self` parameter to `area`, we can use it like any
 other parameter. Because we know it’s a `Circle`, we can access the `radius`
-just like we would with any other `struct`.
+like we would with any other `struct`.
 
 We should default to using `&self`, as you should prefer borrowing over taking
 ownership, as well as taking immutable references over mutable ones. Here’s an
@@ -151,7 +151,7 @@ fn grow(&self, increment: f64) -> Circle {
 # Circle } }
 ```
 
-We just say we’re returning a `Circle`. With this method, we can grow a new
+We say we’re returning a `Circle`. With this method, we can grow a new
 `Circle` to any arbitrary size.
 
 # Associated functions
index 0578fbf8bdb09286f768f6b617aeedab63b23480..b3be71038a992f66605f461c14f5888b708a6c5d 100644 (file)
@@ -39,7 +39,7 @@ script:
 $ sudo /usr/local/lib/rustlib/uninstall.sh
 ```
 
-If you used the Windows installer, just re-run the `.msi` and it will give you
+If you used the Windows installer, re-run the `.msi` and it will give you
 an uninstall option.
 
 Some people, and somewhat rightfully so, get very upset when we tell you to
@@ -66,7 +66,7 @@ Finally, a comment about Windows. Rust considers Windows to be a first-class
 platform upon release, but if we're honest, the Windows experience isn't as
 integrated as the Linux/OS X experience is. We're working on it! If anything
 does not work, it is a bug. Please let us know if that happens. Each and every
-commit is tested against Windows just like any other platform.
+commit is tested against Windows like any other platform.
 
 If you've got Rust installed, you can open up a shell, and type this:
 
index e53664eeb552662e2b0e4a17aafd17dea37c2adb..fcce831c2d09d23d3ebe8dc0c270bb1f276b5700 100644 (file)
@@ -120,7 +120,7 @@ fn main() {
 }
 ```
 
-For `HasArea` and `Square`, we just declare a type parameter `T` and replace
+For `HasArea` and `Square`, we declare a type parameter `T` and replace
 `f64` with it. The `impl` needs more involved modifications:
 
 ```ignore
index 17b263ef00ab786ea2f72a914724d64bea5b42ca..a62d31d362b14f217c367efbfe76779167dd1ded 100644 (file)
@@ -51,15 +51,24 @@ fn foo() {
 }
 ```
 
-When `v` comes into scope, a new [`Vec<T>`][vect] is created. In this case, the
-vector also allocates space on [the heap][heap], for the three elements. When
-`v` goes out of scope at the end of `foo()`, Rust will clean up everything
-related to the vector, even the heap-allocated memory. This happens
-deterministically, at the end of the scope.
+When `v` comes into scope, a new [vector] is created, and it allocates space on
+[the heap][heap] for each of its elements. When `v` goes out of scope at the
+end of `foo()`, Rust will clean up everything related to the vector, even the
+heap-allocated memory. This happens deterministically, at the end of the scope.
 
-[vect]: ../std/vec/struct.Vec.html
+We'll cover [vectors] in detail later in this chapter; we only use them
+here as an example of a type that allocates space on the heap at runtime. They
+behave like [arrays], except their size may change by `push()`ing more
+elements onto them.
+
+Vectors have a [generic type][generics] `Vec<T>`, so in this example `v` will have type
+`Vec<i32>`. We'll cover generics in detail later in this chapter.
+
+[arrays]: primitive-types.html#arrays
+[vectors]: vectors.html
 [heap]: the-stack-and-the-heap.html
 [bindings]: variable-bindings.html
+[generics]: generics.html
 
 # Move semantics
 
index 8f4a7a439553b8d4cf7f52ea7c4c42f50905fee0..8e9e7246e56f0b3daa935be46250aec061c449bf 100644 (file)
@@ -27,7 +27,7 @@ There’s one pitfall with patterns: like anything that introduces a new binding
 they introduce shadowing. For example:
 
 ```rust
-let x = 'x';
+let x = 1;
 let c = 'c';
 
 match c {
@@ -41,12 +41,14 @@ This prints:
 
 ```text
 x: c c: c
-x: x
+x: 1
 ```
 
 In other words, `x =>` matches the pattern and introduces a new binding named
-`x` that’s in scope for the match arm. Because we already have a binding named
-`x`, this new `x` shadows it.
+`x`. This new binding is in scope for the match arm and takes on the value of
+`c`. Notice that the value of `x` outside the scope of the match has no bearing
+on the value of `x` within it. Because we already have a binding named `x`, this
+new `x` shadows it.
 
 # Multiple patterns
 
@@ -116,7 +118,7 @@ match origin {
 
 This prints `x is 0`.
 
-You can do this kind of match on any member, not just the first:
+You can do this kind of match on any member, not only the first:
 
 ```rust
 struct Point {
@@ -153,7 +155,7 @@ match some_value {
 ```
 
 In the first arm, we bind the value inside the `Ok` variant to `value`. But
-in the `Err` arm, we use `_` to disregard the specific error, and just print
+in the `Err` arm, we use `_` to disregard the specific error, and print
 a general error message.
 
 `_` is valid in any pattern that creates a binding. This can be useful to
@@ -324,7 +326,7 @@ match x {
 ```
 
 This prints `no`, because the `if` applies to the whole of `4 | 5`, and not to
-just the `5`. In other words, the precedence of `if` behaves like this:
+only the `5`. In other words, the precedence of `if` behaves like this:
 
 ```text
 (4 | 5) if y => ...
index a8c7a7d41573e4fa565a264cf829bc451f2e1b20..cfd5372b90f91f92f19a60c84e5c972d2e349363 100644 (file)
@@ -160,20 +160,23 @@ documentation][array].
 
 A ‘slice’ is a reference to (or “view” into) another data structure. They are
 useful for allowing safe, efficient access to a portion of an array without
-copying. For example, you might want to reference just one line of a file read
+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.
 
 ## Slicing syntax
 
 You can use a combo of `&` and `[]` to create a slice from various things. The
-`&` indicates that slices are similar to references, and the `[]`s, with a
-range, let you define the length of the slice:
+`&` indicates that slices are similar to [references], which we will cover in
+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
-let middle = &a[1..4]; // A slice of a: just the elements 1, 2, and 3
+let middle = &a[1..4]; // A slice of a: only the elements 1, 2, and 3
 ```
 
 Slices have type `&[T]`. We’ll talk about that `T` when we cover
@@ -189,11 +192,13 @@ documentation][slice].
 # `str`
 
 Rust’s `str` type is the most primitive string type. As an [unsized type][dst],
-it’s not very useful by itself, but becomes useful when placed behind a reference,
-like [`&str`][strings]. As such, we’ll just leave it at that.
+it’s not very useful by itself, but becomes useful when placed behind a
+reference, like `&str`. We'll elaborate further when we cover
+[Strings][strings] and [references].
 
 [dst]: unsized-types.html
 [strings]: strings.html
+[references]: references-and-borrowing.html
 
 You can find more documentation for `str` [in the standard library
 documentation][str].
@@ -215,11 +220,11 @@ with the type annotated:
 let x: (i32, &str) = (1, "hello");
 ```
 
-As you can see, the type of a tuple looks just like the tuple, but with each
+As you can see, the type of a tuple looks like the tuple, but with each
 position having a type name rather than the value. Careful readers will also
 note that tuples are heterogeneous: we have an `i32` and a `&str` in this tuple.
 In systems programming languages, strings are a bit more complex than in other
-languages. For now, just read `&str` as a *string slice*, and we’ll learn more
+languages. For now, read `&str` as a *string slice*, and we’ll learn more
 soon.
 
 You can assign one tuple into another, if they have the same contained types
@@ -244,7 +249,7 @@ println!("x is {}", x);
 ```
 
 Remember [before][let] when I said the left-hand side of a `let` statement was more
-powerful than just assigning a binding? Here we are. We can put a pattern on
+powerful than assigning a binding? Here we are. We can put a pattern on
 the left-hand side of the `let`, and if it matches up to the right-hand side,
 we can assign multiple bindings at once. In this case, `let` “destructures”
 or “breaks up” the tuple, and assigns the bits to three bindings.
index d8758e0c695c21fa394675062133ac856a80bce0..e7faf174600a9381b040c6c03d9deba9476f7774 100644 (file)
@@ -84,7 +84,7 @@ it borrows ownership. A binding that borrows something does not deallocate the
 resource when it goes out of scope. This means that after the call to `foo()`,
 we can use our original bindings again.
 
-References are immutable, just like bindings. This means that inside of `foo()`,
+References are immutable, like bindings. This means that inside of `foo()`,
 the vectors can’t be changed at all:
 
 ```rust,ignore
@@ -126,10 +126,10 @@ 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 an `&mut` reference. You'll also need to use them for
+this is because `y` is a `&mut` reference. You'll also need to use them for
 accessing the contents of a reference as well.
 
-Otherwise, `&mut` references are just like references. There _is_ a large
+Otherwise, `&mut` references are like references. There _is_ a large
 difference between the two, and how they interact, though. You can tell
 something is fishy in the above example, because we need that extra scope, with
 the `{` and `}`. If we remove them, we get an error:
@@ -263,7 +263,7 @@ for i in &v {
 }
 ```
 
-This prints out one through three. As we iterate through the vectors, we’re
+This prints out one through three. As we iterate through the vector, we’re
 only given references to the elements. And `v` is itself borrowed as immutable,
 which means we can’t change it while we’re iterating:
 
diff --git a/src/doc/book/rust-inside-other-languages.md b/src/doc/book/rust-inside-other-languages.md
deleted file mode 100644 (file)
index 61627c0..0000000
+++ /dev/null
@@ -1,344 +0,0 @@
-% Rust Inside Other Languages
-
-For our third project, we’re going to choose something that shows off one of
-Rust’s greatest strengths: a lack of a substantial runtime.
-
-As organizations grow, they increasingly rely on a multitude of programming
-languages. Different programming languages have different strengths and
-weaknesses, and a polyglot stack lets you use a particular language where
-its strengths make sense and a different one where it’s weak.
-
-A very common area where many programming languages are weak is in runtime
-performance of programs. Often, using a language that is slower, but offers
-greater programmer productivity, is a worthwhile trade-off. To help mitigate
-this, they provide a way to write some of your system in C and then call
-that C code as though it were written in the higher-level language. This is
-called a ‘foreign function interface’, often shortened to ‘FFI’.
-
-Rust has support for FFI in both directions: it can call into C code easily,
-but crucially, it can also be called _into_ as easily as C. Combined with
-Rust’s lack of a garbage collector and low runtime requirements, this makes
-Rust a great candidate to embed inside of other languages when you need
-that extra oomph.
-
-There is a whole [chapter devoted to FFI][ffi] and its specifics elsewhere in
-the book, but in this chapter, we’ll examine this particular use-case of FFI,
-with examples in Ruby, Python, and JavaScript.
-
-[ffi]: ffi.html
-
-# The problem
-
-There are many different projects we could choose here, but we’re going to
-pick an example where Rust has a clear advantage over many other languages:
-numeric computing and threading.
-
-Many languages, for the sake of consistency, place numbers on the heap, rather
-than on the stack. Especially in languages that focus on object-oriented
-programming and use garbage collection, heap allocation is the default. Sometimes
-optimizations can stack allocate particular numbers, but rather than relying
-on an optimizer to do its job, we may want to ensure that we’re always using
-primitive number types rather than some sort of object type.
-
-Second, many languages have a ‘global interpreter lock’ (GIL), which limits
-concurrency in many situations. This is done in the name of safety, which is
-a positive effect, but it limits the amount of work that can be done at the
-same time, which is a big negative.
-
-To emphasize these two aspects, we’re going to create a little project that
-uses these two aspects heavily. Since the focus of the example is to embed
-Rust into other languages, rather than the problem itself, we’ll just use a
-toy example:
-
-> Start ten threads. Inside each thread, count from one to five million. After
-> all ten threads are finished, print out ‘done!’.
-
-I chose five million based on my particular computer. Here’s an example of this
-code in Ruby:
-
-```ruby
-threads = []
-
-10.times do
-  threads << Thread.new do
-    count = 0
-
-    5_000_000.times do
-      count += 1
-    end
-
-    count
-  end
-end
-
-threads.each do |t|
-  puts "Thread finished with count=#{t.value}"
-end
-puts "done!"
-```
-
-Try running this example, and choose a number that runs for a few seconds.
-Depending on your computer’s hardware, you may have to increase or decrease the
-number.
-
-On my system, running this program takes `2.156` seconds. And, if I use some
-sort of process monitoring tool, like `top`, I can see that it only uses one
-core on my machine. That’s the GIL kicking in.
-
-While it’s true that this is a synthetic program, one can imagine many problems
-that are similar to this in the real world. For our purposes, spinning up a few
-busy threads represents some sort of parallel, expensive computation.
-
-# A Rust library
-
-Let’s rewrite this problem in Rust. First, let’s make a new project with
-Cargo:
-
-```bash
-$ cargo new embed
-$ cd embed
-```
-
-This program is fairly easy to write in Rust:
-
-```rust
-use std::thread;
-
-fn process() {
-    let handles: Vec<_> = (0..10).map(|_| {
-        thread::spawn(|| {
-            let mut x = 0;
-            for _ in 0..5_000_000 {
-                x += 1
-            }
-            x
-        })
-    }).collect();
-
-    for h in handles {
-        println!("Thread finished with count={}",
-           h.join().map_err(|_| "Could not join a thread!").unwrap());
-    }
-}
-```
-
-Some of this should look familiar from previous examples. We spin up ten
-threads, collecting them into a `handles` vector. Inside of each thread, we
-loop five million times, and add one to `x` each time. Finally, we join on
-each thread.
-
-Right now, however, this is a Rust library, and it doesn’t expose anything
-that’s callable from C. If we tried to hook this up to another language right
-now, it wouldn’t work. We only need to make two small changes to fix this,
-though. The first is to modify the beginning of our code:
-
-```rust,ignore
-#[no_mangle]
-pub extern fn process() {
-```
-
-We have to add a new attribute, `no_mangle`. When you create a Rust library, it
-changes the name of the function in the compiled output. The reasons for this
-are outside the scope of this tutorial, but in order for other languages to
-know how to call the function, we can’t do that. This attribute turns
-that behavior off.
-
-The other change is the `pub extern`. The `pub` means that this function should
-be callable from outside of this module, and the `extern` says that it should
-be able to be called from C. That’s it! Not a whole lot of change.
-
-The second thing we need to do is to change a setting in our `Cargo.toml`. Add
-this at the bottom:
-
-```toml
-[lib]
-name = "embed"
-crate-type = ["dylib"]
-```
-
-This tells Rust that we want to compile our library into a standard dynamic
-library. By default, Rust compiles an ‘rlib’, a Rust-specific format.
-
-Let’s build the project now:
-
-```bash
-$ cargo build --release
-   Compiling embed v0.1.0 (file:///home/steve/src/embed)
-```
-
-We’ve chosen `cargo build --release`, which builds with optimizations on. We
-want this to be as fast as possible! You can find the output of the library in
-`target/release`:
-
-```bash
-$ ls target/release/
-build  deps  examples  libembed.so  native
-```
-
-That `libembed.so` is our ‘shared object’ library. We can use this file
-just like any shared object library written in C! As an aside, this may be
-`embed.dll` (Microsoft Windows) or `libembed.dylib` (Mac OS X), depending on 
-your operating system.
-
-Now that we’ve got our Rust library built, let’s use it from our Ruby.
-
-# Ruby
-
-Open up an `embed.rb` file inside of our project, and do this:
-
-```ruby
-require 'ffi'
-
-module Hello
-  extend FFI::Library
-  ffi_lib 'target/release/libembed.so'
-  attach_function :process, [], :void
-end
-
-Hello.process
-
-puts 'done!'
-```
-
-Before we can run this, we need to install the `ffi` gem:
-
-```bash
-$ gem install ffi # this may need sudo
-Fetching: ffi-1.9.8.gem (100%)
-Building native extensions.  This could take a while...
-Successfully installed ffi-1.9.8
-Parsing documentation for ffi-1.9.8
-Installing ri documentation for ffi-1.9.8
-Done installing documentation for ffi after 0 seconds
-1 gem installed
-```
-
-And finally, we can try running it:
-
-```bash
-$ ruby embed.rb
-Thread finished with count=5000000
-Thread finished with count=5000000
-Thread finished with count=5000000
-Thread finished with count=5000000
-Thread finished with count=5000000
-Thread finished with count=5000000
-Thread finished with count=5000000
-Thread finished with count=5000000
-Thread finished with count=5000000
-Thread finished with count=5000000
-done!
-done!
-$
-```
-
-Whoa, that was fast! On my system, this took `0.086` seconds, rather than
-the two seconds the pure Ruby version took. Let’s break down this Ruby
-code:
-
-```ruby
-require 'ffi'
-```
-
-We first need to require the `ffi` gem. This lets us interface with our
-Rust library like a C library.
-
-```ruby
-module Hello
-  extend FFI::Library
-  ffi_lib 'target/release/libembed.so'
-```
-
-The `Hello` module is used to attach the native functions from the shared
-library. Inside, we `extend` the necessary `FFI::Library` module and then call
-`ffi_lib` to load up our shared object library. We just pass it the path that
-our library is stored, which, as we saw before, is
-`target/release/libembed.so`.
-
-```ruby
-attach_function :process, [], :void
-```
-
-The `attach_function` method is provided by the FFI gem. It’s what
-connects our `process()` function in Rust to a Ruby function of the
-same name. Since `process()` takes no arguments, the second parameter
-is an empty array, and since it returns nothing, we pass `:void` as
-the final argument.
-
-```ruby
-Hello.process
-```
-
-This is the actual call into Rust. The combination of our `module`
-and the call to `attach_function` sets this all up. It looks like
-a Ruby function but is actually Rust!
-
-```ruby
-puts 'done!'
-```
-
-Finally, as per our project’s requirements, we print out `done!`.
-
-That’s it! As we’ve seen, bridging between the two languages is really easy,
-and buys us a lot of performance.
-
-Next, let’s try Python!
-
-# Python
-
-Create an `embed.py` file in this directory, and put this in it:
-
-```python
-from ctypes import cdll
-
-lib = cdll.LoadLibrary("target/release/libembed.so")
-
-lib.process()
-
-print("done!")
-```
-
-Even easier! We use `cdll` from the `ctypes` module. A quick call
-to `LoadLibrary` later, and we can call `process()`.
-
-On my system, this takes `0.017` seconds. Speedy!
-
-# Node.js
-
-Node isn’t a language, but it’s currently the dominant implementation of
-server-side JavaScript.
-
-In order to do FFI with Node, we first need to install the library:
-
-```bash
-$ npm install ffi
-```
-
-After that installs, we can use it:
-
-```javascript
-var ffi = require('ffi');
-
-var lib = ffi.Library('target/release/libembed', {
-  'process': ['void', []]
-});
-
-lib.process();
-
-console.log("done!");
-```
-
-It looks more like the Ruby example than the Python example. We use
-the `ffi` module to get access to `ffi.Library()`, which loads up
-our shared object. We need to annotate the return type and argument
-types of the function, which are `void` for return and an empty
-array to signify no arguments. From there, we just call it and
-print the result.
-
-On my system, this takes a quick `0.092` seconds.
-
-# Conclusion
-
-As you can see, the basics of doing this are _very_ easy. Of course,
-there's a lot more that we could do here. Check out the [FFI][ffi]
-chapter for more details.
index 42a0acd21a2a0a0aeb19a8f2969db3b562453017..751619d544a4af57746c31b0acf734bd6dab6adf 100644 (file)
@@ -44,7 +44,7 @@ let s = "foo\
 assert_eq!("foobar", s);
 ```
 
-Rust has more than just `&str`s though. A `String`, is a heap-allocated string.
+Rust has more than only `&str`s though. A `String`, is a heap-allocated string.
 This string is growable, and is also guaranteed to be UTF-8. `String`s are
 commonly created by converting from a string slice using the `to_string`
 method.
index b51ad8e087d22a995f550d2f15f79fc864cf17ba..b2fddf336273fe0d1f3d663e7bd09886579e41fb 100644 (file)
@@ -9,7 +9,8 @@ let origin_x = 0;
 let origin_y = 0;
 ```
 
-A `struct` lets us combine these two into a single, unified datatype:
+A `struct` lets us combine these two into a single, unified datatype with `x`
+and `y` as field labels:
 
 ```rust
 struct Point {
@@ -32,7 +33,7 @@ We can create an instance of our `struct` via `let`, as usual, but we use a `key
 value` style syntax to set each field. The order doesn’t need to be the same as
 in the original declaration.
 
-Finally, because fields have names, we can access the field through dot
+Finally, because fields have names, we can access them through dot
 notation: `origin.x`.
 
 The values in `struct`s are immutable by default, like other bindings in Rust.
@@ -67,9 +68,8 @@ struct Point {
 
 Mutability is a property of the binding, not of the structure itself. If you’re
 used to field-level mutability, this may seem strange at first, but it
-significantly simplifies things. It even lets you make things mutable for a short
-time only:
-
+significantly simplifies things. It even lets you make things mutable on a temporary
+basis:
 
 ```rust,ignore
 struct Point {
@@ -82,12 +82,41 @@ fn main() {
 
     point.x = 5;
 
-    let point = point; // this new binding can’t change now
+    let point = point; // now immutable
 
     point.y = 6; // this causes an error
 }
 ```
 
+Your structure can still contain `&mut` pointers, which will let
+you do some kinds of mutation:
+
+```rust
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+struct PointRef<'a> {
+    x: &'a mut i32,
+    y: &'a mut i32,
+}
+
+fn main() {
+    let mut point = Point { x: 0, y: 0 };
+
+    {
+        let r = PointRef { x: &mut point.x, y: &mut point.y };
+
+        *r.x = 5;
+        *r.y = 6;
+    }
+
+    assert_eq!(5, point.x);
+    assert_eq!(6, point.y);
+}
+```
+
 # Update syntax
 
 A `struct` can include `..` to indicate that you want to use a copy of some
@@ -121,27 +150,24 @@ let point = Point3d { z: 1, x: 2, .. origin };
 # Tuple structs
 
 Rust has another data type that’s like a hybrid between a [tuple][tuple] and a
-`struct`, called a ‘tuple struct’. Tuple structs have a name, but
-their fields don’t:
+`struct`, called a ‘tuple struct’. Tuple structs have a name, but their fields
+don't. They are declared with the `struct` keyword, and then with a name
+followed by a tuple:
+
+[tuple]: primitive-types.html#tuples
 
 ```rust
 struct Color(i32, i32, i32);
 struct Point(i32, i32, i32);
-```
-
-[tuple]: primitive-types.html#tuples
-
-These two will not be equal, even if they have the same values:
 
-```rust
-# struct Color(i32, i32, i32);
-# struct Point(i32, i32, i32);
 let black = Color(0, 0, 0);
 let origin = Point(0, 0, 0);
 ```
+Here, `black` and `origin` are not equal, even though they contain the same
+values.
 
-It is almost always better to use a `struct` than a tuple struct. We would write
-`Color` and `Point` like this instead:
+It is almost always better to use a `struct` than a tuple struct. We
+would write `Color` and `Point` like this instead:
 
 ```rust
 struct Color {
@@ -157,13 +183,14 @@ struct Point {
 }
 ```
 
-Now, we have actual names, rather than positions. Good names are important,
-and with a `struct`, we have actual names.
+Good names are important, and while values in a tuple struct can be
+referenced with dot notation as well, a `struct` gives us actual names,
+rather than positions.
 
-There _is_ one case when a tuple struct is very useful, though, and that’s a
-tuple struct with only one element. We call this the ‘newtype’ pattern, because
-it allows you to create a new type, distinct from that of its contained value
-and expressing its own semantic meaning:
+There _is_ one case when a tuple struct is very useful, though, and that is when
+it has only one element. We call this the ‘newtype’ pattern, because
+it allows you to create a new type that is distinct from its contained value
+and also expresses its own semantic meaning:
 
 ```rust
 struct Inches(i32);
@@ -175,7 +202,7 @@ println!("length is {} inches", integer_length);
 ```
 
 As you can see here, you can extract the inner integer type through a
-destructuring `let`, just as with regular tuples. In this case, the
+destructuring `let`, as with regular tuples. In this case, the
 `let Inches(integer_length)` assigns `10` to `integer_length`.
 
 # Unit-like structs
@@ -196,7 +223,7 @@ This is rarely useful on its own (although sometimes it can serve as a
 marker type), but in combination with other features, it can become
 useful. For instance, a library may ask you to create a structure that
 implements a certain [trait][trait] to handle events. If you don’t have
-any data you need to store in the structure, you can just create a
+any data you need to store in the structure, you can create a
 unit-like `struct`.
 
 [trait]: traits.html
index cce985c9e484c1436179e5773e396b47a35ecad4..e9ec26dccdcdcb4c934bcf112f4ddc73445920ba 100644 (file)
@@ -1,6 +1,6 @@
 % Syntax and Semantics
 
-This section breaks Rust down into small chunks, one for each concept.
+This chapter breaks Rust down into small chunks, one for each concept.
 
 If you’d like to learn Rust from the bottom up, reading this in order is a
 great way to do that.
index 528c0b537f396035e1097bfd1d3f39bc2ad5c6e3..f7e32943c638e470b76d4930f69a71009022a42f 100644 (file)
@@ -41,6 +41,7 @@
 
 * `!` (`ident!(…)`, `ident!{…}`, `ident![…]`): denotes macro expansion.  See [Macros].
 * `!` (`!expr`): bitwise or logical complement.  Overloadable (`Not`).
+* `!=` (`var != expr`): nonequality comparison.  Overloadable (`PartialEq`).
 * `%` (`expr % expr`): arithmetic remainder.  Overloadable (`Rem`).
 * `%=` (`var %= expr`): arithmetic remainder & assignment.
 * `&` (`expr & expr`): bitwise and.  Overloadable (`BitAnd`).
 * `;` (`[…; len]`): part of fixed-size array syntax.  See [Primitive Types (Arrays)].
 * `<<` (`expr << expr`): left-shift.  Overloadable (`Shl`).
 * `<<=` (`var <<= expr`): left-shift & assignment.
-* `<` (`expr < expr`): less-than comparison.  Overloadable (`Cmp`, `PartialCmp`).
-* `<=` (`var <= expr`): less-than or equal-to comparison.  Overloadable (`Cmp`, `PartialCmp`).
+* `<` (`expr < expr`): less-than comparison.  Overloadable (`PartialOrd`).
+* `<=` (`var <= expr`): less-than or equal-to comparison.  Overloadable (`PartialOrd`).
 * `=` (`var = expr`, `ident = type`): assignment/equivalence.  See [Variable Bindings], [`type` Aliases], generic parameter defaults.
-* `==` (`var == expr`): comparison.  Overloadable (`Eq`, `PartialEq`).
+* `==` (`var == expr`): equality comparison.  Overloadable (`PartialEq`).
 * `=>` (`pat => expr`): part of match arm syntax.  See [Match].
-* `>` (`expr > expr`): greater-than comparison.  Overloadable (`Cmp`, `PartialCmp`).
-* `>=` (`var >= expr`): greater-than or equal-to comparison.  Overloadable (`Cmp`, `PartialCmp`).
+* `>` (`expr > expr`): greater-than comparison.  Overloadable (`PartialOrd`).
+* `>=` (`var >= expr`): greater-than or equal-to comparison.  Overloadable (`PartialOrd`).
 * `>>` (`expr >> expr`): right-shift.  Overloadable (`Shr`).
 * `>>=` (`var >>= expr`): right-shift & assignment.
 * `@` (`ident @ pat`): pattern binding.  See [Patterns (Bindings)].
 [Traits (Multiple Trait Bounds)]: traits.html#multiple-trait-bounds
 [Traits]: traits.html
 [Unsafe]: unsafe.html
-[Unsized Types (`?Sized`)]: unsized-types.html#?sized
+[Unsized Types (`?Sized`)]: unsized-types.html#sized
 [Variable Bindings]: variable-bindings.html
index 32f0d689e30ecec843021e0323adf898f7cf5279..005184e90a7e96a8e86205d38ffeb132b860420d 100644 (file)
@@ -365,7 +365,7 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
 It works!
 
 The current convention is to use the `tests` module to hold your "unit-style"
-tests. Anything that just tests one small bit of functionality makes sense to
+tests. Anything that tests one small bit of functionality makes sense to
 go here. But what about "integration-style" tests instead? For that, we have
 the `tests` directory.
 
@@ -503,7 +503,7 @@ for the function test. These will auto increment with names like `add_two_1` as
 you add more examples.
 
 We haven’t covered all of the details with writing documentation tests. For more,
-please see the [Documentation chapter](documentation.html)
+please see the [Documentation chapter](documentation.html).
 
 One final note: documentation tests *cannot* be run on binary crates.
 To see more on file arrangement see the [Crates and
index 9cc3e12aa04ac729255851e692752e9bf205c3b7..a7b6faccd8d6e01bac21c0e22c8e6c3a7e9bf36a 100644 (file)
@@ -44,7 +44,7 @@ values ‘go on the stack’. What does that mean?
 Well, when a function gets called, some memory gets allocated for all of its
 local variables and some other information. This is called a ‘stack frame’, and
 for the purpose of this tutorial, we’re going to ignore the extra information
-and just consider the local variables we’re allocating. So in this case, when
+and only consider the local variables we’re allocating. So in this case, when
 `main()` is run, we’ll allocate a single 32-bit integer for our stack frame.
 This is automatically handled for you, as you can see; we didn’t have to write
 any special Rust code or anything.
@@ -130,63 +130,64 @@ on the stack is the first one you retrieve from it.
 Let’s try a three-deep example:
 
 ```rust
-fn bar() {
+fn italic() {
     let i = 6;
 }
 
-fn foo() {
+fn bold() {
     let a = 5;
     let b = 100;
     let c = 1;
 
-    bar();
+    italic();
 }
 
 fn main() {
     let x = 42;
 
-    foo();
+    bold();
 }
 ```
 
+We have some kooky function names to make the diagrams clearer.
+
 Okay, first, we call `main()`:
 
 | Address | Name | Value |
 |---------|------|-------|
 | 0       | x    | 42    |
 
-Next up, `main()` calls `foo()`:
+Next up, `main()` calls `bold()`:
 
 | Address | Name | Value |
 |---------|------|-------|
-| 3       | c    | 1     |
-| 2       | b    | 100   |
-| 1       | a    | 5     |
+| **3**   | **c**|**1**  |
+| **2**   | **b**|**100**|
+| **1**   | **a**| **5** |
 | 0       | x    | 42    |
 
-And then `foo()` calls `bar()`:
+And then `bold()` calls `italic()`:
 
 | Address | Name | Value |
 |---------|------|-------|
-| 4       | i    | 6     |
-| 3       | c    | 1     |
-| 2       | b    | 100   |
-| 1       | a    | 5     |
+| *4*     | *i*  | *6*   |
+| **3**   | **c**|**1**  |
+| **2**   | **b**|**100**|
+| **1**   | **a**| **5** |
 | 0       | x    | 42    |
-
 Whew! Our stack is growing tall.
 
-After `bar()` is over, its frame is deallocated, leaving just `foo()` and
+After `italic()` is over, its frame is deallocated, leaving only `bold()` and
 `main()`:
 
 | Address | Name | Value |
 |---------|------|-------|
-| 3       | c    | 1     |
-| 2       | b    | 100   |
-| 1       | a    | 5     |
+| **3**   | **c**|**1**  |
+| **2**   | **b**|**100**|
+| **1**   | **a**| **5** |
 | 0       | x    | 42    |
 
-And then `foo()` ends, leaving just `main()`:
+And then `bold()` ends, leaving only `main()`:
 
 | Address | Name | Value |
 |---------|------|-------|
@@ -246,7 +247,7 @@ location we’ve asked for.
 We haven’t really talked too much about what it actually means to allocate and
 deallocate memory in these contexts. Getting into very deep detail is out of
 the scope of this tutorial, but what’s important to point out here is that
-the heap isn’t just a stack that grows from the opposite end. We’ll have an
+the heap isn’t a stack that grows from the opposite end. We’ll have an
 example of this later in the book, but because the heap can be allocated and
 freed in any order, it can end up with ‘holes’. Here’s a diagram of the memory
 layout of a program which has been running for a while now:
@@ -331,13 +332,13 @@ What about when we call `foo()`, passing `y` as an argument?
 | 1       | y    | → 0    |
 | 0       | x    | 5      |
 
-Stack frames aren’t just for local bindings, they’re for arguments too. So in
+Stack frames aren’t only for local bindings, they’re for arguments too. So in
 this case, we need to have both `i`, our argument, and `z`, our local variable
 binding. `i` is a copy of the argument, `y`. Since `y`’s value is `0`, so is
 `i`’s.
 
 This is one reason why borrowing a variable doesn’t deallocate any memory: the
-value of a reference is just a pointer to a memory location. If we got rid of
+value of a reference is a pointer to a memory location. If we got rid of
 the underlying memory, things wouldn’t work very well.
 
 # A complex example
@@ -453,7 +454,7 @@ Next, `foo()` calls `bar()` with `x` and `z`:
 | 0                    | h    | 3                      |
 
 We end up allocating another value on the heap, and so we have to subtract one
-from (2<sup>30</sup>) - 1. It’s easier to just write that than `1,073,741,822`. In any
+from (2<sup>30</sup>) - 1. It’s easier to write that than `1,073,741,822`. In any
 case, we set up the variables as usual.
 
 At the end of `bar()`, it calls `baz()`:
@@ -538,7 +539,7 @@ instead.
 # Which to use?
 
 So if the stack is faster and easier to manage, why do we need the heap? A big
-reason is that Stack-allocation alone means you only have LIFO semantics for
+reason is that Stack-allocation alone means you only have 'Last In First Out (LIFO)' semantics for
 reclaiming storage. Heap-allocation is strictly more general, allowing storage
 to be taken from and returned to the pool in arbitrary order, but at a
 complexity cost.
@@ -549,12 +550,12 @@ has two big impacts: runtime efficiency and semantic impact.
 
 ## Runtime Efficiency
 
-Managing the memory for the stack is trivial: The machine just
+Managing the memory for the stack is trivial: The machine
 increments or decrements a single value, the so-called “stack pointer”.
 Managing memory for the heap is non-trivial: heap-allocated memory is freed at
 arbitrary points, and each block of heap-allocated memory can be of arbitrary
-size, the memory manager must generally work much harder to identify memory for
-reuse.
+size, so the memory manager must generally work much harder to
+identify memory for reuse.
 
 If you’d like to dive into this topic in greater detail, [this paper][wilson]
 is a great introduction.
index 8127b0898c46fe4508d8fcb2cf6f1f0a1f7603d1..1d63435ed5fe71d3d3da89cf7384341082cf3faf 100644 (file)
@@ -272,7 +272,7 @@ made more flexible.
 
 Suppose we’ve got some values that implement `Foo`. The explicit form of
 construction and use of `Foo` trait objects might look a bit like (ignoring the
-type mismatches: they’re all just pointers anyway):
+type mismatches: they’re all pointers anyway):
 
 ```rust,ignore
 let a: String = "foo".to_string();
index f9e3299f9e726c2d4db86c2a6ca1b37fc219ea24..d40689190e7fef4ca038d359983bf11af33ab41c 100644 (file)
@@ -44,8 +44,8 @@ impl HasArea for Circle {
 ```
 
 As you can see, the `trait` block looks very similar to the `impl` block,
-but we don’t define a body, just a type signature. When we `impl` a trait,
-we use `impl Trait for Item`, rather than just `impl Item`.
+but we don’t define a body, only a type signature. When we `impl` a trait,
+we use `impl Trait for Item`, rather than only `impl Item`.
 
 ## Trait bounds on generic functions
 
index 1b223365bd63ac72a15c072fc79da5a5ec3967d9..ecd196a9f0d1fe5b3b79b4f3b54dd7d8df892ed9 100644 (file)
@@ -41,8 +41,8 @@ unsafe impl Scary for i32 {}
 ```
 
 It’s important to be able to explicitly delineate code that may have bugs that
-cause big problems. If a Rust program segfaults, you can be sure it’s somewhere
-in the sections marked `unsafe`.
+cause big problems. If a Rust program segfaults, you can be sure the cause is
+related to something marked `unsafe`.
 
 # What does ‘safe’ mean?
 
@@ -100,7 +100,7 @@ that you normally can not do. Just three. Here they are:
 
 That’s it. It’s important that `unsafe` does not, for example, ‘turn off the
 borrow checker’. Adding `unsafe` to some random Rust code doesn’t change its
-semantics, it won’t just start accepting anything. But it will let you write
+semantics, it won’t start accepting anything. But it will let you write
 things that _do_ break some of the rules.
 
 You will also encounter the `unsafe` keyword when writing bindings to foreign
index b1a2bb5d4172f16ef56e7279ac7ed8dba2d46b3d..73b90355e4f1b0331a89e89f0c10bbdc632d619b 100644 (file)
@@ -11,7 +11,7 @@ Rust understands a few of these types, but they have some restrictions. There
 are three:
 
 1. We can only manipulate an instance of an unsized type via a pointer. An
-   `&[T]` works just fine, but a `[T]` does not.
+   `&[T]` works fine, but a `[T]` does not.
 2. Variables and arguments cannot have dynamically sized types.
 3. Only the last field in a `struct` may have a dynamically sized type; the
    other fields must not. Enum variants must not have dynamically sized types as
index f3a5d1dd886c82b86538372b525f35da1c8ebff7..29b59937a63fa6b4bf3d93dc230abe160102c662 100644 (file)
@@ -2,7 +2,7 @@
 
 Virtually every non-'Hello World’ Rust program uses *variable bindings*. They
 bind some value to a name, so it can be used later. `let` is
-used to introduce a binding, just like this:
+used to introduce a binding, like this:
 
 ```rust
 fn main() {
@@ -18,7 +18,7 @@ function, rather than leaving it off. Otherwise, you’ll get an error.
 
 In many languages, a variable binding would be called a *variable*, but Rust’s
 variable bindings have a few tricks up their sleeves. For example the
-left-hand side of a `let` expression is a ‘[pattern][pattern]’, not just a
+left-hand side of a `let` expression is a ‘[pattern][pattern]’, not a
 variable name. This means we can do things like:
 
 ```rust
@@ -27,7 +27,7 @@ let (x, y) = (1, 2);
 
 After this expression is evaluated, `x` will be one, and `y` will be two.
 Patterns are really powerful, and have [their own section][pattern] in the
-book. We don’t need those features for now, so we’ll just keep this in the back
+book. We don’t need those features for now, so we’ll keep this in the back
 of our minds as we go forward.
 
 [pattern]: patterns.html
@@ -169,10 +169,10 @@ in the middle of a string." We add a comma, and then `x`, to indicate that we
 want `x` to be the value we’re interpolating. The comma is used to separate
 arguments we pass to functions and macros, if you’re passing more than one.
 
-When you just use the curly braces, Rust will attempt to display the value in a
+When you use the curly braces, Rust will attempt to display the value in a
 meaningful way by checking out its type. If you want to specify the format in a
 more detailed manner, there are a [wide number of options available][format].
-For now, we'll just stick to the default: integers aren't very complicated to
+For now, we'll stick to the default: integers aren't very complicated to
 print.
 
 [format]: ../std/fmt/index.html
index 7b826f08ae6fbb34c9ee1ed47b482ef813b3270a..b09735c3feee6b9d12c7816fe06c8adafa6933d0 100644 (file)
@@ -61,6 +61,33 @@ error: aborting due to previous error
 There’s a lot of punctuation in that message, but the core of it makes sense:
 you cannot index with an `i32`.
 
+## Out-of-bounds Access
+
+If you try to access an index that doesn’t exist:
+
+```ignore
+let v = vec![1, 2, 3];
+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'
+```
+
+If you want to handle out-of-bounds errors without panicking, you can use
+methods like [`get`][get] or [`get_mut`][get_mut] that return `None` when
+given an invalid index:
+
+```rust
+let v = vec![1, 2, 3];
+match v.get(7) {
+    Some(x) => println!("Item 7 is {}", x),
+    None => println!("Sorry, this vector is too short.")
+}
+```
+
 ## Iterating
 
 Once you have a vector, you can iterate through its elements with `for`. There
@@ -87,3 +114,6 @@ API documentation][vec].
 
 [vec]: ../std/vec/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
index 36e76896dad44d4fb7e7c62d263777f7ef328be2..f4898dc676530356e86b287c42018a2ad4cd5699 100644 (file)
@@ -1,186 +1,3 @@
 % The Rust Design FAQ
 
-This document describes decisions that were arrived at after lengthy discussion and
-experimenting with alternatives. Please do not propose reversing them unless
-you have a new, extremely compelling argument. Note that this document
-specifically talks about the *language* and not any library or implementation.
-
-A few general guidelines define the philosophy:
-
-- [Memory safety][mem] must never be compromised
-- [Abstraction][abs] should be zero-cost, while still maintaining safety
-- Practicality is key
-
-[mem]: http://en.wikipedia.org/wiki/Memory_safety
-[abs]: http://en.wikipedia.org/wiki/Abstraction_%28computer_science%29
-
-# Semantics
-
-## Data layout is unspecified
-
-In the general case, `enum` and `struct` layout is undefined. This allows the
-compiler to potentially do optimizations like re-using padding for the
-discriminant, compacting variants of nested enums, reordering fields to remove
-padding, etc. `enum`s which carry no data ("C-like") are eligible to have a
-defined representation. Such `enum`s are easily distinguished in that they are
-simply a list of names that carry no data:
-
-```
-enum CLike {
-    A,
-    B = 32,
-    C = 34,
-    D
-}
-```
-
-The [repr attribute][repr] can be applied to such `enum`s to give them the same
-representation as a primitive. This allows using Rust `enum`s in FFI where C
-`enum`s are also used, for most use cases. The attribute can also be applied
-to `struct`s to get the same layout as a C struct would.
-
-[repr]: reference.html#ffi-attributes
-
-## There is no GC
-
-A language that requires a GC is a language that opts into a larger, more
-complex runtime than Rust cares for. Rust is usable on bare metal with no
-extra runtime. Additionally, garbage collection is frequently a source of
-non-deterministic behavior. Rust provides the tools to make using a GC
-possible and even pleasant, but it should not be a requirement for
-implementing the language.
-
-## Non-`Sync` `static mut` is unsafe
-
-Types which are [`Sync`][sync] are thread-safe when multiple shared
-references to them are used concurrently. Types which are not `Sync` are not
-thread-safe, and thus when used in a global require unsafe code to use.
-
-[sync]: core/marker/trait.Sync.html
-
-### If mutable static items that implement `Sync` are safe, why is taking &mut SHARABLE unsafe?
-
-Having multiple aliasing `&mut T`s is never allowed. Due to the nature of
-globals, the borrow checker cannot possibly ensure that a static obeys the
-borrowing rules, so taking a mutable reference to a static is always unsafe.
-
-## There is no life before or after main (no static ctors/dtors)
-
-Globals can not have a non-constant-expression constructor and cannot have a
-destructor at all. This is an opinion of the language. Static constructors are
-undesirable because they can slow down program startup. Life before main is
-often considered a misfeature, never to be used. Rust helps this along by just
-not having the feature.
-
-See [the C++ FQA][fqa]  about the "static initialization order fiasco", and
-[Eric Lippert's blog][elp] for the challenges in C#, which also has this
-feature.
-
-A nice replacement is [lazy_static][lazy_static].
-
-[fqa]: http://yosefk.com/c++fqa/ctors.html#fqa-10.12
-[elp]: http://ericlippert.com/2013/02/06/static-constructors-part-one/
-[lazy_static]: https://crates.io/crates/lazy_static
-
-## The language does not require a runtime
-
-See the above entry on GC. Requiring a runtime limits the utility of the
-language, and makes it undeserving of the title "systems language". All Rust
-code should need to run is a stack.
-
-## `match` must be exhaustive
-
-`match` being exhaustive has some useful properties. First, if every
-possibility is covered by the `match`, adding further variants to the `enum`
-in the future will prompt a compilation failure, rather than runtime panic.
-Second, it makes cost explicit. In general, the only safe way to have a
-non-exhaustive match would be to panic the thread if nothing is matched, though
-it could fall through if the type of the `match` expression is `()`. This sort
-of hidden cost and special casing is against the language's philosophy. It's
-easy to ignore all unspecified cases by using the `_` wildcard:
-
-```rust,ignore
-match val.do_something() {
-    Cat(a) => { /* ... */ }
-    _      => { /* ... */ }
-}
-```
-
-[#3101][iss] is the issue that proposed making this the only behavior, with
-rationale and discussion.
-
-[iss]: https://github.com/rust-lang/rust/issues/3101
-
-## No guaranteed tail-call optimization
-
-In general, tail-call optimization is not guaranteed: see [here][tml] for a
-detailed explanation with references. There is a [proposed extension][tce] that
-would allow tail-call elimination in certain contexts. The compiler is still
-free to optimize tail-calls [when it pleases][sco], however.
-
-[tml]: https://mail.mozilla.org/pipermail/rust-dev/2013-April/003557.html
-[sco]: http://llvm.org/docs/CodeGenerator.html#sibling-call-optimization
-[tce]: https://github.com/rust-lang/rfcs/pull/81
-
-## No constructors
-
-Functions can serve the same purpose as constructors without adding any
-language complexity.
-
-## No copy constructors
-
-Types which implement [`Copy`][copy], will do a standard C-like "shallow copy"
-with no extra work (similar to "plain old data" in C++). It is impossible to
-implement `Copy` types that require custom copy behavior. Instead, in Rust
-"copy constructors" are created by implementing the [`Clone`][clone] trait,
-and explicitly calling the `clone` method. Making user-defined copy operators
-explicit surfaces the underlying complexity, forcing the developer to opt-in
-to potentially expensive operations.
-
-[copy]: core/marker/trait.Copy.html
-[clone]: core/clone/trait.Clone.html
-
-## No move constructors
-
-Values of all types are moved via `memcpy`. This makes writing generic unsafe
-code much simpler since assignment, passing and returning are known to never
-have a side effect like unwinding.
-
-# Syntax
-
-## Macros require balanced delimiters
-
-This is to make the language easier to parse for machines. Since the body of a
-macro can contain arbitrary tokens, some restriction is needed to allow simple
-non-macro-expanding lexers and parsers. This comes in the form of requiring
-that all delimiters be balanced.
-
-## `->` for function return type
-
-This is to make the language easier to parse for humans, especially in the face
-of higher-order functions. `fn foo<T>(f: fn(i32): i32, fn(T): U): U` is not
-particularly easy to read.
-
-## Why is `let` used to introduce variables?
-
-Instead of the term "variable", we use "variable bindings". The
-simplest way for creating a binding is by using the `let` syntax.
-Other ways include `if let`, `while let`, and `match`. Bindings also
-exist in function argument positions.
-
-Bindings always happen in pattern matching positions, and it's also Rust's way
-to declare mutability. One can also re-declare mutability of a binding in
-pattern matching. This is useful to avoid unnecessary `mut` annotations. An
-interesting historical note is that Rust comes, syntactically, most closely
-from ML, which also uses `let` to introduce bindings.
-
-See also [a long thread][alt] on renaming `let mut` to `var`.
-
-[alt]: https://mail.mozilla.org/pipermail/rust-dev/2014-January/008319.html
-
-## Why no `--x` or `x++`?
-
-Preincrement and postincrement, while convenient, are also fairly complex. They
-require knowledge of evaluation order, and often lead to subtle bugs and
-undefined behavior in C and C++. `x = x + 1` or `x += 1` is only slightly
-longer, but unambiguous.
+This content has moved to [the website](https://www.rust-lang.org/).
index 55abebf496dcaeaa6710f82db71507633451e05e..920c6edc389fe8aafdd17f582df8af6aed80cf2e 100644 (file)
@@ -1,177 +1,3 @@
 % The Rust Language FAQ
 
-## Are there any big programs written in it yet? I want to read big samples.
-
-There aren't many large programs yet. The Rust [compiler][rustc], 60,000+ lines at the time of writing, is written in Rust. As the oldest body of Rust code it has gone through many iterations of the language, and some parts are nicer to look at than others. It may not be the best code to learn from, but [borrowck] and [resolve] were written recently.
-
-[rustc]: https://github.com/rust-lang/rust/tree/master/src/librustc
-[resolve]: https://github.com/rust-lang/rust/tree/master/src/librustc_resolve
-[borrowck]: https://github.com/rust-lang/rust/tree/master/src/librustc_borrowck/borrowck
-
-A research browser engine called [Servo][servo], currently 30,000+ lines across more than a dozen crates, will be exercising a lot of Rust's distinctive type-system and concurrency features, and integrating many native libraries.
-
-[servo]: https://github.com/servo/servo
-
-Some examples that demonstrate different aspects of the language:
-
-* [sprocketnes], an NES emulator with no GC, using modern Rust conventions
-* The language's general-purpose [hash] function, SipHash-2-4. Bit twiddling, OO, macros
-* The standard library's [HashMap], a sendable hash map in an OO style
-* The standard library's [json] module. Enums and pattern matching
-
-[sprocketnes]: https://github.com/pcwalton/sprocketnes
-[hash]: https://github.com/rust-lang/rust/tree/master/src/libcore/hash
-[HashMap]: https://github.com/rust-lang/rust/tree/master/src/libstd/collections/hash
-[json]: https://github.com/rust-lang/rust/blob/master/src/libserialize/json.rs
-
-You may also be interested in browsing [trending Rust repositories][github-rust] on GitHub.
-
-[github-rust]: https://github.com/trending?l=rust
-
-## Is anyone using Rust in production?
-
-Yes. For example (incomplete):
-
-* [OpenDNS](http://labs.opendns.com/2013/10/04/zeromq-helping-us-block-malicious-domains/)
-* [Skylight](http://skylight.io)
-* [wit.ai](https://github.com/wit-ai/witd)
-* [Codius](https://codius.org/blog/codius-rust/)
-* [MaidSafe](http://maidsafe.net/)
-* [Terminal.com](https://terminal.com)
-
-## Does it run on Windows?
-
-Yes. All development happens in lockstep on all 3 target platforms (using MinGW, not Cygwin).
-
-## Is it OO? How do I do this thing I normally do in an OO language?
-
-It is multi-paradigm. Not everything is shoe-horned into a single abstraction. Many things you can do in OO languages you can do in Rust, but not everything, and not always using the same abstraction you're accustomed to.
-
-## How do you get away with "no null pointers"?
-
-Data values in the language can only be constructed through a fixed set of initializer forms. Each of those forms requires that its inputs already be initialized. A liveness analysis ensures that local variables are initialized before use.
-
-## What is the relationship between a module and a crate?
-
-* A crate is a top-level compilation unit that corresponds to a single loadable object.
-* A module is a (possibly nested) unit of name-management inside a crate.
-* A crate contains an implicit, un-named top-level module.
-* Recursive definitions can span modules, but not crates.
-* Crates do not have global names, only a set of non-unique metadata tags.
-* There is no global inter-crate namespace; all name management occurs within a crate.
- * Using another crate binds the root of _its_ namespace into the user's namespace.
-
-## Why is panic unwinding non-recoverable within a thread? Why not try to "catch exceptions"?
-
-In short, because too few guarantees could be made about the dynamic environment of the catch block, as well as invariants holding in the unwound heap, to be able to safely resume; we believe that other methods of signalling and logging errors are more appropriate, with threads playing the role of a "hard" isolation boundary between separate heaps.
-
-Rust provides, instead, three predictable and well-defined options for handling any combination of the three main categories of "catch" logic:
-
-* Failure _logging_ is done by the integrated logging subsystem.
-* _Recovery_ after a panic is done by trapping a thread panic from _outside_
-  the thread, where other threads are known to be unaffected.
-* _Cleanup_ of resources is done by RAII-style objects with destructors.
-
-Cleanup through RAII-style destructors is more likely to work than in catch blocks anyways, since it will be better tested (part of the non-error control paths, so executed all the time).
-
-## Why aren't modules type-parametric?
-
-We want to maintain the option to parameterize at runtime. We may eventually change this limitation, but initially this is how type parameters were implemented.
-
-## Why aren't values type-parametric? Why only items?
-
-Doing so would make type inference much more complex, and require the implementation strategy of runtime parameterization.
-
-## Why are enumerations nominal and closed?
-
-We don't know if there's an obvious, easy, efficient, stock-textbook way of supporting open or structural disjoint unions. We prefer to stick to language features that have an obvious and well-explored semantics.
-
-## Why aren't channels synchronous?
-
-There's a lot of debate on this topic; it's easy to find a proponent of default-sync or default-async communication, and there are good reasons for either. Our choice rests on the following arguments:
-
-* Part of the point of isolating threads is to decouple threads from one another, such that assumptions in one thread do not cause undue constraints (or bugs, if violated!) in another. Temporal coupling is as real as any other kind; async-by-default relaxes the default case to only _causal_ coupling.
-* Default-async supports buffering and batching communication, reducing the frequency and severity of thread-switching and inter-thread / inter-domain synchronization.
-* Default-async with transmittable channels is the lowest-level building block on which more-complex synchronization topologies and strategies can be built; it is not clear to us that the majority of cases fit the 2-party full-synchronization pattern rather than some more complex multi-party or multi-stage scenario. We did not want to force all programs to pay for wiring the former assumption into all communications.
-
-## Why are channels half-duplex (one-way)?
-
-Similar to the reasoning about default-sync: it wires fewer assumptions into the implementation, that would have to be paid by all use-cases even if they actually require a more complex communication topology.
-
-## Why are strings UTF-8 by default? Why not UCS2 or UCS4?
-
-The `str` type is UTF-8 because we observe more text in the wild in this encoding – particularly in network transmissions, which are endian-agnostic – and we think it's best that the default treatment of I/O not involve having to recode codepoints in each direction.
-
-This does mean that indexed access to a Unicode codepoint inside a `str` value is an O(n) operation. On the one hand, this is clearly undesirable; on the other hand, this problem is full of trade-offs and we'd like to point a few important qualifications:
-
-* Scanning a `str` for ASCII-range codepoints can still be done safely octet-at-a-time. If you use `.as_bytes()`, pulling out a `u8` costs only O(1) and produces a value that can be cast and compared to an ASCII-range `char`. So if you're (say) line-breaking on `'\n'`, octet-based treatment still works. UTF8 was well-designed this way.
-* Most "character oriented" operations on text only work under very restricted language assumptions sets such as "ASCII-range codepoints only". Outside ASCII-range, you tend to have to use a complex (non-constant-time) algorithm for determining linguistic-unit (glyph, word, paragraph) boundaries anyways. We recommend using an "honest" linguistically-aware, Unicode-approved algorithm.
-* The `char` type is UCS4. If you honestly need to do a codepoint-at-a-time algorithm, it's trivial to write a `type wstr = [char]`, and unpack a `str` into it in a single pass, then work with the `wstr`. In other words: the fact that the language is not "decoding to UCS4 by default" shouldn't stop you from decoding (or re-encoding any other way) if you need to work with that encoding.
-
-## Why are `str`s, slices, arrays etc. built-in types rather than (say) special kinds of trait/impl?
-
-In each case there is one or more operator, literal constructor, overloaded use or integration with a built-in control structure that makes us think it would be awkward to phrase the type in terms of more-general type constructors. Same as, say, with numbers! But this is partly an aesthetic call, and we'd be willing to look at a worked-out proposal for eliminating or rephrasing these special cases.
-
-## Can Rust code call C code?
-
-Yes. Calling C code from Rust is simple and exactly as efficient as calling C code from C.
-
-## Can C code call Rust code?
-
-Yes. The Rust code has to be exposed via an `extern` declaration, which makes it C-ABI compatible. Such a function can be passed to C code as a function pointer or, if given the `#[no_mangle]` attribute to disable symbol mangling, can be called directly from C code.
-
-## Why aren't function signatures inferred? Why only local variables?
-
-* Mechanically, it simplifies the inference algorithm; inference only requires looking at one function at a time.
-* The same simplification goes double for human readers. A reader does not need an IDE running an inference algorithm across an entire crate to be able to guess at a function's argument types; it's always explicit and nearby.
-
-## Why does a type parameter need explicit trait bounds to invoke methods on it, when C++ templates do not?
-
-* Requiring explicit bounds means that the compiler can type-check the code at the point where the type-parametric item is *defined*, rather than delaying to when its type parameters are instantiated.  You know that *any* set of type parameters fulfilling the bounds listed in the API will compile. It's an enforced minimal level of documentation, and results in very clean error messages.
-
-* Scoping of methods is also a problem.  C++ needs [Koenig (argument dependent) lookup](http://en.wikipedia.org/wiki/Argument-dependent_name_lookup), which comes with its own host of problems. Explicit bounds avoid this issue: traits are explicitly imported and then used as bounds on type parameters, so there is a clear mapping from the method to its implementation (via the trait and the instantiated type).
-
-  * Related to the above point: since a parameter explicitly names its trait bounds, a single type is able to implement traits whose sets of method names overlap, cleanly and unambiguously.
-
-* There is further discussion on [this thread on the Rust mailing list](https://mail.mozilla.org/pipermail/rust-dev/2013-September/005603.html).
-
-## Will Rust implement automatic semicolon insertion, like in Go?
-
-For simplicity, we do not plan to do so. Implementing automatic semicolon insertion for Rust would be tricky because the absence of a trailing semicolon means "return a value".
-
-## How do I get my program to display the output of logging macros?
-
-**Short Answer**: Set the `RUST_LOG` environment variable to the name of your source file, sans extension.
-
-```sh
-rustc hello.rs
-export RUST_LOG=hello
-./hello
-```
-
-**Long Answer**: `RUST_LOG` takes a 'logging spec' that consists of a
-comma-separated list of paths, where a path consists of the crate name and
-sequence of module names, each separated by double-colons. For standalone `.rs`
-files, the crate is implicitly named after the source file, so in the above
-example we were setting `RUST_LOG` to the name of the hello crate. Multiple paths
-can be combined to control the exact logging you want to see. For example, when
-debugging linking in the compiler, you might set the following:
-
-```sh
-RUST_LOG=rustc_metadata::creader,rustc::util::filesearch,rustc::back::rpath
-```
-
-For a full description, see [the logging crate][1].
-
-## How fast is Rust?
-
-As always, this question is difficult to answer. There's still a lot of work to
-do on speed, and depending on what you're benchmarking, Rust has variable
-performance.
-
-That said, it is an explicit goal of Rust to be as fast as C++ for most things.
-Language decisions are made with performance in mind, and we want Rust to be as
-fast as possible. Given that Rust is built on top of LLVM, any performance
-improvements in it also help Rust become faster.
-
-[1]:log/index.html
+This content has moved to [the website](https://www.rust-lang.org/).
index 1ed961fd558674ef677bfff181b4e4594ce2751c..b44de8e2cb32d3cd72213bcb4228870f1edcf0dc 100644 (file)
@@ -1,42 +1,3 @@
 % The Rust Project FAQ
 
-# What is this project's goal, in one sentence?
-
-To design and implement a safe, concurrent, practical, static systems language.
-
-# Why are you doing this?
-
-Existing languages at this level of abstraction and efficiency are unsatisfactory. In particular:
-
-* Too little attention paid to safety.
-* Poor concurrency support.
-* Lack of practical affordances, too dogmatic about paradigm.
-
-# What are some non-goals?
-
-* To employ any particularly cutting-edge technologies. Old, established techniques are better.
-* To prize expressiveness, minimalism or elegance above other goals. These are desirable but subordinate goals.
-* To cover the complete feature-set of C++, or any other language. It should provide majority-case features.
-* To be 100% static, 100% safe, 100% reflective, or too dogmatic in any other sense. Trade-offs exist.
-* To run on "every possible platform". It must eventually work without unnecessary compromises on widely-used hardware and software platforms.
-
-# Is any part of this thing production-ready?
-
-Yes!
-
-# Is this a completely Mozilla-planned and orchestrated thing?
-
-No. It started as a Graydon Hoare's part-time side project in 2006 and remained so for over 3 years. Mozilla got involved in 2009 once the language was mature enough to run some basic tests and demonstrate the idea. Though it is sponsored by Mozilla, Rust is developed by a diverse community of enthusiasts.
-
-# What will Mozilla use Rust for?
-
-Mozilla intends to use Rust as a platform for prototyping experimental browser architectures. Specifically, the hope is to develop a browser that is more amenable to parallelization than existing ones, while also being less prone to common C++ coding errors that result in security exploits. The name of that project is _[Servo](http://github.com/servo/servo)_.
-
-# Why a BSD-style permissive license rather than MPL or tri-license?
-
-* Partly due to preference of the original developer (Graydon).
-* Partly due to the fact that languages tend to have a wider audience and more diverse set of possible embeddings and end-uses than focused, coherent products such as web browsers. We'd like to appeal to as many of those potential contributors as possible.
-
-# Why dual MIT/ASL2 license?
-
-The Apache license includes important protection against patent aggression, but it is not compatible with the GPL, version 2. To avoid problems using Rust with GPL2, it is alternately MIT licensed.
+This content has moved to [the website](https://www.rust-lang.org/).
index 5f2ef6107298cde6bc8ab91f4532cd2fe4502b81..f8a1ec134d9242faa877a156868a53dfc5efa110 100644 (file)
@@ -1,72 +1,37 @@
 % Rust Documentation
 
-Welcome to the Rust documentation! You can use the section headings above
-to jump to any particular section.
+<style>
+nav {
+    display: none;
+}
+</style>
 
-# Getting Started
+This is an index of the documentation included with the Rust
+compiler. For more comprehensive documentation see [the
+website](https://www.rust-lang.org).
 
-If you haven't seen Rust at all yet, the first thing you should read is the
-introduction to [The Rust Programming Language](book/index.html). It'll give
-you a good idea of what Rust is like.
+[**The Rust Programming Language**][book]. Also known as "The Book",
+The Rust Programming Language is the most comprehensive resource for
+all topics related to Rust, and is the primary official document of
+the language.
 
-The book provides a lengthy explanation of Rust, its syntax, and its
-concepts. Upon completing the book, you'll be an intermediate Rust
-developer, and will have a good grasp of the fundamental ideas behind
-Rust.
+[**The Rust Reference**][ref]. While Rust does not have a
+specification, the reference tries to describe its working in
+detail. It tends to be out of date.
 
-[Rust By Example][rbe] teaches you Rust through a series of small
-examples.
+[**Standard Library API Reference**][api]. Documentation for the
+standard library.
 
-[rbe]: http://rustbyexample.com/
+[**The Rustonomicon**][nomicon]. An entire book dedicated to
+explaining how to write unsafe Rust code. It is for advanced Rust
+programmers.
 
-# Language Reference
+[**Compiler Error Index**][err]. Extended explanations of
+the errors produced by the Rust compiler.
 
-Rust does not have an exact specification yet, but an effort to describe as much of
-the language in as much detail as possible is in [the reference](reference.html).
+[book]: book/index.html
+[ref]: reference.html
+[api]: std/index.html
+[nomicon]: nomicon/index.html
+[err]: error-index.html
 
-# Standard Library Reference
-
-We have [API documentation for the entire standard
-library](std/index.html). There's a list of crates on the left with more
-specific sections, or you can use the search bar at the top to search for
-something if you know its name.
-
-# The Rustonomicon
-
-[The Rustonomicon] is an entire book dedicated to explaining
-how to write `unsafe` Rust code. It is for advanced Rust programmers.
-
-[The Rustonomicon]: nomicon/index.html
-
-# Tools
-
-[Cargo](http://doc.crates.io/index.html) is the Rust package manager providing access to libraries
-beyond the standard one, and its website contains lots of good documentation.
-
-[`rustdoc`](book/documentation.html) is the Rust's documentation generator, a tool converting
-annotated source code into HTML docs.
-
-# FAQs
-
-There are questions that are asked quite often, so we've made FAQs for them:
-
-* [Language Design FAQ](complement-design-faq.html)
-* [Language FAQ](complement-lang-faq.html)
-* [Project FAQ](complement-project-faq.html)
-* [How to submit a bug report](https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports)
-
-# The Error Index
-
-If you encounter an error while compiling your code you may be able to look it
-up in the [Rust Compiler Error Index](error-index.html).
-
-# Community Translations
-
-Several projects have been started to translate the documentation into other
-languages:
-
-- [Russian](https://github.com/kgv/rust_book_ru)
-- [Korean](https://github.com/rust-kr/doc.rust-kr.org)
-- [Chinese](https://github.com/KaiserY/rust-book-chinese)
-- [Spanish](https://goyox86.github.io/elpr)
-- [German](https://panicbit.github.io/rustbook-de)
index 41014f46dd953827374f55791adf5f7d0ed7b135..bcd93a58d859a96d8807f7bd8b10baf6af1264a8 100644 (file)
@@ -55,8 +55,8 @@ fn frob(s: &str, t: &str) -> &str;                      // ILLEGAL
 fn get_mut(&mut self) -> &mut T;                        // elided
 fn get_mut<'a>(&'a mut self) -> &'a mut T;              // expanded
 
-fn args<T:ToCStr>(&mut self, args: &[T]) -> &mut Command                  // elided
-fn args<'a, 'b, T:ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command // expanded
+fn args<T: ToCStr>(&mut self, args: &[T]) -> &mut Command                  // elided
+fn args<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command // expanded
 
 fn new(buf: &mut [u8]) -> BufWriter;                    // elided
 fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a>          // expanded
index e21207efdcf9d9f033b147100a3a16a25358d033..45eb68baeb732827d6a54751ed22e38e189b2d27 100644 (file)
@@ -107,8 +107,8 @@ This signature of `as_str` takes a reference to a u32 with *some* lifetime, and
 promises that it can produce a reference to a str that can live *just as long*.
 Already we can see why this signature might be trouble. That basically implies
 that we're going to find a str somewhere in the scope the reference
-to the u32 originated in, or somewhere *even earlier*. That's a bit of a big
-ask.
+to the u32 originated in, or somewhere *even earlier*. That's a bit of a tall
+order.
 
 We then proceed to compute the string `s`, and return a reference to it. Since
 the contract of our function says the reference must outlive `'a`, that's the
index 65dcb6a94e13ccbf9aec33415fc638525202f9b9..5def5c3903353ef85575993ccaa6c0173db1b316 100644 (file)
@@ -44,10 +44,11 @@ subtyping of its outputs. There are two kinds of variance in Rust:
 * F is *invariant* over `T` otherwise (no subtyping relation can be derived)
 
 (For those of you who are familiar with variance from other languages, what we
-refer to as "just" variance is in fact *covariance*. Rust does not have
-contravariance. Historically Rust did have some contravariance but it was
-scrapped due to poor interactions with other features. If you experience
-contravariance in Rust call your local compiler developer for medical advice.)
+refer to as "just" variance is in fact *covariance*. Rust has *contravariance*
+for functions. The future of contravariance is uncertain and it may be
+scrapped. For now, `fn(T)` is contravariant in `T`, which is used in matching
+methods in trait implementations to the trait definition. Traits don't have
+inferred variance, so `Fn(T)` is invariant in `T`).
 
 Some important variances:
 
@@ -200,7 +201,7 @@ use std::cell::Cell;
 
 struct Foo<'a, 'b, A: 'a, B: 'b, C, D, E, F, G, H> {
     a: &'a A,     // variant over 'a and A
-    b: &'b mut B, // invariant over 'b and B
+    b: &'b mut B, // variant over 'b and invariant over B
     c: *const C,  // variant over C
     d: *mut D,    // invariant over D
     e: Vec<E>,    // variant over E
index b767caa4912364e9ae52e7c543f8d3f625409d58..706fe680e006669ba82cf7a5da34b79e28d528c6 100644 (file)
@@ -21,7 +21,7 @@ impl<T> Drop for Vec<T> {
             let elem_size = mem::size_of::<T>();
             let num_bytes = elem_size * self.cap;
             unsafe {
-                heap::deallocate(*self.ptr, num_bytes, align);
+                heap::deallocate(*self.ptr as *mut _, num_bytes, align);
             }
         }
     }
index 52c22f65076f9def84f83bc2e57955e1bf49ea19..1f4377a7ca335d380ee7bad8f75f763a7c71ca85 100644 (file)
@@ -226,7 +226,11 @@ impl<T> Iterator for RawValIter<T> {
         } else {
             unsafe {
                 let result = ptr::read(self.start);
-                self.start = self.start.offset(1);
+                self.start = if mem::size_of::<T>() == 0 {
+                    (self.start as usize + 1) as *const _
+                } else {
+                    self.start.offset(1)
+                };
                 Some(result)
             }
         }
@@ -246,7 +250,11 @@ impl<T> DoubleEndedIterator for RawValIter<T> {
             None
         } else {
             unsafe {
-                self.end = self.end.offset(-1);
+                self.end = if mem::size_of::<T>() == 0 {
+                    (self.end as usize - 1) as *const _
+                } else {
+                    self.end.offset(-1)
+                };
                 Some(ptr::read(self.end))
             }
         }
index 0a37170c52ca34bf39bef5bd96a80dbec6492f1f..bcecd78a1b75a1227b5dbc2c6aeb53dc13612ae9 100644 (file)
@@ -24,7 +24,7 @@ pub fn insert(&mut self, index: usize, elem: T) {
             // ptr::copy(src, dest, len): "copy from source to dest len elems"
             ptr::copy(self.ptr.offset(index as isize),
                       self.ptr.offset(index as isize + 1),
-                      len - index);
+                      self.len - index);
         }
         ptr::write(self.ptr.offset(index as isize), elem);
         self.len += 1;
@@ -44,7 +44,7 @@ pub fn remove(&mut self, index: usize) -> T {
         let result = ptr::read(self.ptr.offset(index as isize));
         ptr::copy(self.ptr.offset(index as isize + 1),
                   self.ptr.offset(index as isize),
-                  len - index);
+                  self.len - index);
         result
     }
 }
index fb337a891a8d42ddb50ac5ed224d931b2fee71ae..5f3b2a81364e196b6e0454a3cf79a593d5e98f03 100644 (file)
@@ -140,8 +140,8 @@ impl<T> Iterator for RawValIter<T> {
                 self.start = if mem::size_of::<T>() == 0 {
                     (self.start as usize + 1) as *const _
                 } else {
-                    self.start.offset(1);
-                }
+                    self.start.offset(1)
+                };
                 Some(result)
             }
         }
@@ -164,8 +164,8 @@ impl<T> DoubleEndedIterator for RawValIter<T> {
                 self.end = if mem::size_of::<T>() == 0 {
                     (self.end as usize - 1) as *const _
                 } else {
-                    self.end.offset(-1);
-                }
+                    self.end.offset(-1)
+                };
                 Some(ptr::read(self.end))
             }
         }
index 0262ff5a71aadc65a1d313950362a9ded50c800c..f0fdae27ac7fb33ccd4728db2f1277ba0290dad5 100644 (file)
@@ -208,10 +208,10 @@ A _string literal_ is a sequence of any Unicode characters enclosed within two
 which must be _escaped_ by a preceding `U+005C` character (`\`).
 
 Line-break characters are allowed in string literals. Normally they represent
-themselves (i.e. no translation), but as a special exception, when a `U+005C`
-character (`\`) occurs immediately before the newline, the `U+005C` character,
-the newline, and all whitespace at the beginning of the next line are ignored.
-Thus `a` and `b` are equal:
+themselves (i.e. no translation), but as a special exception, when an unescaped
+`U+005C` character (`\`) occurs immediately before the newline (`U+000A`), the
+`U+005C` character, the newline, and all whitespace at the beginning of the
+next line are ignored. Thus `a` and `b` are equal:
 
 ```rust
 let a = "foobar";
@@ -2044,7 +2044,7 @@ The following configurations must be defined by the implementation:
   production.  For example, it controls the behavior of the standard library's
   `debug_assert!` macro.
 * `target_arch = "..."` - Target CPU architecture, such as `"x86"`, `"x86_64"`
-  `"mips"`, `"powerpc"`, `"arm"`, or `"aarch64"`.
+  `"mips"`, `"powerpc"`, `"powerpc64"`, `"powerpc64le"`, `"arm"`, or `"aarch64"`.
 * `target_endian = "..."` - Endianness of the target CPU, either `"little"` or
   `"big"`.
 * `target_env = ".."` - An option provided by the compiler by default
@@ -2372,10 +2372,6 @@ The currently implemented features of the reference compiler are:
                    Such items should not be allowed by the compiler to exist,
                    so if you need this there probably is a compiler bug.
 
-* `visible_private_types` - Allows public APIs to expose otherwise private
-                            types, e.g. as the return type of a public function.
-                            This capability may be removed in the future.
-
 * `allow_internal_unstable` - Allows `macro_rules!` macros to be tagged with the
                               `#[allow_internal_unstable]` attribute, designed
                               to allow `std` macros to call
@@ -2390,6 +2386,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_();`)
+
 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
@@ -3677,10 +3680,10 @@ sites are:
 
 * `let` statements where an explicit type is given.
 
-   For example, `128` is coerced to have type `i8` in the following:
+   For example, `42` is coerced to have type `i8` in the following:
 
    ```rust
-   let _: i8 = 128;
+   let _: i8 = 42;
    ```
 
 * `static` and `const` statements (similar to `let` statements).
@@ -3690,36 +3693,36 @@ sites are:
   The value being coerced is the actual parameter, and it is coerced to
   the type of the formal parameter.
 
-  For example, `128` is coerced to have type `i8` in the following:
+  For example, `42` is coerced to have type `i8` in the following:
 
   ```rust
   fn bar(_: i8) { }
 
   fn main() {
-      bar(128);
+      bar(42);
   }
   ```
 
 * Instantiations of struct or variant fields
 
-  For example, `128` is coerced to have type `i8` in the following:
+  For example, `42` is coerced to have type `i8` in the following:
 
   ```rust
   struct Foo { x: i8 }
 
   fn main() {
-      Foo { x: 128 };
+      Foo { x: 42 };
   }
   ```
 
 * Function results, either the final line of a block if it is not
   semicolon-terminated or any expression in a `return` statement
 
-  For example, `128` is coerced to have type `i8` in the following:
+  For example, `42` is coerced to have type `i8` in the following:
 
   ```rust
   fn foo() -> i8 {
-      128
+      42
   }
   ```
 
index 866578930a3d8dca0f83d6ce285bc32e0913914d..50e30caa2b34b0fa58700b81091da0fe28185b93 100644 (file)
@@ -1,17 +1,11 @@
 {
    osx-frameworks.rs-fails-otherwise-1
    Memcheck:Leak
-   match-leak-kinds: possible
+   match-leak-kinds: definite,possible
    fun:malloc
    ...
    fun:__CFInitialize
-   fun:_ZN16ImageLoaderMachO11doImageInitERKN11ImageLoader11LinkContextE
-   fun:_ZN16ImageLoaderMachO16doInitializationERKN11ImageLoader11LinkContextE
-   fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
-   fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
-   fun:_ZN11ImageLoader19processInitializersERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
-   fun:_ZN11ImageLoader15runInitializersERKNS_11LinkContextERNS_21InitializerTimingListE
-   fun:_ZN4dyld24initializeMainExecutableEv
+   ...
 }
 
 {
    ...
    fun:__CFInitialize
    fun:_ZN16ImageLoaderMachO11doImageInitERKN11ImageLoader11LinkContextE
-   fun:_ZN16ImageLoaderMachO16doInitializationERKN11ImageLoader11LinkContextE
-   fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
-   fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
-   fun:_ZN11ImageLoader19processInitializersERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
 }
 
 {
    Memcheck:Leak
    match-leak-kinds: possible
    fun:realloc
-   fun:_ZL12realizeClassP10objc_class
-   fun:_ZL12realizeClassP10objc_class
-   fun:_ZN13list_array_ttIm15protocol_list_tE11attachListsEPKPS0_j
+   ...
    fun:_read_images
    fun:map_images_nolock
-   fun:map_2_images
+   ...
    fun:_ZN4dyldL18notifyBatchPartialE17dyld_image_statesbPFPKcS0_jPK15dyld_image_infoE
    fun:_ZN4dyld36registerImageStateBatchChangeHandlerE17dyld_image_statesPFPKcS0_jPK15dyld_image_infoE
    fun:dyld_register_image_state_change_handler
@@ -49,7 +37,7 @@
 {
    osx-frameworks.rs-fails-otherwise-4
    Memcheck:Leak
-   match-leak-kinds: possible
+   match-leak-kinds: definite,possible
    fun:calloc
    ...
    fun:__CFInitialize
 {
    osx-frameworks.rs-fails-otherwise-5
    Memcheck:Leak
-   match-leak-kinds: definite
-   fun:calloc
+   match-leak-kinds: definite,possible
+   fun:malloc_zone_malloc
    ...
    fun:__CFInitialize
-   fun:_ZN16ImageLoaderMachO11doImageInitERKN11ImageLoader11LinkContextE
-   fun:_ZN16ImageLoaderMachO16doInitializationERKN11ImageLoader11LinkContextE
-   fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
-}
-
-{
-   osx-frameworks.rs-fails-otherwise-6
-   Memcheck:Leak
-   match-leak-kinds: definite
-   fun:malloc
-   fun:strdup
-   fun:_CFProcessPath
-   fun:__CFInitialize
-   fun:_ZN16ImageLoaderMachO11doImageInitERKN11ImageLoader11LinkContextE
-   fun:_ZN16ImageLoaderMachO16doInitializationERKN11ImageLoader11LinkContextE
-   fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
-   fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
-   fun:_ZN11ImageLoader19processInitializersERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
-   fun:_ZN11ImageLoader15runInitializersERKNS_11LinkContextERNS_21InitializerTimingListE
-   fun:_ZN4dyld24initializeMainExecutableEv
-   fun:_ZN4dyld5_mainEPK12macho_headermiPPKcS5_S5_Pm
+   ...
 }
 
 {
-   osx-frameworks.rs-fails-otherwise-7
+   fails-since-xcode-7.2
    Memcheck:Leak
-   match-leak-kinds: definite
+   match-leak-kinds: possible
    fun:malloc_zone_malloc
-   ...
-   fun:__CFInitialize
-   fun:_ZN16ImageLoaderMachO11doImageInitERKN11ImageLoader11LinkContextE
-   fun:_ZN16ImageLoaderMachO16doInitializationERKN11ImageLoader11LinkContextE
-   fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
-   fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
-   fun:_ZN11ImageLoader19processInitializersERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE
-   fun:_ZN11ImageLoader15runInitializersERKNS_11LinkContextERNS_21InitializerTimingListE
-   fun:_ZN4dyld24initializeMainExecutableEv
+   fun:_objc_copyClassNamesForImage
+   fun:_ZL9protocolsv
+   fun:_Z9readClassP10objc_classbb
+   fun:gc_init
+   fun:_ZL33objc_initializeClassPair_internalP10objc_classPKcS0_S0_
+   fun:layout_string_create
+   fun:_ZL12realizeClassP10objc_class
+   fun:_ZL22copySwiftV1MangledNamePKcb
+   fun:_ZL22copySwiftV1MangledNamePKcb
+   fun:_ZL22copySwiftV1MangledNamePKcb
+   fun:_ZL22copySwiftV1MangledNamePKcb
 }
index b0140fb24559dc0eca78542ec0b5c9ee6b8ff652..9fdab1fcfca28a33cd35d67731af7a28b40f0f72 100644 (file)
@@ -25,6 +25,7 @@ even larger, and it's already uncomfortably large (6 KiB).
 """
 from __future__ import print_function
 import sys
+from math import ceil, log
 from fractions import Fraction
 from collections import namedtuple
 
@@ -33,7 +34,6 @@ N = 64  # Size of the significand field in bits
 MIN_SIG = 2 ** (N - 1)
 MAX_SIG = (2 ** N) - 1
 
-
 # Hand-rolled fp representation without arithmetic or any other operations.
 # The significand is normalized and always N bit, but the exponent is
 # unrestricted in range.
@@ -92,7 +92,7 @@ def error(f, e, z):
     ulp_err = abs_err / Fraction(2) ** z.exp
     return float(ulp_err)
 
-LICENSE = """
+HEADER = """
 // 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.
@@ -102,9 +102,23 @@ LICENSE = """
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
+
+//! Tables of approximations of powers of ten.
+//! DO NOT MODIFY: Generated by `src/etc/dec2flt_table.py`
 """
 
+
 def main():
+    print(HEADER.strip())
+    print()
+    print_proper_powers()
+    print()
+    print_short_powers(32, 24)
+    print()
+    print_short_powers(64, 53)
+
+
+def print_proper_powers():
     MIN_E = -305
     MAX_E = 305
     e_range = range(MIN_E, MAX_E+1)
@@ -114,13 +128,10 @@ def main():
         err = error(1, e, z)
         assert err < 0.5
         powers.append(z)
-    typ = "([u64; {0}], [i16; {0}])".format(len(e_range))
-    print(LICENSE.strip())
-    print("// Table of approximations of powers of ten.")
-    print("// DO NOT MODIFY: Generated by a src/etc/dec2flt_table.py")
     print("pub const MIN_E: i16 = {};".format(MIN_E))
     print("pub const MAX_E: i16 = {};".format(MAX_E))
     print()
+    typ = "([u64; {0}], [i16; {0}])".format(len(powers))
     print("pub const POWERS: ", typ, " = ([", sep='')
     for z in powers:
         print("    0x{:x},".format(z.sig))
@@ -130,5 +141,17 @@ def main():
     print("]);")
 
 
+def print_short_powers(num_bits, significand_size):
+    max_sig = 2**significand_size - 1
+    # The fast path bails out for exponents >= ceil(log5(max_sig))
+    max_e = int(ceil(log(max_sig, 5)))
+    e_range = range(max_e)
+    typ = "[f{}; {}]".format(num_bits, len(e_range))
+    print("pub const F", num_bits, "_SHORT_POWERS: ", typ, " = [", sep='')
+    for e in e_range:
+        print("    1e{},".format(e))
+    print("];")
+
+
 if __name__ == '__main__':
     main()
index 2acee8a97f59f344a57f65f3b7fac89f0bf79d36..8362c239b655d0b975eda8ab7bc008a14bcb66cf 100644 (file)
@@ -104,6 +104,7 @@ checks if the given file does not exist, for example.
 
 """
 
+from __future__ import print_function
 import sys
 import os.path
 import re
@@ -160,8 +161,13 @@ class CustomHTMLParser(HTMLParser):
         HTMLParser.close(self)
         return self.__builder.close()
 
-Command = namedtuple('Command', 'negated cmd args lineno')
+Command = namedtuple('Command', 'negated cmd args lineno context')
 
+class FailedCheck(Exception):
+    pass
+
+class InvalidCheck(Exception):
+    pass
 
 def concat_multi_lines(f):
     """returns a generator out of the file object, which
@@ -196,7 +202,7 @@ def concat_multi_lines(f):
             catenated = ''
 
     if lastline is not None:
-        raise RuntimeError('Trailing backslash in the end of file')
+        print_err(lineno, line, 'Trailing backslash at the end of the file')
 
 LINE_PATTERN = re.compile(r'''
     (?<=(?<!\S)@)(?P<negated>!?)
@@ -216,9 +222,10 @@ def get_commands(template):
             cmd = m.group('cmd')
             args = m.group('args')
             if args and not args[:1].isspace():
-                raise RuntimeError('Invalid template syntax at line {}'.format(lineno+1))
+                print_err(lineno, line, 'Invalid template syntax')
+                continue
             args = shlex.split(args)
-            yield Command(negated=negated, cmd=cmd, args=args, lineno=lineno+1)
+            yield Command(negated=negated, cmd=cmd, args=args, lineno=lineno+1, context=line)
 
 
 def _flatten(node, acc):
@@ -242,8 +249,7 @@ def normalize_xpath(path):
     elif path.startswith('.//'):
         return path
     else:
-        raise RuntimeError('Non-absolute XPath is not supported due to \
-                            the implementation issue.')
+        raise InvalidCheck('Non-absolute XPath is not supported due to implementation issues')
 
 
 class CachedFiles(object):
@@ -259,41 +265,40 @@ class CachedFiles(object):
             self.last_path = path
             return path
         elif self.last_path is None:
-            raise RuntimeError('Tried to use the previous path in the first command')
+            raise InvalidCheck('Tried to use the previous path in the first command')
         else:
             return self.last_path
 
     def get_file(self, path):
         path = self.resolve_path(path)
-        try:
+        if path in self.files:
             return self.files[path]
-        except KeyError:
-            try:
-                with open(os.path.join(self.root, path)) as f:
-                    data = f.read()
-            except Exception as e:
-                raise RuntimeError('Cannot open file {!r}: {}'.format(path, e))
-            else:
-                self.files[path] = data
-                return data
+
+        abspath = os.path.join(self.root, path)
+        if not(os.path.exists(abspath) and os.path.isfile(abspath)):
+            raise FailedCheck('File does not exist {!r}'.format(path))
+
+        with open(abspath) as f:
+            data = f.read()
+            self.files[path] = data
+            return data
 
     def get_tree(self, path):
         path = self.resolve_path(path)
-        try:
+        if path in self.trees:
             return self.trees[path]
-        except KeyError:
-            try:
-                f = open(os.path.join(self.root, path))
-            except Exception as e:
-                raise RuntimeError('Cannot open file {!r}: {}'.format(path, e))
+
+        abspath = os.path.join(self.root, path)
+        if not(os.path.exists(abspath) and os.path.isfile(abspath)):
+            raise FailedCheck('File does not exist {!r}'.format(path))
+
+        with open(abspath) as f:
             try:
-                with f:
-                    tree = ET.parse(f, CustomHTMLParser())
+                tree = ET.parse(f, CustomHTMLParser())
             except Exception as e:
                 raise RuntimeError('Cannot parse an HTML file {!r}: {}'.format(path, e))
-            else:
-                self.trees[path] = tree
-                return self.trees[path]
+            self.trees[path] = tree
+            return self.trees[path]
 
 
 def check_string(data, pat, regexp):
@@ -311,14 +316,14 @@ def check_tree_attr(tree, path, attr, pat, regexp):
     path = normalize_xpath(path)
     ret = False
     for e in tree.findall(path):
-        try:
+        if attr in e.attrib:
             value = e.attrib[attr]
-        except KeyError:
-            continue
         else:
-            ret = check_string(value, pat, regexp)
-            if ret:
-                break
+            continue
+
+        ret = check_string(value, pat, regexp)
+        if ret:
+            break
     return ret
 
 
@@ -341,57 +346,84 @@ def check_tree_count(tree, path, count):
     path = normalize_xpath(path)
     return len(tree.findall(path)) == count
 
+def stderr(*args):
+    print(*args, file=sys.stderr)
 
-def check(target, commands):
-    cache = CachedFiles(target)
-    for c in commands:
+def print_err(lineno, context, err, message=None):
+    global ERR_COUNT
+    ERR_COUNT += 1
+    stderr("{}: {}".format(lineno, message or err))
+    if message and err:
+        stderr("\t{}".format(err))
+
+    if context:
+        stderr("\t{}".format(context))
+
+ERR_COUNT = 0
+
+def check_command(c, cache):
+    try:
+        cerr = ""
         if c.cmd == 'has' or c.cmd == 'matches': # string test
             regexp = (c.cmd == 'matches')
             if len(c.args) == 1 and not regexp: # @has <path> = file existence
                 try:
                     cache.get_file(c.args[0])
                     ret = True
-                except RuntimeError:
+                except FailedCheck as err:
+                    cerr = err.message
                     ret = False
             elif len(c.args) == 2: # @has/matches <path> <pat> = string test
+                cerr = "`PATTERN` did not match"
                 ret = check_string(cache.get_file(c.args[0]), c.args[1], regexp)
             elif len(c.args) == 3: # @has/matches <path> <pat> <match> = XML tree test
+                cerr = "`XPATH PATTERN` did not match"
                 tree = cache.get_tree(c.args[0])
                 pat, sep, attr = c.args[1].partition('/@')
                 if sep: # attribute
-                    ret = check_tree_attr(cache.get_tree(c.args[0]), pat, attr, c.args[2], regexp)
+                    tree = cache.get_tree(c.args[0])
+                    ret = check_tree_attr(tree, pat, attr, c.args[2], regexp)
                 else: # normalized text
                     pat = c.args[1]
                     if pat.endswith('/text()'):
                         pat = pat[:-7]
                     ret = check_tree_text(cache.get_tree(c.args[0]), pat, c.args[2], regexp)
             else:
-                raise RuntimeError('Invalid number of @{} arguments \
-                                    at line {}'.format(c.cmd, c.lineno))
+                raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
 
         elif c.cmd == 'count': # count test
             if len(c.args) == 3: # @count <path> <pat> <count> = count test
                 ret = check_tree_count(cache.get_tree(c.args[0]), c.args[1], int(c.args[2]))
             else:
-                raise RuntimeError('Invalid number of @{} arguments \
-                                    at line {}'.format(c.cmd, c.lineno))
-
+                raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
         elif c.cmd == 'valid-html':
-            raise RuntimeError('Unimplemented @valid-html at line {}'.format(c.lineno))
+            raise InvalidCheck('Unimplemented @valid-html')
 
         elif c.cmd == 'valid-links':
-            raise RuntimeError('Unimplemented @valid-links at line {}'.format(c.lineno))
-
+            raise InvalidCheck('Unimplemented @valid-links')
         else:
-            raise RuntimeError('Unrecognized @{} at line {}'.format(c.cmd, c.lineno))
+            raise InvalidCheck('Unrecognized @{}'.format(c.cmd))
 
         if ret == c.negated:
-            raise RuntimeError('@{}{} check failed at line {}'.format('!' if c.negated else '',
-                                                                      c.cmd, c.lineno))
+            raise FailedCheck(cerr)
+
+    except FailedCheck as err:
+        message = '@{}{} check failed'.format('!' if c.negated else '', c.cmd)
+        print_err(c.lineno, c.context, err.message, message)
+    except InvalidCheck as err:
+        print_err(c.lineno, c.context, err.message)
+
+def check(target, commands):
+    cache = CachedFiles(target)
+    for c in commands:
+        check_command(c, cache)
 
 if __name__ == '__main__':
-    if len(sys.argv) < 3:
-        print >>sys.stderr, 'Usage: {} <doc dir> <template>'.format(sys.argv[0])
+    if len(sys.argv) != 3:
+        stderr('Usage: {} <doc dir> <template>'.format(sys.argv[0]))
+        raise SystemExit(1)
+
+    check(sys.argv[1], get_commands(sys.argv[2]))
+    if ERR_COUNT:
+        stderr("\nEncountered {} errors".format(ERR_COUNT))
         raise SystemExit(1)
-    else:
-        check(sys.argv[1], get_commands(sys.argv[2]))
index c2958caddc40098e22d677c9e282fe73a74f0c83..34c2cdbef353869d84c96b65529258da36835ace 100644 (file)
@@ -53,6 +53,8 @@ putenv('HOST_RPATH_DIR', os.path.abspath(sys.argv[10]))
 putenv('TARGET_RPATH_DIR', os.path.abspath(sys.argv[11]))
 putenv('RUST_BUILD_STAGE', sys.argv[12])
 putenv('S', os.path.abspath(sys.argv[13]))
+putenv('RUSTFLAGS', sys.argv[15])
+putenv('LLVM_COMPONENTS', sys.argv[16])
 putenv('PYTHON', sys.executable)
 os.putenv('TARGET', target_triple)
 
index 215fa1cdd1bb757ac0bb06377a92ebf8a29ec182..8381e4f704097a3982761a1382e5d118bdbe2050 100644 (file)
@@ -18,6 +18,7 @@ components = sys.argv[2].split() # splits on whitespace
 enable_static = sys.argv[3]
 llvm_config = sys.argv[4]
 stdcpp_name = sys.argv[5]
+use_libcpp = sys.argv[6]
 
 f.write("""// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
@@ -44,11 +45,25 @@ def run(args):
         sys.exit(1)
     return out
 
+def runErr(args):
+    proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    out, err = proc.communicate()
+
+    if err:
+        return False, out
+    else:
+        return True, out
+
 f.write("\n")
 
+args = [llvm_config, '--shared-mode']
+args.extend(components)
+llvm_shared, out = runErr(args)
+if llvm_shared:
+    llvm_shared = 'shared' in out
+
 # LLVM libs
 args = [llvm_config, '--libs', '--system-libs']
-
 args.extend(components)
 out = run(args)
 for lib in out.strip().replace("\n", ' ').split(' '):
@@ -63,8 +78,7 @@ for lib in out.strip().replace("\n", ' ').split(' '):
     elif lib[0] == '-':
         lib = lib.strip()[1:]
     f.write("#[link(name = \"" + lib + "\"")
-    # LLVM libraries are all static libraries
-    if 'LLVM' in lib:
+    if not llvm_shared and 'LLVM' in lib:
         f.write(", kind = \"static\"")
     f.write(")]\n")
 
@@ -83,7 +97,7 @@ else:
     # Note that we use `cfg_attr` here because on MSVC the C++ standard library
     # is not c++ or stdc++, but rather the linker takes care of linking the
     # right standard library.
-    if 'stdlib=libc++' in out:
+    if use_libcpp != "0" or 'stdlib=libc++' in out:
         f.write("#[cfg_attr(not(target_env = \"msvc\"), link(name = \"c++\"))]\n")
     else:
         f.write("#[cfg_attr(not(target_env = \"msvc\"), link(name = \"" + stdcpp_name + "\"))]\n")
index 3c1659ba2e0c46f160d1ee05920a4b0b9d86fda1..10b864a902dc0a4bebb166b1d8ec0723fa3f4343 100755 (executable)
@@ -271,43 +271,6 @@ def load_properties(f, interestingprops):
 
     return props
 
-# load all widths of want_widths, except those in except_cats
-def load_east_asian_width(want_widths, except_cats):
-    f = "EastAsianWidth.txt"
-    fetch(f)
-    widths = {}
-    re1 = re.compile("^([0-9A-F]+);(\w+) +# (\w+)")
-    re2 = re.compile("^([0-9A-F]+)\.\.([0-9A-F]+);(\w+) +# (\w+)")
-
-    for line in fileinput.input(f):
-        width = None
-        d_lo = 0
-        d_hi = 0
-        cat = None
-        m = re1.match(line)
-        if m:
-            d_lo = m.group(1)
-            d_hi = m.group(1)
-            width = m.group(2)
-            cat = m.group(3)
-        else:
-            m = re2.match(line)
-            if m:
-                d_lo = m.group(1)
-                d_hi = m.group(2)
-                width = m.group(3)
-                cat = m.group(4)
-            else:
-                continue
-        if cat in except_cats or width not in want_widths:
-            continue
-        d_lo = int(d_lo, 16)
-        d_hi = int(d_hi, 16)
-        if width not in widths:
-            widths[width] = []
-        widths[width].append((d_lo, d_hi))
-    return widths
-
 def escape_char(c):
     return "'\\u{%x}'" % c if c != 0 else "'\\0'"
 
@@ -316,12 +279,12 @@ def emit_bsearch_range_table(f):
 fn bsearch_range_table(c: char, r: &'static [(char, char)]) -> bool {
     use core::cmp::Ordering::{Equal, Less, Greater};
     r.binary_search_by(|&(lo, hi)| {
-         if lo <= c && c <= hi {
-             Equal
+         if c < lo {
+             Greater
          } else if hi < c {
              Less
          } else {
-             Greater
+             Equal
          }
      })
      .is_ok()
@@ -356,34 +319,25 @@ def emit_property_module(f, mod, tbl, emit):
 def emit_conversions_module(f, to_upper, to_lower, to_title):
     f.write("pub mod conversions {")
     f.write("""
-    use core::cmp::Ordering::{Equal, Less, Greater};
     use core::option::Option;
     use core::option::Option::{Some, None};
-    use core::result::Result::{Ok, Err};
 
     pub fn to_lower(c: char) -> [char; 3] {
         match bsearch_case_table(c, to_lowercase_table) {
-          None        => [c, '\\0', '\\0'],
-          Some(index) => to_lowercase_table[index].1
+            None        => [c, '\\0', '\\0'],
+            Some(index) => to_lowercase_table[index].1,
         }
     }
 
     pub fn to_upper(c: char) -> [char; 3] {
         match bsearch_case_table(c, to_uppercase_table) {
             None        => [c, '\\0', '\\0'],
-            Some(index) => to_uppercase_table[index].1
+            Some(index) => to_uppercase_table[index].1,
         }
     }
 
     fn bsearch_case_table(c: char, table: &'static [(char, [char; 3])]) -> Option<usize> {
-        match table.binary_search_by(|&(key, _)| {
-            if c == key { Equal }
-            else if key < c { Less }
-            else { Greater }
-        }) {
-            Ok(i) => Some(i),
-            Err(_) => None,
-        }
+        table.binary_search_by(|&(key, _)| key.cmp(&c)).ok()
     }
 
 """)
@@ -398,47 +352,6 @@ def emit_conversions_module(f, to_upper, to_lower, to_title):
         is_pub=False, t_type = t_type, pfun=pfun)
     f.write("}\n\n")
 
-def emit_charwidth_module(f, width_table):
-    f.write("pub mod charwidth {\n")
-    f.write("    use core::option::Option;\n")
-    f.write("    use core::option::Option::{Some, None};\n")
-    f.write("    use core::result::Result::{Ok, Err};\n")
-    f.write("""
-    fn bsearch_range_value_table(c: char, is_cjk: bool, r: &'static [(char, char, u8, u8)]) -> u8 {
-        use core::cmp::Ordering::{Equal, Less, Greater};
-        match r.binary_search_by(|&(lo, hi, _, _)| {
-            if lo <= c && c <= hi { Equal }
-            else if hi < c { Less }
-            else { Greater }
-        }) {
-            Ok(idx) => {
-                let (_, _, r_ncjk, r_cjk) = r[idx];
-                if is_cjk { r_cjk } else { r_ncjk }
-            }
-            Err(_) => 1
-        }
-    }
-""")
-
-    f.write("""
-    pub fn width(c: char, is_cjk: bool) -> Option<usize> {
-        match c as usize {
-            _c @ 0 => Some(0),          // null is zero width
-            cu if cu < 0x20 => None,    // control sequences have no width
-            cu if cu < 0x7F => Some(1), // ASCII
-            cu if cu < 0xA0 => None,    // more control sequences
-            _ => Some(bsearch_range_value_table(c, is_cjk, charwidth_table) as usize)
-        }
-    }
-
-""")
-
-    f.write("    // character width table. Based on Markus Kuhn's free wcwidth() implementation,\n")
-    f.write("    //     http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c\n")
-    emit_table(f, "charwidth_table", width_table, "&'static [(char, char, u8, u8)]", is_pub=False,
-            pfun=lambda x: "(%s,%s,%s,%s)" % (escape_char(x[0]), escape_char(x[1]), x[2], x[3]))
-    f.write("}\n\n")
-
 def emit_norm_module(f, canon, compat, combine, norm_props):
     canon_keys = canon.keys()
     canon_keys.sort()
@@ -459,43 +372,6 @@ def emit_norm_module(f, canon, compat, combine, norm_props):
     canon_comp_keys = canon_comp.keys()
     canon_comp_keys.sort()
 
-def remove_from_wtable(wtable, val):
-    wtable_out = []
-    while wtable:
-        if wtable[0][1] < val:
-            wtable_out.append(wtable.pop(0))
-        elif wtable[0][0] > val:
-            break
-        else:
-            (wt_lo, wt_hi, width, width_cjk) = wtable.pop(0)
-            if wt_lo == wt_hi == val:
-                continue
-            elif wt_lo == val:
-                wtable_out.append((wt_lo+1, wt_hi, width, width_cjk))
-            elif wt_hi == val:
-                wtable_out.append((wt_lo, wt_hi-1, width, width_cjk))
-            else:
-                wtable_out.append((wt_lo, val-1, width, width_cjk))
-                wtable_out.append((val+1, wt_hi, width, width_cjk))
-    if wtable:
-        wtable_out.extend(wtable)
-    return wtable_out
-
-
-
-def optimize_width_table(wtable):
-    wtable_out = []
-    w_this = wtable.pop(0)
-    while wtable:
-        if w_this[1] == wtable[0][0] - 1 and w_this[2:3] == wtable[0][2:3]:
-            w_tmp = wtable.pop(0)
-            w_this = (w_this[0], w_tmp[1], w_tmp[2], w_tmp[3])
-        else:
-            wtable_out.append(w_this)
-            w_this = wtable.pop(0)
-    wtable_out.append(w_this)
-    return wtable_out
-
 if __name__ == "__main__":
     r = "tables.rs"
     if os.path.exists(r):
index e4368da90331f3ab3732d7e0253eb6d0c79d1151..6e409af79aecfe48f4c49bcd8cc97ae1dbb110b6 100644 (file)
@@ -12,7 +12,7 @@
    fun:tlv_finalize
    fun:_pthread_tsd_cleanup
    fun:_pthread_exit
-   fun:_pthread_body
+   ...
    fun:_pthread_start
    fun:thread_start
 }
@@ -24,7 +24,7 @@
    fun:tlv_finalize
    fun:_pthread_tsd_cleanup
    fun:_pthread_exit
-   fun:_pthread_body
+   ...
    fun:_pthread_start
    fun:thread_start
 }
@@ -36,7 +36,7 @@
    fun:tlv_finalize
    fun:_pthread_tsd_cleanup
    fun:_pthread_exit
-   fun:_pthread_body
+   ...
    fun:_pthread_start
    fun:thread_start
 }
@@ -48,7 +48,7 @@
    fun:tlv_finalize
    fun:_pthread_tsd_cleanup
    fun:_pthread_exit
-   fun:_pthread_body
+   ...
    fun:_pthread_start
    fun:thread_start
 }
index bdda0feb9e5d423741c7411725df48fab953852d..611968cda508b4f82344bf540f997b2f44346591 100644 (file)
@@ -1,10 +1,10 @@
 Unless otherwise specified, files in the jemalloc source distribution are
 subject to the following license:
 --------------------------------------------------------------------------------
-Copyright (C) 2002-2014 Jason Evans <jasone@canonware.com>.
+Copyright (C) 2002-2015 Jason Evans <jasone@canonware.com>.
 All rights reserved.
 Copyright (C) 2007-2012 Mozilla Foundation.  All rights reserved.
-Copyright (C) 2009-2014 Facebook, Inc.  All rights reserved.
+Copyright (C) 2009-2015 Facebook, Inc.  All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
index d56ee999e69cdaff28c8a0cb2b24ac67421b89d1..8ed42cbeadb4bf4ecec5f028983c84111030af85 100644 (file)
 Following are change highlights associated with official releases.  Important
-bug fixes are all mentioned, but internal enhancements are omitted here for
-brevity (even though they are more fun to write about).  Much more detail can be
-found in the git revision history:
+bug fixes are all mentioned, but some internal enhancements are omitted here for
+brevity.  Much more detail can be found in the git revision history:
 
     https://github.com/jemalloc/jemalloc
 
+* 4.0.4 (October 24, 2015)
+
+  This bugfix release fixes another xallocx() regression.  No other regressions
+  have come to light in over a month, so this is likely a good starting point
+  for people who prefer to wait for "dot one" releases with all the major issues
+  shaken out.
+
+  Bug fixes:
+  - Fix xallocx(..., MALLOCX_ZERO to zero the last full trailing page of large
+    allocations that have been randomly assigned an offset of 0 when
+    --enable-cache-oblivious configure option is enabled.
+
+* 4.0.3 (September 24, 2015)
+
+  This bugfix release continues the trend of xallocx() and heap profiling fixes.
+
+  Bug fixes:
+  - Fix xallocx(..., MALLOCX_ZERO) to zero all trailing bytes of large
+    allocations when --enable-cache-oblivious configure option is enabled.
+  - Fix xallocx(..., MALLOCX_ZERO) to zero trailing bytes of huge allocations
+    when resizing from/to a size class that is not a multiple of the chunk size.
+  - Fix prof_tctx_dump_iter() to filter out nodes that were created after heap
+    profile dumping started.
+  - Work around a potentially bad thread-specific data initialization
+    interaction with NPTL (glibc's pthreads implementation).
+
+* 4.0.2 (September 21, 2015)
+
+  This bugfix release addresses a few bugs specific to heap profiling.
+
+  Bug fixes:
+  - Fix ixallocx_prof_sample() to never modify nor create sampled small
+    allocations.  xallocx() is in general incapable of moving small allocations,
+    so this fix removes buggy code without loss of generality.
+  - Fix irallocx_prof_sample() to always allocate large regions, even when
+    alignment is non-zero.
+  - Fix prof_alloc_rollback() to read tdata from thread-specific data rather
+    than dereferencing a potentially invalid tctx.
+
+* 4.0.1 (September 15, 2015)
+
+  This is a bugfix release that is somewhat high risk due to the amount of
+  refactoring required to address deep xallocx() problems.  As a side effect of
+  these fixes, xallocx() now tries harder to partially fulfill requests for
+  optional extra space.  Note that a couple of minor heap profiling
+  optimizations are included, but these are better thought of as performance
+  fixes that were integral to disovering most of the other bugs.
+
+  Optimizations:
+  - Avoid a chunk metadata read in arena_prof_tctx_set(), since it is in the
+    fast path when heap profiling is enabled.  Additionally, split a special
+    case out into arena_prof_tctx_reset(), which also avoids chunk metadata
+    reads.
+  - Optimize irallocx_prof() to optimistically update the sampler state.  The
+    prior implementation appears to have been a holdover from when
+    rallocx()/xallocx() functionality was combined as rallocm().
+
+  Bug fixes:
+  - Fix TLS configuration such that it is enabled by default for platforms on
+    which it works correctly.
+  - Fix arenas_cache_cleanup() and arena_get_hard() to handle
+    allocation/deallocation within the application's thread-specific data
+    cleanup functions even after arenas_cache is torn down.
+  - Fix xallocx() bugs related to size+extra exceeding HUGE_MAXCLASS.
+  - Fix chunk purge hook calls for in-place huge shrinking reallocation to
+    specify the old chunk size rather than the new chunk size.  This bug caused
+    no correctness issues for the default chunk purge function, but was
+    visible to custom functions set via the "arena.<i>.chunk_hooks" mallctl.
+  - Fix heap profiling bugs:
+    + Fix heap profiling to distinguish among otherwise identical sample sites
+      with interposed resets (triggered via the "prof.reset" mallctl).  This bug
+      could cause data structure corruption that would most likely result in a
+      segfault.
+    + Fix irealloc_prof() to prof_alloc_rollback() on OOM.
+    + Make one call to prof_active_get_unlocked() per allocation event, and use
+      the result throughout the relevant functions that handle an allocation
+      event.  Also add a missing check in prof_realloc().  These fixes protect
+      allocation events against concurrent prof_active changes.
+    + Fix ixallocx_prof() to pass usize_max and zero to ixallocx_prof_sample()
+      in the correct order.
+    + Fix prof_realloc() to call prof_free_sampled_object() after calling
+      prof_malloc_sample_object().  Prior to this fix, if tctx and old_tctx were
+      the same, the tctx could have been prematurely destroyed.
+  - Fix portability bugs:
+    + Don't bitshift by negative amounts when encoding/decoding run sizes in
+      chunk header maps.  This affected systems with page sizes greater than 8
+      KiB.
+    + Rename index_t to szind_t to avoid an existing type on Solaris.
+    + Add JEMALLOC_CXX_THROW to the memalign() function prototype, in order to
+      match glibc and avoid compilation errors when including both
+      jemalloc/jemalloc.h and malloc.h in C++ code.
+    + Don't assume that /bin/sh is appropriate when running size_classes.sh
+      during configuration.
+    + Consider __sparcv9 a synonym for __sparc64__ when defining LG_QUANTUM.
+    + Link tests to librt if it contains clock_gettime(2).
+
+* 4.0.0 (August 17, 2015)
+
+  This version contains many speed and space optimizations, both minor and
+  major.  The major themes are generalization, unification, and simplification.
+  Although many of these optimizations cause no visible behavior change, their
+  cumulative effect is substantial.
+
+  New features:
+  - Normalize size class spacing to be consistent across the complete size
+    range.  By default there are four size classes per size doubling, but this
+    is now configurable via the --with-lg-size-class-group option.  Also add the
+    --with-lg-page, --with-lg-page-sizes, --with-lg-quantum, and
+    --with-lg-tiny-min options, which can be used to tweak page and size class
+    settings.  Impacts:
+    + Worst case performance for incrementally growing/shrinking reallocation
+      is improved because there are far fewer size classes, and therefore
+      copying happens less often.
+    + Internal fragmentation is limited to 20% for all but the smallest size
+      classes (those less than four times the quantum).  (1B + 4 KiB)
+      and (1B + 4 MiB) previously suffered nearly 50% internal fragmentation.
+    + Chunk fragmentation tends to be lower because there are fewer distinct run
+      sizes to pack.
+  - Add support for explicit tcaches.  The "tcache.create", "tcache.flush", and
+    "tcache.destroy" mallctls control tcache lifetime and flushing, and the
+    MALLOCX_TCACHE(tc) and MALLOCX_TCACHE_NONE flags to the *allocx() API
+    control which tcache is used for each operation.
+  - Implement per thread heap profiling, as well as the ability to
+    enable/disable heap profiling on a per thread basis.  Add the "prof.reset",
+    "prof.lg_sample", "thread.prof.name", "thread.prof.active",
+    "opt.prof_thread_active_init", "prof.thread_active_init", and
+    "thread.prof.active" mallctls.
+  - Add support for per arena application-specified chunk allocators, configured
+    via the "arena.<i>.chunk_hooks" mallctl.
+  - Refactor huge allocation to be managed by arenas, so that arenas now
+    function as general purpose independent allocators.  This is important in
+    the context of user-specified chunk allocators, aside from the scalability
+    benefits.  Related new statistics:
+    + The "stats.arenas.<i>.huge.allocated", "stats.arenas.<i>.huge.nmalloc",
+      "stats.arenas.<i>.huge.ndalloc", and "stats.arenas.<i>.huge.nrequests"
+      mallctls provide high level per arena huge allocation statistics.
+    + The "arenas.nhchunks", "arenas.hchunk.<i>.size",
+      "stats.arenas.<i>.hchunks.<j>.nmalloc",
+      "stats.arenas.<i>.hchunks.<j>.ndalloc",
+      "stats.arenas.<i>.hchunks.<j>.nrequests", and
+      "stats.arenas.<i>.hchunks.<j>.curhchunks" mallctls provide per size class
+      statistics.
+  - Add the 'util' column to malloc_stats_print() output, which reports the
+    proportion of available regions that are currently in use for each small
+    size class.
+  - Add "alloc" and "free" modes for for junk filling (see the "opt.junk"
+    mallctl), so that it is possible to separately enable junk filling for
+    allocation versus deallocation.
+  - Add the jemalloc-config script, which provides information about how
+    jemalloc was configured, and how to integrate it into application builds.
+  - Add metadata statistics, which are accessible via the "stats.metadata",
+    "stats.arenas.<i>.metadata.mapped", and
+    "stats.arenas.<i>.metadata.allocated" mallctls.
+  - Add the "stats.resident" mallctl, which reports the upper limit of
+    physically resident memory mapped by the allocator.
+  - Add per arena control over unused dirty page purging, via the
+    "arenas.lg_dirty_mult", "arena.<i>.lg_dirty_mult", and
+    "stats.arenas.<i>.lg_dirty_mult" mallctls.
+  - Add the "prof.gdump" mallctl, which makes it possible to toggle the gdump
+    feature on/off during program execution.
+  - Add sdallocx(), which implements sized deallocation.  The primary
+    optimization over dallocx() is the removal of a metadata read, which often
+    suffers an L1 cache miss.
+  - Add missing header includes in jemalloc/jemalloc.h, so that applications
+    only have to #include <jemalloc/jemalloc.h>.
+  - Add support for additional platforms:
+    + Bitrig
+    + Cygwin
+    + DragonFlyBSD
+    + iOS
+    + OpenBSD
+    + OpenRISC/or1k
+
+  Optimizations:
+  - Maintain dirty runs in per arena LRUs rather than in per arena trees of
+    dirty-run-containing chunks.  In practice this change significantly reduces
+    dirty page purging volume.
+  - Integrate whole chunks into the unused dirty page purging machinery.  This
+    reduces the cost of repeated huge allocation/deallocation, because it
+    effectively introduces a cache of chunks.
+  - Split the arena chunk map into two separate arrays, in order to increase
+    cache locality for the frequently accessed bits.
+  - Move small run metadata out of runs, into arena chunk headers.  This reduces
+    run fragmentation, smaller runs reduce external fragmentation for small size
+    classes, and packed (less uniformly aligned) metadata layout improves CPU
+    cache set distribution.
+  - Randomly distribute large allocation base pointer alignment relative to page
+    boundaries in order to more uniformly utilize CPU cache sets.  This can be
+    disabled via the --disable-cache-oblivious configure option, and queried via
+    the "config.cache_oblivious" mallctl.
+  - Micro-optimize the fast paths for the public API functions.
+  - Refactor thread-specific data to reside in a single structure.  This assures
+    that only a single TLS read is necessary per call into the public API.
+  - Implement in-place huge allocation growing and shrinking.
+  - Refactor rtree (radix tree for chunk lookups) to be lock-free, and make
+    additional optimizations that reduce maximum lookup depth to one or two
+    levels.  This resolves what was a concurrency bottleneck for per arena huge
+    allocation, because a global data structure is critical for determining
+    which arenas own which huge allocations.
+
+  Incompatible changes:
+  - Replace --enable-cc-silence with --disable-cc-silence to suppress spurious
+    warnings by default.
+  - Assure that the constness of malloc_usable_size()'s return type matches that
+    of the system implementation.
+  - Change the heap profile dump format to support per thread heap profiling,
+    rename pprof to jeprof, and enhance it with the --thread=<n> option.  As a
+    result, the bundled jeprof must now be used rather than the upstream
+    (gperftools) pprof.
+  - Disable "opt.prof_final" by default, in order to avoid atexit(3), which can
+    internally deadlock on some platforms.
+  - Change the "arenas.nlruns" mallctl type from size_t to unsigned.
+  - Replace the "stats.arenas.<i>.bins.<j>.allocated" mallctl with
+    "stats.arenas.<i>.bins.<j>.curregs".
+  - Ignore MALLOC_CONF in set{uid,gid,cap} binaries.
+  - Ignore MALLOCX_ARENA(a) in dallocx(), in favor of using the
+    MALLOCX_TCACHE(tc) and MALLOCX_TCACHE_NONE flags to control tcache usage.
+
+  Removed features:
+  - Remove the *allocm() API, which is superseded by the *allocx() API.
+  - Remove the --enable-dss options, and make dss non-optional on all platforms
+    which support sbrk(2).
+  - Remove the "arenas.purge" mallctl, which was obsoleted by the
+    "arena.<i>.purge" mallctl in 3.1.0.
+  - Remove the unnecessary "opt.valgrind" mallctl; jemalloc automatically
+    detects whether it is running inside Valgrind.
+  - Remove the "stats.huge.allocated", "stats.huge.nmalloc", and
+    "stats.huge.ndalloc" mallctls.
+  - Remove the --enable-mremap option.
+  - Remove the "stats.chunks.current", "stats.chunks.total", and
+    "stats.chunks.high" mallctls.
+
+  Bug fixes:
+  - Fix the cactive statistic to decrease (rather than increase) when active
+    memory decreases.  This regression was first released in 3.5.0.
+  - Fix OOM handling in memalign() and valloc().  A variant of this bug existed
+    in all releases since 2.0.0, which introduced these functions.
+  - Fix an OOM-related regression in arena_tcache_fill_small(), which could
+    cause cache corruption on OOM.  This regression was present in all releases
+    from 2.2.0 through 3.6.0.
+  - Fix size class overflow handling for malloc(), posix_memalign(), memalign(),
+    calloc(), and realloc() when profiling is enabled.
+  - Fix the "arena.<i>.dss" mallctl to return an error if "primary" or
+    "secondary" precedence is specified, but sbrk(2) is not supported.
+  - Fix fallback lg_floor() implementations to handle extremely large inputs.
+  - Ensure the default purgeable zone is after the default zone on OS X.
+  - Fix latent bugs in atomic_*().
+  - Fix the "arena.<i>.dss" mallctl to handle read-only calls.
+  - Fix tls_model configuration to enable the initial-exec model when possible.
+  - Mark malloc_conf as a weak symbol so that the application can override it.
+  - Correctly detect glibc's adaptive pthread mutexes.
+  - Fix the --without-export configure option.
+
 * 3.6.0 (March 31, 2014)
 
   This version contains a critical bug fix for a regression present in 3.5.0 and
@@ -21,7 +273,7 @@ found in the git revision history:
     backtracing to be reliable.
   - Use dss allocation precedence for huge allocations as well as small/large
     allocations.
-  - Fix test assertion failure message formatting.  This bug did not manifect on
+  - Fix test assertion failure message formatting.  This bug did not manifest on
     x86_64 systems because of implementation subtleties in va_list.
   - Fix inconsequential test failures for hash and SFMT code.
 
@@ -516,7 +768,7 @@ found in the git revision history:
   - Make it possible for the application to manually flush a thread's cache, via
     the "tcache.flush" mallctl.
   - Base maximum dirty page count on proportion of active memory.
-  - Compute various addtional run-time statistics, including per size class
+  - Compute various additional run-time statistics, including per size class
     statistics for large objects.
   - Expose malloc_stats_print(), which can be called repeatedly by the
     application.
index 9af233693b2714021c9a97557fceb0e61501a0e7..8d3968745ea09521fc29cb03b0b71423e483778e 100644 (file)
@@ -107,15 +107,15 @@ any of the following arguments (not a definitive list) to 'configure':
     there are interactions between the various coverage targets, so it is
     usually advisable to run 'make clean' between repeated code coverage runs.
 
---enable-ivsalloc
-    Enable validation code, which verifies that pointers reside within
-    jemalloc-owned chunks before dereferencing them.  This incurs a substantial
-    performance hit.
-
 --disable-stats
     Disable statistics gathering functionality.  See the "opt.stats_print"
     option documentation for usage details.
 
+--enable-ivsalloc
+    Enable validation code, which verifies that pointers reside within
+    jemalloc-owned chunks before dereferencing them.  This incurs a minor
+    performance hit.
+
 --enable-prof
     Enable heap profiling and leak detection functionality.  See the "opt.prof"
     option documentation for usage details.  When enabled, there are several
@@ -185,10 +185,106 @@ any of the following arguments (not a definitive list) to 'configure':
     thread-local variables via the __thread keyword.  If TLS is available,
     jemalloc uses it for several purposes.
 
+--disable-cache-oblivious
+    Disable cache-oblivious large allocation alignment for large allocation
+    requests with no alignment constraints.  If this feature is disabled, all
+    large allocations are page-aligned as an implementation artifact, which can
+    severely harm CPU cache utilization.  However, the cache-oblivious layout
+    comes at the cost of one extra page per large allocation, which in the
+    most extreme case increases physical memory usage for the 16 KiB size class
+    to 20 KiB.
+
 --with-xslroot=<path>
     Specify where to find DocBook XSL stylesheets when building the
     documentation.
 
+--with-lg-page=<lg-page>
+    Specify the base 2 log of the system page size.  This option is only useful
+    when cross compiling, since the configure script automatically determines
+    the host's page size by default.
+
+--with-lg-page-sizes=<lg-page-sizes>
+    Specify the comma-separated base 2 logs of the page sizes to support.  This
+    option may be useful when cross-compiling in combination with
+    --with-lg-page, but its primary use case is for integration with FreeBSD's
+    libc, wherein jemalloc is embedded.
+
+--with-lg-size-class-group=<lg-size-class-group>
+    Specify the base 2 log of how many size classes to use for each doubling in
+    size.  By default jemalloc uses <lg-size-class-group>=2, which results in
+    e.g. the following size classes:
+
+      [...], 64,
+      80, 96, 112, 128,
+      160, [...]
+
+    <lg-size-class-group>=3 results in e.g. the following size classes:
+
+      [...], 64,
+      72, 80, 88, 96, 104, 112, 120, 128,
+      144, [...]
+
+    The minimal <lg-size-class-group>=0 causes jemalloc to only provide size
+    classes that are powers of 2:
+
+      [...],
+      64,
+      128,
+      256,
+      [...]
+
+    An implementation detail currently limits the total number of small size
+    classes to 255, and a compilation error will result if the
+    <lg-size-class-group> you specify cannot be supported.  The limit is
+    roughly <lg-size-class-group>=4, depending on page size.
+
+--with-lg-quantum=<lg-quantum>
+    Specify the base 2 log of the minimum allocation alignment.  jemalloc needs
+    to know the minimum alignment that meets the following C standard
+    requirement (quoted from the April 12, 2011 draft of the C11 standard):
+
+      The pointer returned if the allocation succeeds is suitably aligned so
+      that it may be assigned to a pointer to any type of object with a
+      fundamental alignment requirement and then used to access such an object
+      or an array of such objects in the space allocated [...]
+
+    This setting is architecture-specific, and although jemalloc includes known
+    safe values for the most commonly used modern architectures, there is a
+    wrinkle related to GNU libc (glibc) that may impact your choice of
+    <lg-quantum>.  On most modern architectures, this mandates 16-byte alignment
+    (<lg-quantum>=4), but the glibc developers chose not to meet this
+    requirement for performance reasons.  An old discussion can be found at
+    https://sourceware.org/bugzilla/show_bug.cgi?id=206 .  Unlike glibc,
+    jemalloc does follow the C standard by default (caveat: jemalloc
+    technically cheats if --with-lg-tiny-min is smaller than
+    --with-lg-quantum), but the fact that Linux systems already work around
+    this allocator noncompliance means that it is generally safe in practice to
+    let jemalloc's minimum alignment follow glibc's lead.  If you specify
+    --with-lg-quantum=3 during configuration, jemalloc will provide additional
+    size classes that are not 16-byte-aligned (24, 40, and 56, assuming
+    --with-lg-size-class-group=2).
+
+--with-lg-tiny-min=<lg-tiny-min>
+    Specify the base 2 log of the minimum tiny size class to support.  Tiny
+    size classes are powers of 2 less than the quantum, and are only
+    incorporated if <lg-tiny-min> is less than <lg-quantum> (see
+    --with-lg-quantum).  Tiny size classes technically violate the C standard
+    requirement for minimum alignment, and crashes could conceivably result if
+    the compiler were to generate instructions that made alignment assumptions,
+    both because illegal instruction traps could result, and because accesses
+    could straddle page boundaries and cause segmentation faults due to
+    accessing unmapped addresses.
+
+    The default of <lg-tiny-min>=3 works well in practice even on architectures
+    that technically require 16-byte alignment, probably for the same reason
+    --with-lg-quantum=3 works.  Smaller tiny size classes can, and will, cause
+    crashes (see https://bugzilla.mozilla.org/show_bug.cgi?id=691003 for an
+    example).
+
+    This option is rarely useful, and is mainly provided as documentation of a
+    subtle implementation detail.  If you do use this option, specify a
+    value in [3, ..., <lg-quantum>].
+
 The following environment variables (not a definitive list) impact configure's
 behavior:
 
index 50f6596a6c742afb2db570f85202f9eb0fb9226d..1ac6f2926d840294dc3c7d3e576afe9abb8b2036 100644 (file)
@@ -28,6 +28,7 @@ CFLAGS := @CFLAGS@
 LDFLAGS := @LDFLAGS@
 EXTRA_LDFLAGS := @EXTRA_LDFLAGS@
 LIBS := @LIBS@
+TESTLIBS := @TESTLIBS@
 RPATH_EXTRA := @RPATH_EXTRA@
 SO := @so@
 IMPORTLIB := @importlib@
@@ -48,8 +49,10 @@ cfgoutputs_in := $(addprefix $(srcroot),@cfgoutputs_in@)
 cfgoutputs_out := @cfgoutputs_out@
 enable_autogen := @enable_autogen@
 enable_code_coverage := @enable_code_coverage@
+enable_prof := @enable_prof@
 enable_valgrind := @enable_valgrind@
 enable_zone_allocator := @enable_zone_allocator@
+MALLOC_CONF := @JEMALLOC_CPREFIX@MALLOC_CONF
 DSO_LDFLAGS = @DSO_LDFLAGS@
 SOREV = @SOREV@
 PIC_CFLAGS = @PIC_CFLAGS@
@@ -73,16 +76,17 @@ endif
 LIBJEMALLOC := $(LIBPREFIX)jemalloc$(install_suffix)
 
 # Lists of files.
-BINS := $(srcroot)bin/pprof $(objroot)bin/jemalloc.sh
+BINS := $(objroot)bin/jemalloc-config $(objroot)bin/jemalloc.sh $(objroot)bin/jeprof
 C_HDRS := $(objroot)include/jemalloc/jemalloc$(install_suffix).h
 C_SRCS := $(srcroot)src/jemalloc.c $(srcroot)src/arena.c \
        $(srcroot)src/atomic.c $(srcroot)src/base.c $(srcroot)src/bitmap.c \
        $(srcroot)src/chunk.c $(srcroot)src/chunk_dss.c \
        $(srcroot)src/chunk_mmap.c $(srcroot)src/ckh.c $(srcroot)src/ctl.c \
        $(srcroot)src/extent.c $(srcroot)src/hash.c $(srcroot)src/huge.c \
-       $(srcroot)src/mb.c $(srcroot)src/mutex.c $(srcroot)src/prof.c \
-       $(srcroot)src/quarantine.c $(srcroot)src/rtree.c $(srcroot)src/stats.c \
-       $(srcroot)src/tcache.c $(srcroot)src/util.c $(srcroot)src/tsd.c
+       $(srcroot)src/mb.c $(srcroot)src/mutex.c $(srcroot)src/pages.c \
+       $(srcroot)src/prof.c $(srcroot)src/quarantine.c $(srcroot)src/rtree.c \
+       $(srcroot)src/stats.c $(srcroot)src/tcache.c $(srcroot)src/util.c \
+       $(srcroot)src/tsd.c
 ifeq ($(enable_valgrind), 1)
 C_SRCS += $(srcroot)src/valgrind.c
 endif
@@ -104,20 +108,23 @@ endif
 PC := $(objroot)jemalloc.pc
 MAN3 := $(objroot)doc/jemalloc$(install_suffix).3
 DOCS_XML := $(objroot)doc/jemalloc$(install_suffix).xml
-DOCS_HTML := $(DOCS_XML:$(objroot)%.xml=$(srcroot)%.html)
-DOCS_MAN3 := $(DOCS_XML:$(objroot)%.xml=$(srcroot)%.3)
+DOCS_HTML := $(DOCS_XML:$(objroot)%.xml=$(objroot)%.html)
+DOCS_MAN3 := $(DOCS_XML:$(objroot)%.xml=$(objroot)%.3)
 DOCS := $(DOCS_HTML) $(DOCS_MAN3)
 C_TESTLIB_SRCS := $(srcroot)test/src/btalloc.c $(srcroot)test/src/btalloc_0.c \
        $(srcroot)test/src/btalloc_1.c $(srcroot)test/src/math.c \
-       $(srcroot)test/src/mtx.c $(srcroot)test/src/SFMT.c \
-       $(srcroot)test/src/test.c $(srcroot)test/src/thd.c \
-       $(srcroot)test/src/timer.c
+       $(srcroot)test/src/mtx.c $(srcroot)test/src/mq.c \
+       $(srcroot)test/src/SFMT.c $(srcroot)test/src/test.c \
+       $(srcroot)test/src/thd.c $(srcroot)test/src/timer.c
 C_UTIL_INTEGRATION_SRCS := $(srcroot)src/util.c
 TESTS_UNIT := $(srcroot)test/unit/atomic.c \
        $(srcroot)test/unit/bitmap.c \
        $(srcroot)test/unit/ckh.c \
        $(srcroot)test/unit/hash.c \
        $(srcroot)test/unit/junk.c \
+       $(srcroot)test/unit/junk_alloc.c \
+       $(srcroot)test/unit/junk_free.c \
+       $(srcroot)test/unit/lg_chunk.c \
        $(srcroot)test/unit/mallctl.c \
        $(srcroot)test/unit/math.c \
        $(srcroot)test/unit/mq.c \
@@ -134,6 +141,7 @@ TESTS_UNIT := $(srcroot)test/unit/atomic.c \
        $(srcroot)test/unit/rb.c \
        $(srcroot)test/unit/rtree.c \
        $(srcroot)test/unit/SFMT.c \
+       $(srcroot)test/unit/size_classes.c \
        $(srcroot)test/unit/stats.c \
        $(srcroot)test/unit/tsd.c \
        $(srcroot)test/unit/util.c \
@@ -143,6 +151,7 @@ TESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \
        $(srcroot)test/integration/sdallocx.c \
        $(srcroot)test/integration/mallocx.c \
        $(srcroot)test/integration/MALLOCX_ARENA.c \
+       $(srcroot)test/integration/overflow.c \
        $(srcroot)test/integration/posix_memalign.c \
        $(srcroot)test/integration/rallocx.c \
        $(srcroot)test/integration/thread_arena.c \
@@ -178,10 +187,10 @@ all: build_lib
 
 dist: build_doc
 
-$(srcroot)doc/%.html : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/html.xsl
+$(objroot)doc/%.html : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/html.xsl
        $(XSLTPROC) -o $@ $(objroot)doc/html.xsl $<
 
-$(srcroot)doc/%.3 : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/manpages.xsl
+$(objroot)doc/%.3 : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/manpages.xsl
        $(XSLTPROC) -o $@ $(objroot)doc/manpages.xsl $<
 
 build_doc_html: $(DOCS_HTML)
@@ -257,15 +266,15 @@ $(STATIC_LIBS):
 
 $(objroot)test/unit/%$(EXE): $(objroot)test/unit/%.$(O) $(TESTS_UNIT_LINK_OBJS) $(C_JET_OBJS) $(C_TESTLIB_UNIT_OBJS)
        @mkdir -p $(@D)
-       $(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LDFLAGS) $(filter-out -lm,$(LIBS)) -lm $(EXTRA_LDFLAGS)
+       $(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LDFLAGS) $(filter-out -lm,$(LIBS)) -lm $(TESTLIBS) $(EXTRA_LDFLAGS)
 
 $(objroot)test/integration/%$(EXE): $(objroot)test/integration/%.$(O) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
        @mkdir -p $(@D)
-       $(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(filter -lpthread,$(LIBS))) -lm $(EXTRA_LDFLAGS)
+       $(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(filter -lpthread,$(LIBS))) -lm $(TESTLIBS) $(EXTRA_LDFLAGS)
 
 $(objroot)test/stress/%$(EXE): $(objroot)test/stress/%.$(O) $(C_JET_OBJS) $(C_TESTLIB_STRESS_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
        @mkdir -p $(@D)
-       $(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(LIBS)) -lm $(EXTRA_LDFLAGS)
+       $(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(LIBS)) -lm $(TESTLIBS) $(EXTRA_LDFLAGS)
 
 build_lib_shared: $(DSOS)
 build_lib_static: $(STATIC_LIBS)
@@ -335,18 +344,23 @@ check_unit_dir:
        @mkdir -p $(objroot)test/unit
 check_integration_dir:
        @mkdir -p $(objroot)test/integration
-check_stress_dir:
+stress_dir:
        @mkdir -p $(objroot)test/stress
-check_dir: check_unit_dir check_integration_dir check_stress_dir
+check_dir: check_unit_dir check_integration_dir
 
 check_unit: tests_unit check_unit_dir
        $(SHELL) $(objroot)test/test.sh $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%)
+check_integration_prof: tests_integration check_integration_dir
+ifeq ($(enable_prof), 1)
+       $(MALLOC_CONF)="prof:true" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
+       $(MALLOC_CONF)="prof:true,prof_active:false" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
+endif
 check_integration: tests_integration check_integration_dir
        $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
-check_stress: tests_stress check_stress_dir
+stress: tests_stress stress_dir
        $(SHELL) $(objroot)test/test.sh $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%)
-check: tests check_dir
-       $(SHELL) $(objroot)test/test.sh $(TESTS:$(srcroot)%.c=$(objroot)%)
+check: tests check_dir check_integration_prof
+       $(SHELL) $(objroot)test/test.sh $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
 
 ifeq ($(enable_code_coverage), 1)
 coverage_unit: check_unit
@@ -360,7 +374,7 @@ coverage_integration: check_integration
        $(SHELL) $(srcroot)coverage.sh $(srcroot)test/src integration $(C_TESTLIB_INTEGRATION_OBJS)
        $(SHELL) $(srcroot)coverage.sh $(srcroot)test/integration integration $(TESTS_INTEGRATION_OBJS)
 
-coverage_stress: check_stress
+coverage_stress: stress
        $(SHELL) $(srcroot)coverage.sh $(srcroot)src pic $(C_PIC_OBJS)
        $(SHELL) $(srcroot)coverage.sh $(srcroot)src jet $(C_JET_OBJS)
        $(SHELL) $(srcroot)coverage.sh $(srcroot)test/src stress $(C_TESTLIB_STRESS_OBJS)
@@ -405,7 +419,9 @@ clean:
        rm -f $(objroot)*.gcov.*
 
 distclean: clean
+       rm -f $(objroot)bin/jemalloc-config
        rm -f $(objroot)bin/jemalloc.sh
+       rm -f $(objroot)bin/jeprof
        rm -f $(objroot)config.log
        rm -f $(objroot)config.status
        rm -f $(objroot)config.stamp
@@ -414,7 +430,7 @@ distclean: clean
 
 relclean: distclean
        rm -f $(objroot)configure
-       rm -f $(srcroot)VERSION
+       rm -f $(objroot)VERSION
        rm -f $(DOCS_HTML)
        rm -f $(DOCS_MAN3)
 
diff --git a/src/jemalloc/VERSION b/src/jemalloc/VERSION
deleted file mode 100644 (file)
index 51f89bb..0000000
+++ /dev/null
@@ -1 +0,0 @@
-0.12.0-15684-gc30b771ad9d44ab84f8c88b80c25fcfde2433126
diff --git a/src/jemalloc/bin/jemalloc-config.in b/src/jemalloc/bin/jemalloc-config.in
new file mode 100644 (file)
index 0000000..b016c8d
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+usage() {
+       cat <<EOF
+Usage:
+  @BINDIR@/jemalloc-config <option>
+Options:
+  --help | -h  : Print usage.
+  --version    : Print jemalloc version.
+  --revision   : Print shared library revision number.
+  --config     : Print configure options used to build jemalloc.
+  --prefix     : Print installation directory prefix.
+  --bindir     : Print binary installation directory.
+  --datadir    : Print data installation directory.
+  --includedir : Print include installation directory.
+  --libdir     : Print library installation directory.
+  --mandir     : Print manual page installation directory.
+  --cc         : Print compiler used to build jemalloc.
+  --cflags     : Print compiler flags used to build jemalloc.
+  --cppflags   : Print preprocessor flags used to build jemalloc.
+  --ldflags    : Print library flags used to build jemalloc.
+  --libs       : Print libraries jemalloc was linked against.
+EOF
+}
+
+prefix="@prefix@"
+exec_prefix="@exec_prefix@"
+
+case "$1" in
+--help | -h)
+       usage
+       exit 0
+       ;;
+--version)
+       echo "@jemalloc_version@"
+       ;;
+--revision)
+       echo "@rev@"
+       ;;
+--config)
+       echo "@CONFIG@"
+       ;;
+--prefix)
+       echo "@PREFIX@"
+       ;;
+--bindir)
+       echo "@BINDIR@"
+       ;;
+--datadir)
+       echo "@DATADIR@"
+       ;;
+--includedir)
+       echo "@INCLUDEDIR@"
+       ;;
+--libdir)
+       echo "@LIBDIR@"
+       ;;
+--mandir)
+       echo "@MANDIR@"
+       ;;
+--cc)
+       echo "@CC@"
+       ;;
+--cflags)
+       echo "@CFLAGS@"
+       ;;
+--cppflags)
+       echo "@CPPFLAGS@"
+       ;;
+--ldflags)
+       echo "@LDFLAGS@ @EXTRA_LDFLAGS@"
+       ;;
+--libs)
+       echo "@LIBS@"
+       ;;
+*)
+       usage
+       exit 1
+esac
diff --git a/src/jemalloc/bin/jeprof.in b/src/jemalloc/bin/jeprof.in
new file mode 100644 (file)
index 0000000..e717807
--- /dev/null
@@ -0,0 +1,5510 @@
+#! /usr/bin/env perl
+
+# Copyright (c) 1998-2007, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# ---
+# Program for printing the profile generated by common/profiler.cc,
+# or by the heap profiler (common/debugallocation.cc)
+#
+# The profile contains a sequence of entries of the form:
+#       <count> <stack trace>
+# This program parses the profile, and generates user-readable
+# output.
+#
+# Examples:
+#
+# % tools/jeprof "program" "profile"
+#   Enters "interactive" mode
+#
+# % tools/jeprof --text "program" "profile"
+#   Generates one line per procedure
+#
+# % tools/jeprof --gv "program" "profile"
+#   Generates annotated call-graph and displays via "gv"
+#
+# % tools/jeprof --gv --focus=Mutex "program" "profile"
+#   Restrict to code paths that involve an entry that matches "Mutex"
+#
+# % tools/jeprof --gv --focus=Mutex --ignore=string "program" "profile"
+#   Restrict to code paths that involve an entry that matches "Mutex"
+#   and does not match "string"
+#
+# % tools/jeprof --list=IBF_CheckDocid "program" "profile"
+#   Generates disassembly listing of all routines with at least one
+#   sample that match the --list=<regexp> pattern.  The listing is
+#   annotated with the flat and cumulative sample counts at each line.
+#
+# % tools/jeprof --disasm=IBF_CheckDocid "program" "profile"
+#   Generates disassembly listing of all routines with at least one
+#   sample that match the --disasm=<regexp> pattern.  The listing is
+#   annotated with the flat and cumulative sample counts at each PC value.
+#
+# TODO: Use color to indicate files?
+
+use strict;
+use warnings;
+use Getopt::Long;
+
+my $JEPROF_VERSION = "@jemalloc_version@";
+my $PPROF_VERSION = "2.0";
+
+# These are the object tools we use which can come from a
+# user-specified location using --tools, from the JEPROF_TOOLS
+# environment variable, or from the environment.
+my %obj_tool_map = (
+  "objdump" => "objdump",
+  "nm" => "nm",
+  "addr2line" => "addr2line",
+  "c++filt" => "c++filt",
+  ## ConfigureObjTools may add architecture-specific entries:
+  #"nm_pdb" => "nm-pdb",       # for reading windows (PDB-format) executables
+  #"addr2line_pdb" => "addr2line-pdb",                                # ditto
+  #"otool" => "otool",         # equivalent of objdump on OS X
+);
+# NOTE: these are lists, so you can put in commandline flags if you want.
+my @DOT = ("dot");          # leave non-absolute, since it may be in /usr/local
+my @GV = ("gv");
+my @EVINCE = ("evince");    # could also be xpdf or perhaps acroread
+my @KCACHEGRIND = ("kcachegrind");
+my @PS2PDF = ("ps2pdf");
+# These are used for dynamic profiles
+my @URL_FETCHER = ("curl", "-s");
+
+# These are the web pages that servers need to support for dynamic profiles
+my $HEAP_PAGE = "/pprof/heap";
+my $PROFILE_PAGE = "/pprof/profile";   # must support cgi-param "?seconds=#"
+my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\?.*)?"; # must support cgi-param
+                                                # ?seconds=#&event=x&period=n
+my $GROWTH_PAGE = "/pprof/growth";
+my $CONTENTION_PAGE = "/pprof/contention";
+my $WALL_PAGE = "/pprof/wall(?:\\?.*)?";  # accepts options like namefilter
+my $FILTEREDPROFILE_PAGE = "/pprof/filteredprofile(?:\\?.*)?";
+my $CENSUSPROFILE_PAGE = "/pprof/censusprofile(?:\\?.*)?"; # must support cgi-param
+                                                       # "?seconds=#",
+                                                       # "?tags_regexp=#" and
+                                                       # "?type=#".
+my $SYMBOL_PAGE = "/pprof/symbol";     # must support symbol lookup via POST
+my $PROGRAM_NAME_PAGE = "/pprof/cmdline";
+
+# These are the web pages that can be named on the command line.
+# All the alternatives must begin with /.
+my $PROFILES = "($HEAP_PAGE|$PROFILE_PAGE|$PMUPROFILE_PAGE|" .
+               "$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|" .
+               "$FILTEREDPROFILE_PAGE|$CENSUSPROFILE_PAGE)";
+
+# default binary name
+my $UNKNOWN_BINARY = "(unknown)";
+
+# There is a pervasive dependency on the length (in hex characters,
+# i.e., nibbles) of an address, distinguishing between 32-bit and
+# 64-bit profiles.  To err on the safe size, default to 64-bit here:
+my $address_length = 16;
+
+my $dev_null = "/dev/null";
+if (! -e $dev_null && $^O =~ /MSWin/) {    # $^O is the OS perl was built for
+  $dev_null = "nul";
+}
+
+# A list of paths to search for shared object files
+my @prefix_list = ();
+
+# Special routine name that should not have any symbols.
+# Used as separator to parse "addr2line -i" output.
+my $sep_symbol = '_fini';
+my $sep_address = undef;
+
+##### Argument parsing #####
+
+sub usage_string {
+  return <<EOF;
+Usage:
+jeprof [options] <program> <profiles>
+   <profiles> is a space separated list of profile names.
+jeprof [options] <symbolized-profiles>
+   <symbolized-profiles> is a list of profile files where each file contains
+   the necessary symbol mappings  as well as profile data (likely generated
+   with --raw).
+jeprof [options] <profile>
+   <profile> is a remote form.  Symbols are obtained from host:port$SYMBOL_PAGE
+
+   Each name can be:
+   /path/to/profile        - a path to a profile file
+   host:port[/<service>]   - a location of a service to get profile from
+
+   The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
+                         $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
+                         $CENSUSPROFILE_PAGE, or /pprof/filteredprofile.
+   For instance:
+     jeprof http://myserver.com:80$HEAP_PAGE
+   If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
+jeprof --symbols <program>
+   Maps addresses to symbol names.  In this mode, stdin should be a
+   list of library mappings, in the same format as is found in the heap-
+   and cpu-profile files (this loosely matches that of /proc/self/maps
+   on linux), followed by a list of hex addresses to map, one per line.
+
+   For more help with querying remote servers, including how to add the
+   necessary server-side support code, see this filename (or one like it):
+
+   /usr/doc/gperftools-$PPROF_VERSION/pprof_remote_servers.html
+
+Options:
+   --cum               Sort by cumulative data
+   --base=<base>       Subtract <base> from <profile> before display
+   --interactive       Run in interactive mode (interactive "help" gives help) [default]
+   --seconds=<n>       Length of time for dynamic profiles [default=30 secs]
+   --add_lib=<file>    Read additional symbols and line info from the given library
+   --lib_prefix=<dir>  Comma separated list of library path prefixes
+
+Reporting Granularity:
+   --addresses         Report at address level
+   --lines             Report at source line level
+   --functions         Report at function level [default]
+   --files             Report at source file level
+
+Output type:
+   --text              Generate text report
+   --callgrind         Generate callgrind format to stdout
+   --gv                Generate Postscript and display
+   --evince            Generate PDF and display
+   --web               Generate SVG and display
+   --list=<regexp>     Generate source listing of matching routines
+   --disasm=<regexp>   Generate disassembly of matching routines
+   --symbols           Print demangled symbol names found at given addresses
+   --dot               Generate DOT file to stdout
+   --ps                Generate Postcript to stdout
+   --pdf               Generate PDF to stdout
+   --svg               Generate SVG to stdout
+   --gif               Generate GIF to stdout
+   --raw               Generate symbolized jeprof data (useful with remote fetch)
+
+Heap-Profile Options:
+   --inuse_space       Display in-use (mega)bytes [default]
+   --inuse_objects     Display in-use objects
+   --alloc_space       Display allocated (mega)bytes
+   --alloc_objects     Display allocated objects
+   --show_bytes        Display space in bytes
+   --drop_negative     Ignore negative differences
+
+Contention-profile options:
+   --total_delay       Display total delay at each region [default]
+   --contentions       Display number of delays at each region
+   --mean_delay        Display mean delay at each region
+
+Call-graph Options:
+   --nodecount=<n>     Show at most so many nodes [default=80]
+   --nodefraction=<f>  Hide nodes below <f>*total [default=.005]
+   --edgefraction=<f>  Hide edges below <f>*total [default=.001]
+   --maxdegree=<n>     Max incoming/outgoing edges per node [default=8]
+   --focus=<regexp>    Focus on nodes matching <regexp>
+   --thread=<n>        Show profile for thread <n>
+   --ignore=<regexp>   Ignore nodes matching <regexp>
+   --scale=<n>         Set GV scaling [default=0]
+   --heapcheck         Make nodes with non-0 object counts
+                       (i.e. direct leak generators) more visible
+
+Miscellaneous:
+   --tools=<prefix or binary:fullpath>[,...]   \$PATH for object tool pathnames
+   --test              Run unit tests
+   --help              This message
+   --version           Version information
+
+Environment Variables:
+   JEPROF_TMPDIR        Profiles directory. Defaults to \$HOME/jeprof
+   JEPROF_TOOLS         Prefix for object tools pathnames
+
+Examples:
+
+jeprof /bin/ls ls.prof
+                       Enters "interactive" mode
+jeprof --text /bin/ls ls.prof
+                       Outputs one line per procedure
+jeprof --web /bin/ls ls.prof
+                       Displays annotated call-graph in web browser
+jeprof --gv /bin/ls ls.prof
+                       Displays annotated call-graph via 'gv'
+jeprof --gv --focus=Mutex /bin/ls ls.prof
+                       Restricts to code paths including a .*Mutex.* entry
+jeprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof
+                       Code paths including Mutex but not string
+jeprof --list=getdir /bin/ls ls.prof
+                       (Per-line) annotated source listing for getdir()
+jeprof --disasm=getdir /bin/ls ls.prof
+                       (Per-PC) annotated disassembly for getdir()
+
+jeprof http://localhost:1234/
+                       Enters "interactive" mode
+jeprof --text localhost:1234
+                       Outputs one line per procedure for localhost:1234
+jeprof --raw localhost:1234 > ./local.raw
+jeprof --text ./local.raw
+                       Fetches a remote profile for later analysis and then
+                       analyzes it in text mode.
+EOF
+}
+
+sub version_string {
+  return <<EOF
+jeprof (part of jemalloc $JEPROF_VERSION)
+based on pprof (part of gperftools $PPROF_VERSION)
+
+Copyright 1998-2007 Google Inc.
+
+This is BSD licensed software; see the source for copying conditions
+and license information.
+There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.
+EOF
+}
+
+sub usage {
+  my $msg = shift;
+  print STDERR "$msg\n\n";
+  print STDERR usage_string();
+  print STDERR "\nFATAL ERROR: $msg\n";    # just as a reminder
+  exit(1);
+}
+
+sub Init() {
+  # Setup tmp-file name and handler to clean it up.
+  # We do this in the very beginning so that we can use
+  # error() and cleanup() function anytime here after.
+  $main::tmpfile_sym = "/tmp/jeprof$$.sym";
+  $main::tmpfile_ps = "/tmp/jeprof$$";
+  $main::next_tmpfile = 0;
+  $SIG{'INT'} = \&sighandler;
+
+  # Cache from filename/linenumber to source code
+  $main::source_cache = ();
+
+  $main::opt_help = 0;
+  $main::opt_version = 0;
+
+  $main::opt_cum = 0;
+  $main::opt_base = '';
+  $main::opt_addresses = 0;
+  $main::opt_lines = 0;
+  $main::opt_functions = 0;
+  $main::opt_files = 0;
+  $main::opt_lib_prefix = "";
+
+  $main::opt_text = 0;
+  $main::opt_callgrind = 0;
+  $main::opt_list = "";
+  $main::opt_disasm = "";
+  $main::opt_symbols = 0;
+  $main::opt_gv = 0;
+  $main::opt_evince = 0;
+  $main::opt_web = 0;
+  $main::opt_dot = 0;
+  $main::opt_ps = 0;
+  $main::opt_pdf = 0;
+  $main::opt_gif = 0;
+  $main::opt_svg = 0;
+  $main::opt_raw = 0;
+
+  $main::opt_nodecount = 80;
+  $main::opt_nodefraction = 0.005;
+  $main::opt_edgefraction = 0.001;
+  $main::opt_maxdegree = 8;
+  $main::opt_focus = '';
+  $main::opt_thread = undef;
+  $main::opt_ignore = '';
+  $main::opt_scale = 0;
+  $main::opt_heapcheck = 0;
+  $main::opt_seconds = 30;
+  $main::opt_lib = "";
+
+  $main::opt_inuse_space   = 0;
+  $main::opt_inuse_objects = 0;
+  $main::opt_alloc_space   = 0;
+  $main::opt_alloc_objects = 0;
+  $main::opt_show_bytes    = 0;
+  $main::opt_drop_negative = 0;
+  $main::opt_interactive   = 0;
+
+  $main::opt_total_delay = 0;
+  $main::opt_contentions = 0;
+  $main::opt_mean_delay = 0;
+
+  $main::opt_tools   = "";
+  $main::opt_debug   = 0;
+  $main::opt_test    = 0;
+
+  # These are undocumented flags used only by unittests.
+  $main::opt_test_stride = 0;
+
+  # Are we using $SYMBOL_PAGE?
+  $main::use_symbol_page = 0;
+
+  # Files returned by TempName.
+  %main::tempnames = ();
+
+  # Type of profile we are dealing with
+  # Supported types:
+  #     cpu
+  #     heap
+  #     growth
+  #     contention
+  $main::profile_type = '';     # Empty type means "unknown"
+
+  GetOptions("help!"          => \$main::opt_help,
+             "version!"       => \$main::opt_version,
+             "cum!"           => \$main::opt_cum,
+             "base=s"         => \$main::opt_base,
+             "seconds=i"      => \$main::opt_seconds,
+             "add_lib=s"      => \$main::opt_lib,
+             "lib_prefix=s"   => \$main::opt_lib_prefix,
+             "functions!"     => \$main::opt_functions,
+             "lines!"         => \$main::opt_lines,
+             "addresses!"     => \$main::opt_addresses,
+             "files!"         => \$main::opt_files,
+             "text!"          => \$main::opt_text,
+             "callgrind!"     => \$main::opt_callgrind,
+             "list=s"         => \$main::opt_list,
+             "disasm=s"       => \$main::opt_disasm,
+             "symbols!"       => \$main::opt_symbols,
+             "gv!"            => \$main::opt_gv,
+             "evince!"        => \$main::opt_evince,
+             "web!"           => \$main::opt_web,
+             "dot!"           => \$main::opt_dot,
+             "ps!"            => \$main::opt_ps,
+             "pdf!"           => \$main::opt_pdf,
+             "svg!"           => \$main::opt_svg,
+             "gif!"           => \$main::opt_gif,
+             "raw!"           => \$main::opt_raw,
+             "interactive!"   => \$main::opt_interactive,
+             "nodecount=i"    => \$main::opt_nodecount,
+             "nodefraction=f" => \$main::opt_nodefraction,
+             "edgefraction=f" => \$main::opt_edgefraction,
+             "maxdegree=i"    => \$main::opt_maxdegree,
+             "focus=s"        => \$main::opt_focus,
+             "thread=s"       => \$main::opt_thread,
+             "ignore=s"       => \$main::opt_ignore,
+             "scale=i"        => \$main::opt_scale,
+             "heapcheck"      => \$main::opt_heapcheck,
+             "inuse_space!"   => \$main::opt_inuse_space,
+             "inuse_objects!" => \$main::opt_inuse_objects,
+             "alloc_space!"   => \$main::opt_alloc_space,
+             "alloc_objects!" => \$main::opt_alloc_objects,
+             "show_bytes!"    => \$main::opt_show_bytes,
+             "drop_negative!" => \$main::opt_drop_negative,
+             "total_delay!"   => \$main::opt_total_delay,
+             "contentions!"   => \$main::opt_contentions,
+             "mean_delay!"    => \$main::opt_mean_delay,
+             "tools=s"        => \$main::opt_tools,
+             "test!"          => \$main::opt_test,
+             "debug!"         => \$main::opt_debug,
+             # Undocumented flags used only by unittests:
+             "test_stride=i"  => \$main::opt_test_stride,
+      ) || usage("Invalid option(s)");
+
+  # Deal with the standard --help and --version
+  if ($main::opt_help) {
+    print usage_string();
+    exit(0);
+  }
+
+  if ($main::opt_version) {
+    print version_string();
+    exit(0);
+  }
+
+  # Disassembly/listing/symbols mode requires address-level info
+  if ($main::opt_disasm || $main::opt_list || $main::opt_symbols) {
+    $main::opt_functions = 0;
+    $main::opt_lines = 0;
+    $main::opt_addresses = 1;
+    $main::opt_files = 0;
+  }
+
+  # Check heap-profiling flags
+  if ($main::opt_inuse_space +
+      $main::opt_inuse_objects +
+      $main::opt_alloc_space +
+      $main::opt_alloc_objects > 1) {
+    usage("Specify at most on of --inuse/--alloc options");
+  }
+
+  # Check output granularities
+  my $grains =
+      $main::opt_functions +
+      $main::opt_lines +
+      $main::opt_addresses +
+      $main::opt_files +
+      0;
+  if ($grains > 1) {
+    usage("Only specify one output granularity option");
+  }
+  if ($grains == 0) {
+    $main::opt_functions = 1;
+  }
+
+  # Check output modes
+  my $modes =
+      $main::opt_text +
+      $main::opt_callgrind +
+      ($main::opt_list eq '' ? 0 : 1) +
+      ($main::opt_disasm eq '' ? 0 : 1) +
+      ($main::opt_symbols == 0 ? 0 : 1) +
+      $main::opt_gv +
+      $main::opt_evince +
+      $main::opt_web +
+      $main::opt_dot +
+      $main::opt_ps +
+      $main::opt_pdf +
+      $main::opt_svg +
+      $main::opt_gif +
+      $main::opt_raw +
+      $main::opt_interactive +
+      0;
+  if ($modes > 1) {
+    usage("Only specify one output mode");
+  }
+  if ($modes == 0) {
+    if (-t STDOUT) {  # If STDOUT is a tty, activate interactive mode
+      $main::opt_interactive = 1;
+    } else {
+      $main::opt_text = 1;
+    }
+  }
+
+  if ($main::opt_test) {
+    RunUnitTests();
+    # Should not return
+    exit(1);
+  }
+
+  # Binary name and profile arguments list
+  $main::prog = "";
+  @main::pfile_args = ();
+
+  # Remote profiling without a binary (using $SYMBOL_PAGE instead)
+  if (@ARGV > 0) {
+    if (IsProfileURL($ARGV[0])) {
+      $main::use_symbol_page = 1;
+    } elsif (IsSymbolizedProfileFile($ARGV[0])) {
+      $main::use_symbolized_profile = 1;
+      $main::prog = $UNKNOWN_BINARY;  # will be set later from the profile file
+    }
+  }
+
+  if ($main::use_symbol_page || $main::use_symbolized_profile) {
+    # We don't need a binary!
+    my %disabled = ('--lines' => $main::opt_lines,
+                    '--disasm' => $main::opt_disasm);
+    for my $option (keys %disabled) {
+      usage("$option cannot be used without a binary") if $disabled{$option};
+    }
+    # Set $main::prog later...
+    scalar(@ARGV) || usage("Did not specify profile file");
+  } elsif ($main::opt_symbols) {
+    # --symbols needs a binary-name (to run nm on, etc) but not profiles
+    $main::prog = shift(@ARGV) || usage("Did not specify program");
+  } else {
+    $main::prog = shift(@ARGV) || usage("Did not specify program");
+    scalar(@ARGV) || usage("Did not specify profile file");
+  }
+
+  # Parse profile file/location arguments
+  foreach my $farg (@ARGV) {
+    if ($farg =~ m/(.*)\@([0-9]+)(|\/.*)$/ ) {
+      my $machine = $1;
+      my $num_machines = $2;
+      my $path = $3;
+      for (my $i = 0; $i < $num_machines; $i++) {
+        unshift(@main::pfile_args, "$i.$machine$path");
+      }
+    } else {
+      unshift(@main::pfile_args, $farg);
+    }
+  }
+
+  if ($main::use_symbol_page) {
+    unless (IsProfileURL($main::pfile_args[0])) {
+      error("The first profile should be a remote form to use $SYMBOL_PAGE\n");
+    }
+    CheckSymbolPage();
+    $main::prog = FetchProgramName();
+  } elsif (!$main::use_symbolized_profile) {  # may not need objtools!
+    ConfigureObjTools($main::prog)
+  }
+
+  # Break the opt_lib_prefix into the prefix_list array
+  @prefix_list = split (',', $main::opt_lib_prefix);
+
+  # Remove trailing / from the prefixes, in the list to prevent
+  # searching things like /my/path//lib/mylib.so
+  foreach (@prefix_list) {
+    s|/+$||;
+  }
+}
+
+sub FilterAndPrint {
+  my ($profile, $symbols, $libs, $thread) = @_;
+
+  # Get total data in profile
+  my $total = TotalProfile($profile);
+
+  # Remove uniniteresting stack items
+  $profile = RemoveUninterestingFrames($symbols, $profile);
+
+  # Focus?
+  if ($main::opt_focus ne '') {
+    $profile = FocusProfile($symbols, $profile, $main::opt_focus);
+  }
+
+  # Ignore?
+  if ($main::opt_ignore ne '') {
+    $profile = IgnoreProfile($symbols, $profile, $main::opt_ignore);
+  }
+
+  my $calls = ExtractCalls($symbols, $profile);
+
+  # Reduce profiles to required output granularity, and also clean
+  # each stack trace so a given entry exists at most once.
+  my $reduced = ReduceProfile($symbols, $profile);
+
+  # Get derived profiles
+  my $flat = FlatProfile($reduced);
+  my $cumulative = CumulativeProfile($reduced);
+
+  # Print
+  if (!$main::opt_interactive) {
+    if ($main::opt_disasm) {
+      PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm);
+    } elsif ($main::opt_list) {
+      PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
+    } elsif ($main::opt_text) {
+      # Make sure the output is empty when have nothing to report
+      # (only matters when --heapcheck is given but we must be
+      # compatible with old branches that did not pass --heapcheck always):
+      if ($total != 0) {
+        printf("Total%s: %s %s\n",
+               (defined($thread) ? " (t$thread)" : ""),
+               Unparse($total), Units());
+      }
+      PrintText($symbols, $flat, $cumulative, -1);
+    } elsif ($main::opt_raw) {
+      PrintSymbolizedProfile($symbols, $profile, $main::prog);
+    } elsif ($main::opt_callgrind) {
+      PrintCallgrind($calls);
+    } else {
+      if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
+        if ($main::opt_gv) {
+          RunGV(TempName($main::next_tmpfile, "ps"), "");
+        } elsif ($main::opt_evince) {
+          RunEvince(TempName($main::next_tmpfile, "pdf"), "");
+        } elsif ($main::opt_web) {
+          my $tmp = TempName($main::next_tmpfile, "svg");
+          RunWeb($tmp);
+          # The command we run might hand the file name off
+          # to an already running browser instance and then exit.
+          # Normally, we'd remove $tmp on exit (right now),
+          # but fork a child to remove $tmp a little later, so that the
+          # browser has time to load it first.
+          delete $main::tempnames{$tmp};
+          if (fork() == 0) {
+            sleep 5;
+            unlink($tmp);
+            exit(0);
+          }
+        }
+      } else {
+        cleanup();
+        exit(1);
+      }
+    }
+  } else {
+    InteractiveMode($profile, $symbols, $libs, $total);
+  }
+}
+
+sub Main() {
+  Init();
+  $main::collected_profile = undef;
+  @main::profile_files = ();
+  $main::op_time = time();
+
+  # Printing symbols is special and requires a lot less info that most.
+  if ($main::opt_symbols) {
+    PrintSymbols(*STDIN);   # Get /proc/maps and symbols output from stdin
+    return;
+  }
+
+  # Fetch all profile data
+  FetchDynamicProfiles();
+
+  # this will hold symbols that we read from the profile files
+  my $symbol_map = {};
+
+  # Read one profile, pick the last item on the list
+  my $data = ReadProfile($main::prog, pop(@main::profile_files));
+  my $profile = $data->{profile};
+  my $pcs = $data->{pcs};
+  my $libs = $data->{libs};   # Info about main program and shared libraries
+  $symbol_map = MergeSymbols($symbol_map, $data->{symbols});
+
+  # Add additional profiles, if available.
+  if (scalar(@main::profile_files) > 0) {
+    foreach my $pname (@main::profile_files) {
+      my $data2 = ReadProfile($main::prog, $pname);
+      $profile = AddProfile($profile, $data2->{profile});
+      $pcs = AddPcs($pcs, $data2->{pcs});
+      $symbol_map = MergeSymbols($symbol_map, $data2->{symbols});
+    }
+  }
+
+  # Subtract base from profile, if specified
+  if ($main::opt_base ne '') {
+    my $base = ReadProfile($main::prog, $main::opt_base);
+    $profile = SubtractProfile($profile, $base->{profile});
+    $pcs = AddPcs($pcs, $base->{pcs});
+    $symbol_map = MergeSymbols($symbol_map, $base->{symbols});
+  }
+
+  # Collect symbols
+  my $symbols;
+  if ($main::use_symbolized_profile) {
+    $symbols = FetchSymbols($pcs, $symbol_map);
+  } elsif ($main::use_symbol_page) {
+    $symbols = FetchSymbols($pcs);
+  } else {
+    # TODO(csilvers): $libs uses the /proc/self/maps data from profile1,
+    # which may differ from the data from subsequent profiles, especially
+    # if they were run on different machines.  Use appropriate libs for
+    # each pc somehow.
+    $symbols = ExtractSymbols($libs, $pcs);
+  }
+
+  if (!defined($main::opt_thread)) {
+    FilterAndPrint($profile, $symbols, $libs);
+  }
+  if (defined($data->{threads})) {
+    foreach my $thread (sort { $a <=> $b } keys(%{$data->{threads}})) {
+      if (defined($main::opt_thread) &&
+          ($main::opt_thread eq '*' || $main::opt_thread == $thread)) {
+        my $thread_profile = $data->{threads}{$thread};
+        FilterAndPrint($thread_profile, $symbols, $libs, $thread);
+      }
+    }
+  }
+
+  cleanup();
+  exit(0);
+}
+
+##### Entry Point #####
+
+Main();
+
+# Temporary code to detect if we're running on a Goobuntu system.
+# These systems don't have the right stuff installed for the special
+# Readline libraries to work, so as a temporary workaround, we default
+# to using the normal stdio code, rather than the fancier readline-based
+# code
+sub ReadlineMightFail {
+  if (-e '/lib/libtermcap.so.2') {
+    return 0;  # libtermcap exists, so readline should be okay
+  } else {
+    return 1;
+  }
+}
+
+sub RunGV {
+  my $fname = shift;
+  my $bg = shift;       # "" or " &" if we should run in background
+  if (!system(ShellEscape(@GV, "--version") . " >$dev_null 2>&1")) {
+    # Options using double dash are supported by this gv version.
+    # Also, turn on noantialias to better handle bug in gv for
+    # postscript files with large dimensions.
+    # TODO: Maybe we should not pass the --noantialias flag
+    # if the gv version is known to work properly without the flag.
+    system(ShellEscape(@GV, "--scale=$main::opt_scale", "--noantialias", $fname)
+           . $bg);
+  } else {
+    # Old gv version - only supports options that use single dash.
+    print STDERR ShellEscape(@GV, "-scale", $main::opt_scale) . "\n";
+    system(ShellEscape(@GV, "-scale", "$main::opt_scale", $fname) . $bg);
+  }
+}
+
+sub RunEvince {
+  my $fname = shift;
+  my $bg = shift;       # "" or " &" if we should run in background
+  system(ShellEscape(@EVINCE, $fname) . $bg);
+}
+
+sub RunWeb {
+  my $fname = shift;
+  print STDERR "Loading web page file:///$fname\n";
+
+  if (`uname` =~ /Darwin/) {
+    # OS X: open will use standard preference for SVG files.
+    system("/usr/bin/open", $fname);
+    return;
+  }
+
+  # Some kind of Unix; try generic symlinks, then specific browsers.
+  # (Stop once we find one.)
+  # Works best if the browser is already running.
+  my @alt = (
+    "/etc/alternatives/gnome-www-browser",
+    "/etc/alternatives/x-www-browser",
+    "google-chrome",
+    "firefox",
+  );
+  foreach my $b (@alt) {
+    if (system($b, $fname) == 0) {
+      return;
+    }
+  }
+
+  print STDERR "Could not load web browser.\n";
+}
+
+sub RunKcachegrind {
+  my $fname = shift;
+  my $bg = shift;       # "" or " &" if we should run in background
+  print STDERR "Starting '@KCACHEGRIND " . $fname . $bg . "'\n";
+  system(ShellEscape(@KCACHEGRIND, $fname) . $bg);
+}
+
+
+##### Interactive helper routines #####
+
+sub InteractiveMode {
+  $| = 1;  # Make output unbuffered for interactive mode
+  my ($orig_profile, $symbols, $libs, $total) = @_;
+
+  print STDERR "Welcome to jeprof!  For help, type 'help'.\n";
+
+  # Use ReadLine if it's installed and input comes from a console.
+  if ( -t STDIN &&
+       !ReadlineMightFail() &&
+       defined(eval {require Term::ReadLine}) ) {
+    my $term = new Term::ReadLine 'jeprof';
+    while ( defined ($_ = $term->readline('(jeprof) '))) {
+      $term->addhistory($_) if /\S/;
+      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
+        last;    # exit when we get an interactive command to quit
+      }
+    }
+  } else {       # don't have readline
+    while (1) {
+      print STDERR "(jeprof) ";
+      $_ = <STDIN>;
+      last if ! defined $_ ;
+      s/\r//g;         # turn windows-looking lines into unix-looking lines
+
+      # Save some flags that might be reset by InteractiveCommand()
+      my $save_opt_lines = $main::opt_lines;
+
+      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
+        last;    # exit when we get an interactive command to quit
+      }
+
+      # Restore flags
+      $main::opt_lines = $save_opt_lines;
+    }
+  }
+}
+
+# Takes two args: orig profile, and command to run.
+# Returns 1 if we should keep going, or 0 if we were asked to quit
+sub InteractiveCommand {
+  my($orig_profile, $symbols, $libs, $total, $command) = @_;
+  $_ = $command;                # just to make future m//'s easier
+  if (!defined($_)) {
+    print STDERR "\n";
+    return 0;
+  }
+  if (m/^\s*quit/) {
+    return 0;
+  }
+  if (m/^\s*help/) {
+    InteractiveHelpMessage();
+    return 1;
+  }
+  # Clear all the mode options -- mode is controlled by "$command"
+  $main::opt_text = 0;
+  $main::opt_callgrind = 0;
+  $main::opt_disasm = 0;
+  $main::opt_list = 0;
+  $main::opt_gv = 0;
+  $main::opt_evince = 0;
+  $main::opt_cum = 0;
+
+  if (m/^\s*(text|top)(\d*)\s*(.*)/) {
+    $main::opt_text = 1;
+
+    my $line_limit = ($2 ne "") ? int($2) : 10;
+
+    my $routine;
+    my $ignore;
+    ($routine, $ignore) = ParseInteractiveArgs($3);
+
+    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
+    my $reduced = ReduceProfile($symbols, $profile);
+
+    # Get derived profiles
+    my $flat = FlatProfile($reduced);
+    my $cumulative = CumulativeProfile($reduced);
+
+    PrintText($symbols, $flat, $cumulative, $line_limit);
+    return 1;
+  }
+  if (m/^\s*callgrind\s*([^ \n]*)/) {
+    $main::opt_callgrind = 1;
+
+    # Get derived profiles
+    my $calls = ExtractCalls($symbols, $orig_profile);
+    my $filename = $1;
+    if ( $1 eq '' ) {
+      $filename = TempName($main::next_tmpfile, "callgrind");
+    }
+    PrintCallgrind($calls, $filename);
+    if ( $1 eq '' ) {
+      RunKcachegrind($filename, " & ");
+      $main::next_tmpfile++;
+    }
+
+    return 1;
+  }
+  if (m/^\s*(web)?list\s*(.+)/) {
+    my $html = (defined($1) && ($1 eq "web"));
+    $main::opt_list = 1;
+
+    my $routine;
+    my $ignore;
+    ($routine, $ignore) = ParseInteractiveArgs($2);
+
+    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
+    my $reduced = ReduceProfile($symbols, $profile);
+
+    # Get derived profiles
+    my $flat = FlatProfile($reduced);
+    my $cumulative = CumulativeProfile($reduced);
+
+    PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
+    return 1;
+  }
+  if (m/^\s*disasm\s*(.+)/) {
+    $main::opt_disasm = 1;
+
+    my $routine;
+    my $ignore;
+    ($routine, $ignore) = ParseInteractiveArgs($1);
+
+    # Process current profile to account for various settings
+    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
+    my $reduced = ReduceProfile($symbols, $profile);
+
+    # Get derived profiles
+    my $flat = FlatProfile($reduced);
+    my $cumulative = CumulativeProfile($reduced);
+
+    PrintDisassembly($libs, $flat, $cumulative, $routine);
+    return 1;
+  }
+  if (m/^\s*(gv|web|evince)\s*(.*)/) {
+    $main::opt_gv = 0;
+    $main::opt_evince = 0;
+    $main::opt_web = 0;
+    if ($1 eq "gv") {
+      $main::opt_gv = 1;
+    } elsif ($1 eq "evince") {
+      $main::opt_evince = 1;
+    } elsif ($1 eq "web") {
+      $main::opt_web = 1;
+    }
+
+    my $focus;
+    my $ignore;
+    ($focus, $ignore) = ParseInteractiveArgs($2);
+
+    # Process current profile to account for various settings
+    my $profile = ProcessProfile($total, $orig_profile, $symbols,
+                                 $focus, $ignore);
+    my $reduced = ReduceProfile($symbols, $profile);
+
+    # Get derived profiles
+    my $flat = FlatProfile($reduced);
+    my $cumulative = CumulativeProfile($reduced);
+
+    if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
+      if ($main::opt_gv) {
+        RunGV(TempName($main::next_tmpfile, "ps"), " &");
+      } elsif ($main::opt_evince) {
+        RunEvince(TempName($main::next_tmpfile, "pdf"), " &");
+      } elsif ($main::opt_web) {
+        RunWeb(TempName($main::next_tmpfile, "svg"));
+      }
+      $main::next_tmpfile++;
+    }
+    return 1;
+  }
+  if (m/^\s*$/) {
+    return 1;
+  }
+  print STDERR "Unknown command: try 'help'.\n";
+  return 1;
+}
+
+
+sub ProcessProfile {
+  my $total_count = shift;
+  my $orig_profile = shift;
+  my $symbols = shift;
+  my $focus = shift;
+  my $ignore = shift;
+
+  # Process current profile to account for various settings
+  my $profile = $orig_profile;
+  printf("Total: %s %s\n", Unparse($total_count), Units());
+  if ($focus ne '') {
+    $profile = FocusProfile($symbols, $profile, $focus);
+    my $focus_count = TotalProfile($profile);
+    printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n",
+           $focus,
+           Unparse($focus_count), Units(),
+           Unparse($total_count), ($focus_count*100.0) / $total_count);
+  }
+  if ($ignore ne '') {
+    $profile = IgnoreProfile($symbols, $profile, $ignore);
+    my $ignore_count = TotalProfile($profile);
+    printf("After ignoring '%s': %s %s of %s (%0.1f%%)\n",
+           $ignore,
+           Unparse($ignore_count), Units(),
+           Unparse($total_count),
+           ($ignore_count*100.0) / $total_count);
+  }
+
+  return $profile;
+}
+
+sub InteractiveHelpMessage {
+  print STDERR <<ENDOFHELP;
+Interactive jeprof mode
+
+Commands:
+  gv
+  gv [focus] [-ignore1] [-ignore2]
+      Show graphical hierarchical display of current profile.  Without
+      any arguments, shows all samples in the profile.  With the optional
+      "focus" argument, restricts the samples shown to just those where
+      the "focus" regular expression matches a routine name on the stack
+      trace.
+
+  web
+  web [focus] [-ignore1] [-ignore2]
+      Like GV, but displays profile in your web browser instead of using
+      Ghostview. Works best if your web browser is already running.
+      To change the browser that gets used:
+      On Linux, set the /etc/alternatives/gnome-www-browser symlink.
+      On OS X, change the Finder association for SVG files.
+
+  list [routine_regexp] [-ignore1] [-ignore2]
+      Show source listing of routines whose names match "routine_regexp"
+
+  weblist [routine_regexp] [-ignore1] [-ignore2]
+     Displays a source listing of routines whose names match "routine_regexp"
+     in a web browser.  You can click on source lines to view the
+     corresponding disassembly.
+
+  top [--cum] [-ignore1] [-ignore2]
+  top20 [--cum] [-ignore1] [-ignore2]
+  top37 [--cum] [-ignore1] [-ignore2]
+      Show top lines ordered by flat profile count, or cumulative count
+      if --cum is specified.  If a number is present after 'top', the
+      top K routines will be shown (defaults to showing the top 10)
+
+  disasm [routine_regexp] [-ignore1] [-ignore2]
+      Show disassembly of routines whose names match "routine_regexp",
+      annotated with sample counts.
+
+  callgrind
+  callgrind [filename]
+      Generates callgrind file. If no filename is given, kcachegrind is called.
+
+  help - This listing
+  quit or ^D - End jeprof
+
+For commands that accept optional -ignore tags, samples where any routine in
+the stack trace matches the regular expression in any of the -ignore
+parameters will be ignored.
+
+Further pprof details are available at this location (or one similar):
+
+ /usr/doc/gperftools-$PPROF_VERSION/cpu_profiler.html
+ /usr/doc/gperftools-$PPROF_VERSION/heap_profiler.html
+
+ENDOFHELP
+}
+sub ParseInteractiveArgs {
+  my $args = shift;
+  my $focus = "";
+  my $ignore = "";
+  my @x = split(/ +/, $args);
+  foreach $a (@x) {
+    if ($a =~ m/^(--|-)lines$/) {
+      $main::opt_lines = 1;
+    } elsif ($a =~ m/^(--|-)cum$/) {
+      $main::opt_cum = 1;
+    } elsif ($a =~ m/^-(.*)/) {
+      $ignore .= (($ignore ne "") ? "|" : "" ) . $1;
+    } else {
+      $focus .= (($focus ne "") ? "|" : "" ) . $a;
+    }
+  }
+  if ($ignore ne "") {
+    print STDERR "Ignoring samples in call stacks that match '$ignore'\n";
+  }
+  return ($focus, $ignore);
+}
+
+##### Output code #####
+
+sub TempName {
+  my $fnum = shift;
+  my $ext = shift;
+  my $file = "$main::tmpfile_ps.$fnum.$ext";
+  $main::tempnames{$file} = 1;
+  return $file;
+}
+
+# Print profile data in packed binary format (64-bit) to standard out
+sub PrintProfileData {
+  my $profile = shift;
+
+  # print header (64-bit style)
+  # (zero) (header-size) (version) (sample-period) (zero)
+  print pack('L*', 0, 0, 3, 0, 0, 0, 1, 0, 0, 0);
+
+  foreach my $k (keys(%{$profile})) {
+    my $count = $profile->{$k};
+    my @addrs = split(/\n/, $k);
+    if ($#addrs >= 0) {
+      my $depth = $#addrs + 1;
+      # int(foo / 2**32) is the only reliable way to get rid of bottom
+      # 32 bits on both 32- and 64-bit systems.
+      print pack('L*', $count & 0xFFFFFFFF, int($count / 2**32));
+      print pack('L*', $depth & 0xFFFFFFFF, int($depth / 2**32));
+
+      foreach my $full_addr (@addrs) {
+        my $addr = $full_addr;
+        $addr =~ s/0x0*//;  # strip off leading 0x, zeroes
+        if (length($addr) > 16) {
+          print STDERR "Invalid address in profile: $full_addr\n";
+          next;
+        }
+        my $low_addr = substr($addr, -8);       # get last 8 hex chars
+        my $high_addr = substr($addr, -16, 8);  # get up to 8 more hex chars
+        print pack('L*', hex('0x' . $low_addr), hex('0x' . $high_addr));
+      }
+    }
+  }
+}
+
+# Print symbols and profile data
+sub PrintSymbolizedProfile {
+  my $symbols = shift;
+  my $profile = shift;
+  my $prog = shift;
+
+  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
+  my $symbol_marker = $&;
+
+  print '--- ', $symbol_marker, "\n";
+  if (defined($prog)) {
+    print 'binary=', $prog, "\n";
+  }
+  while (my ($pc, $name) = each(%{$symbols})) {
+    my $sep = ' ';
+    print '0x', $pc;
+    # We have a list of function names, which include the inlined
+    # calls.  They are separated (and terminated) by --, which is
+    # illegal in function names.
+    for (my $j = 2; $j <= $#{$name}; $j += 3) {
+      print $sep, $name->[$j];
+      $sep = '--';
+    }
+    print "\n";
+  }
+  print '---', "\n";
+
+  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
+  my $profile_marker = $&;
+  print '--- ', $profile_marker, "\n";
+  if (defined($main::collected_profile)) {
+    # if used with remote fetch, simply dump the collected profile to output.
+    open(SRC, "<$main::collected_profile");
+    while (<SRC>) {
+      print $_;
+    }
+    close(SRC);
+  } else {
+    # dump a cpu-format profile to standard out
+    PrintProfileData($profile);
+  }
+}
+
+# Print text output
+sub PrintText {
+  my $symbols = shift;
+  my $flat = shift;
+  my $cumulative = shift;
+  my $line_limit = shift;
+
+  my $total = TotalProfile($flat);
+
+  # Which profile to sort by?
+  my $s = $main::opt_cum ? $cumulative : $flat;
+
+  my $running_sum = 0;
+  my $lines = 0;
+  foreach my $k (sort { GetEntry($s, $b) <=> GetEntry($s, $a) || $a cmp $b }
+                 keys(%{$cumulative})) {
+    my $f = GetEntry($flat, $k);
+    my $c = GetEntry($cumulative, $k);
+    $running_sum += $f;
+
+    my $sym = $k;
+    if (exists($symbols->{$k})) {
+      $sym = $symbols->{$k}->[0] . " " . $symbols->{$k}->[1];
+      if ($main::opt_addresses) {
+        $sym = $k . " " . $sym;
+      }
+    }
+
+    if ($f != 0 || $c != 0) {
+      printf("%8s %6s %6s %8s %6s %s\n",
+             Unparse($f),
+             Percent($f, $total),
+             Percent($running_sum, $total),
+             Unparse($c),
+             Percent($c, $total),
+             $sym);
+    }
+    $lines++;
+    last if ($line_limit >= 0 && $lines >= $line_limit);
+  }
+}
+
+# Callgrind format has a compression for repeated function and file
+# names.  You show the name the first time, and just use its number
+# subsequently.  This can cut down the file to about a third or a
+# quarter of its uncompressed size.  $key and $val are the key/value
+# pair that would normally be printed by callgrind; $map is a map from
+# value to number.
+sub CompressedCGName {
+  my($key, $val, $map) = @_;
+  my $idx = $map->{$val};
+  # For very short keys, providing an index hurts rather than helps.
+  if (length($val) <= 3) {
+    return "$key=$val\n";
+  } elsif (defined($idx)) {
+    return "$key=($idx)\n";
+  } else {
+    # scalar(keys $map) gives the number of items in the map.
+    $idx = scalar(keys(%{$map})) + 1;
+    $map->{$val} = $idx;
+    return "$key=($idx) $val\n";
+  }
+}
+
+# Print the call graph in a way that's suiteable for callgrind.
+sub PrintCallgrind {
+  my $calls = shift;
+  my $filename;
+  my %filename_to_index_map;
+  my %fnname_to_index_map;
+
+  if ($main::opt_interactive) {
+    $filename = shift;
+    print STDERR "Writing callgrind file to '$filename'.\n"
+  } else {
+    $filename = "&STDOUT";
+  }
+  open(CG, ">$filename");
+  printf CG ("events: Hits\n\n");
+  foreach my $call ( map { $_->[0] }
+                     sort { $a->[1] cmp $b ->[1] ||
+                            $a->[2] <=> $b->[2] }
+                     map { /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
+                           [$_, $1, $2] }
+                     keys %$calls ) {
+    my $count = int($calls->{$call});
+    $call =~ /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
+    my ( $caller_file, $caller_line, $caller_function,
+         $callee_file, $callee_line, $callee_function ) =
+       ( $1, $2, $3, $5, $6, $7 );
+
+    # TODO(csilvers): for better compression, collect all the
+    # caller/callee_files and functions first, before printing
+    # anything, and only compress those referenced more than once.
+    printf CG CompressedCGName("fl", $caller_file, \%filename_to_index_map);
+    printf CG CompressedCGName("fn", $caller_function, \%fnname_to_index_map);
+    if (defined $6) {
+      printf CG CompressedCGName("cfl", $callee_file, \%filename_to_index_map);
+      printf CG CompressedCGName("cfn", $callee_function, \%fnname_to_index_map);
+      printf CG ("calls=$count $callee_line\n");
+    }
+    printf CG ("$caller_line $count\n\n");
+  }
+}
+
+# Print disassembly for all all routines that match $main::opt_disasm
+sub PrintDisassembly {
+  my $libs = shift;
+  my $flat = shift;
+  my $cumulative = shift;
+  my $disasm_opts = shift;
+
+  my $total = TotalProfile($flat);
+
+  foreach my $lib (@{$libs}) {
+    my $symbol_table = GetProcedureBoundaries($lib->[0], $disasm_opts);
+    my $offset = AddressSub($lib->[1], $lib->[3]);
+    foreach my $routine (sort ByName keys(%{$symbol_table})) {
+      my $start_addr = $symbol_table->{$routine}->[0];
+      my $end_addr = $symbol_table->{$routine}->[1];
+      # See if there are any samples in this routine
+      my $length = hex(AddressSub($end_addr, $start_addr));
+      my $addr = AddressAdd($start_addr, $offset);
+      for (my $i = 0; $i < $length; $i++) {
+        if (defined($cumulative->{$addr})) {
+          PrintDisassembledFunction($lib->[0], $offset,
+                                    $routine, $flat, $cumulative,
+                                    $start_addr, $end_addr, $total);
+          last;
+        }
+        $addr = AddressInc($addr);
+      }
+    }
+  }
+}
+
+# Return reference to array of tuples of the form:
+#       [start_address, filename, linenumber, instruction, limit_address]
+# E.g.,
+#       ["0x806c43d", "/foo/bar.cc", 131, "ret", "0x806c440"]
+sub Disassemble {
+  my $prog = shift;
+  my $offset = shift;
+  my $start_addr = shift;
+  my $end_addr = shift;
+
+  my $objdump = $obj_tool_map{"objdump"};
+  my $cmd = ShellEscape($objdump, "-C", "-d", "-l", "--no-show-raw-insn",
+                        "--start-address=0x$start_addr",
+                        "--stop-address=0x$end_addr", $prog);
+  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
+  my @result = ();
+  my $filename = "";
+  my $linenumber = -1;
+  my $last = ["", "", "", ""];
+  while (<OBJDUMP>) {
+    s/\r//g;         # turn windows-looking lines into unix-looking lines
+    chop;
+    if (m|\s*([^:\s]+):(\d+)\s*$|) {
+      # Location line of the form:
+      #   <filename>:<linenumber>
+      $filename = $1;
+      $linenumber = $2;
+    } elsif (m/^ +([0-9a-f]+):\s*(.*)/) {
+      # Disassembly line -- zero-extend address to full length
+      my $addr = HexExtend($1);
+      my $k = AddressAdd($addr, $offset);
+      $last->[4] = $k;   # Store ending address for previous instruction
+      $last = [$k, $filename, $linenumber, $2, $end_addr];
+      push(@result, $last);
+    }
+  }
+  close(OBJDUMP);
+  return @result;
+}
+
+# The input file should contain lines of the form /proc/maps-like
+# output (same format as expected from the profiles) or that looks
+# like hex addresses (like "0xDEADBEEF").  We will parse all
+# /proc/maps output, and for all the hex addresses, we will output
+# "short" symbol names, one per line, in the same order as the input.
+sub PrintSymbols {
+  my $maps_and_symbols_file = shift;
+
+  # ParseLibraries expects pcs to be in a set.  Fine by us...
+  my @pclist = ();   # pcs in sorted order
+  my $pcs = {};
+  my $map = "";
+  foreach my $line (<$maps_and_symbols_file>) {
+    $line =~ s/\r//g;    # turn windows-looking lines into unix-looking lines
+    if ($line =~ /\b(0x[0-9a-f]+)\b/i) {
+      push(@pclist, HexExtend($1));
+      $pcs->{$pclist[-1]} = 1;
+    } else {
+      $map .= $line;
+    }
+  }
+
+  my $libs = ParseLibraries($main::prog, $map, $pcs);
+  my $symbols = ExtractSymbols($libs, $pcs);
+
+  foreach my $pc (@pclist) {
+    # ->[0] is the shortname, ->[2] is the full name
+    print(($symbols->{$pc}->[0] || "??") . "\n");
+  }
+}
+
+
+# For sorting functions by name
+sub ByName {
+  return ShortFunctionName($a) cmp ShortFunctionName($b);
+}
+
+# Print source-listing for all all routines that match $list_opts
+sub PrintListing {
+  my $total = shift;
+  my $libs = shift;
+  my $flat = shift;
+  my $cumulative = shift;
+  my $list_opts = shift;
+  my $html = shift;
+
+  my $output = \*STDOUT;
+  my $fname = "";
+
+  if ($html) {
+    # Arrange to write the output to a temporary file
+    $fname = TempName($main::next_tmpfile, "html");
+    $main::next_tmpfile++;
+    if (!open(TEMP, ">$fname")) {
+      print STDERR "$fname: $!\n";
+      return;
+    }
+    $output = \*TEMP;
+    print $output HtmlListingHeader();
+    printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
+                    $main::prog, Unparse($total), Units());
+  }
+
+  my $listed = 0;
+  foreach my $lib (@{$libs}) {
+    my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
+    my $offset = AddressSub($lib->[1], $lib->[3]);
+    foreach my $routine (sort ByName keys(%{$symbol_table})) {
+      # Print if there are any samples in this routine
+      my $start_addr = $symbol_table->{$routine}->[0];
+      my $end_addr = $symbol_table->{$routine}->[1];
+      my $length = hex(AddressSub($end_addr, $start_addr));
+      my $addr = AddressAdd($start_addr, $offset);
+      for (my $i = 0; $i < $length; $i++) {
+        if (defined($cumulative->{$addr})) {
+          $listed += PrintSource(
+            $lib->[0], $offset,
+            $routine, $flat, $cumulative,
+            $start_addr, $end_addr,
+            $html,
+            $output);
+          last;
+        }
+        $addr = AddressInc($addr);
+      }
+    }
+  }
+
+  if ($html) {
+    if ($listed > 0) {
+      print $output HtmlListingFooter();
+      close($output);
+      RunWeb($fname);
+    } else {
+      close($output);
+      unlink($fname);
+    }
+  }
+}
+
+sub HtmlListingHeader {
+  return <<'EOF';
+<DOCTYPE html>
+<html>
+<head>
+<title>Pprof listing</title>
+<style type="text/css">
+body {
+  font-family: sans-serif;
+}
+h1 {
+  font-size: 1.5em;
+  margin-bottom: 4px;
+}
+.legend {
+  font-size: 1.25em;
+}
+.line {
+  color: #aaaaaa;
+}
+.nop {
+  color: #aaaaaa;
+}
+.unimportant {
+  color: #cccccc;
+}
+.disasmloc {
+  color: #000000;
+}
+.deadsrc {
+  cursor: pointer;
+}
+.deadsrc:hover {
+  background-color: #eeeeee;
+}
+.livesrc {
+  color: #0000ff;
+  cursor: pointer;
+}
+.livesrc:hover {
+  background-color: #eeeeee;
+}
+.asm {
+  color: #008800;
+  display: none;
+}
+</style>
+<script type="text/javascript">
+function jeprof_toggle_asm(e) {
+  var target;
+  if (!e) e = window.event;
+  if (e.target) target = e.target;
+  else if (e.srcElement) target = e.srcElement;
+
+  if (target) {
+    var asm = target.nextSibling;
+    if (asm && asm.className == "asm") {
+      asm.style.display = (asm.style.display == "block" ? "" : "block");
+      e.preventDefault();
+      return false;
+    }
+  }
+}
+</script>
+</head>
+<body>
+EOF
+}
+
+sub HtmlListingFooter {
+  return <<'EOF';
+</body>
+</html>
+EOF
+}
+
+sub HtmlEscape {
+  my $text = shift;
+  $text =~ s/&/&amp;/g;
+  $text =~ s/</&lt;/g;
+  $text =~ s/>/&gt;/g;
+  return $text;
+}
+
+# Returns the indentation of the line, if it has any non-whitespace
+# characters.  Otherwise, returns -1.
+sub Indentation {
+  my $line = shift;
+  if (m/^(\s*)\S/) {
+    return length($1);
+  } else {
+    return -1;
+  }
+}
+
+# If the symbol table contains inlining info, Disassemble() may tag an
+# instruction with a location inside an inlined function.  But for
+# source listings, we prefer to use the location in the function we
+# are listing.  So use MapToSymbols() to fetch full location
+# information for each instruction and then pick out the first
+# location from a location list (location list contains callers before
+# callees in case of inlining).
+#
+# After this routine has run, each entry in $instructions contains:
+#   [0] start address
+#   [1] filename for function we are listing
+#   [2] line number for function we are listing
+#   [3] disassembly
+#   [4] limit address
+#   [5] most specific filename (may be different from [1] due to inlining)
+#   [6] most specific line number (may be different from [2] due to inlining)
+sub GetTopLevelLineNumbers {
+  my ($lib, $offset, $instructions) = @_;
+  my $pcs = [];
+  for (my $i = 0; $i <= $#{$instructions}; $i++) {
+    push(@{$pcs}, $instructions->[$i]->[0]);
+  }
+  my $symbols = {};
+  MapToSymbols($lib, $offset, $pcs, $symbols);
+  for (my $i = 0; $i <= $#{$instructions}; $i++) {
+    my $e = $instructions->[$i];
+    push(@{$e}, $e->[1]);
+    push(@{$e}, $e->[2]);
+    my $addr = $e->[0];
+    my $sym = $symbols->{$addr};
+    if (defined($sym)) {
+      if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\d+)$/) {
+        $e->[1] = $1;  # File name
+        $e->[2] = $2;  # Line number
+      }
+    }
+  }
+}
+
+# Print source-listing for one routine
+sub PrintSource {
+  my $prog = shift;
+  my $offset = shift;
+  my $routine = shift;
+  my $flat = shift;
+  my $cumulative = shift;
+  my $start_addr = shift;
+  my $end_addr = shift;
+  my $html = shift;
+  my $output = shift;
+
+  # Disassemble all instructions (just to get line numbers)
+  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
+  GetTopLevelLineNumbers($prog, $offset, \@instructions);
+
+  # Hack 1: assume that the first source file encountered in the
+  # disassembly contains the routine
+  my $filename = undef;
+  for (my $i = 0; $i <= $#instructions; $i++) {
+    if ($instructions[$i]->[2] >= 0) {
+      $filename = $instructions[$i]->[1];
+      last;
+    }
+  }
+  if (!defined($filename)) {
+    print STDERR "no filename found in $routine\n";
+    return 0;
+  }
+
+  # Hack 2: assume that the largest line number from $filename is the
+  # end of the procedure.  This is typically safe since if P1 contains
+  # an inlined call to P2, then P2 usually occurs earlier in the
+  # source file.  If this does not work, we might have to compute a
+  # density profile or just print all regions we find.
+  my $lastline = 0;
+  for (my $i = 0; $i <= $#instructions; $i++) {
+    my $f = $instructions[$i]->[1];
+    my $l = $instructions[$i]->[2];
+    if (($f eq $filename) && ($l > $lastline)) {
+      $lastline = $l;
+    }
+  }
+
+  # Hack 3: assume the first source location from "filename" is the start of
+  # the source code.
+  my $firstline = 1;
+  for (my $i = 0; $i <= $#instructions; $i++) {
+    if ($instructions[$i]->[1] eq $filename) {
+      $firstline = $instructions[$i]->[2];
+      last;
+    }
+  }
+
+  # Hack 4: Extend last line forward until its indentation is less than
+  # the indentation we saw on $firstline
+  my $oldlastline = $lastline;
+  {
+    if (!open(FILE, "<$filename")) {
+      print STDERR "$filename: $!\n";
+      return 0;
+    }
+    my $l = 0;
+    my $first_indentation = -1;
+    while (<FILE>) {
+      s/\r//g;         # turn windows-looking lines into unix-looking lines
+      $l++;
+      my $indent = Indentation($_);
+      if ($l >= $firstline) {
+        if ($first_indentation < 0 && $indent >= 0) {
+          $first_indentation = $indent;
+          last if ($first_indentation == 0);
+        }
+      }
+      if ($l >= $lastline && $indent >= 0) {
+        if ($indent >= $first_indentation) {
+          $lastline = $l+1;
+        } else {
+          last;
+        }
+      }
+    }
+    close(FILE);
+  }
+
+  # Assign all samples to the range $firstline,$lastline,
+  # Hack 4: If an instruction does not occur in the range, its samples
+  # are moved to the next instruction that occurs in the range.
+  my $samples1 = {};        # Map from line number to flat count
+  my $samples2 = {};        # Map from line number to cumulative count
+  my $running1 = 0;         # Unassigned flat counts
+  my $running2 = 0;         # Unassigned cumulative counts
+  my $total1 = 0;           # Total flat counts
+  my $total2 = 0;           # Total cumulative counts
+  my %disasm = ();          # Map from line number to disassembly
+  my $running_disasm = "";  # Unassigned disassembly
+  my $skip_marker = "---\n";
+  if ($html) {
+    $skip_marker = "";
+    for (my $l = $firstline; $l <= $lastline; $l++) {
+      $disasm{$l} = "";
+    }
+  }
+  my $last_dis_filename = '';
+  my $last_dis_linenum = -1;
+  my $last_touched_line = -1;  # To detect gaps in disassembly for a line
+  foreach my $e (@instructions) {
+    # Add up counts for all address that fall inside this instruction
+    my $c1 = 0;
+    my $c2 = 0;
+    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
+      $c1 += GetEntry($flat, $a);
+      $c2 += GetEntry($cumulative, $a);
+    }
+
+    if ($html) {
+      my $dis = sprintf("      %6s %6s \t\t%8s: %s ",
+                        HtmlPrintNumber($c1),
+                        HtmlPrintNumber($c2),
+                        UnparseAddress($offset, $e->[0]),
+                        CleanDisassembly($e->[3]));
+
+      # Append the most specific source line associated with this instruction
+      if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) };
+      $dis = HtmlEscape($dis);
+      my $f = $e->[5];
+      my $l = $e->[6];
+      if ($f ne $last_dis_filename) {
+        $dis .= sprintf("<span class=disasmloc>%s:%d</span>",
+                        HtmlEscape(CleanFileName($f)), $l);
+      } elsif ($l ne $last_dis_linenum) {
+        # De-emphasize the unchanged file name portion
+        $dis .= sprintf("<span class=unimportant>%s</span>" .
+                        "<span class=disasmloc>:%d</span>",
+                        HtmlEscape(CleanFileName($f)), $l);
+      } else {
+        # De-emphasize the entire location
+        $dis .= sprintf("<span class=unimportant>%s:%d</span>",
+                        HtmlEscape(CleanFileName($f)), $l);
+      }
+      $last_dis_filename = $f;
+      $last_dis_linenum = $l;
+      $running_disasm .= $dis;
+      $running_disasm .= "\n";
+    }
+
+    $running1 += $c1;
+    $running2 += $c2;
+    $total1 += $c1;
+    $total2 += $c2;
+    my $file = $e->[1];
+    my $line = $e->[2];
+    if (($file eq $filename) &&
+        ($line >= $firstline) &&
+        ($line <= $lastline)) {
+      # Assign all accumulated samples to this line
+      AddEntry($samples1, $line, $running1);
+      AddEntry($samples2, $line, $running2);
+      $running1 = 0;
+      $running2 = 0;
+      if ($html) {
+        if ($line != $last_touched_line && $disasm{$line} ne '') {
+          $disasm{$line} .= "\n";
+        }
+        $disasm{$line} .= $running_disasm;
+        $running_disasm = '';
+        $last_touched_line = $line;
+      }
+    }
+  }
+
+  # Assign any leftover samples to $lastline
+  AddEntry($samples1, $lastline, $running1);
+  AddEntry($samples2, $lastline, $running2);
+  if ($html) {
+    if ($lastline != $last_touched_line && $disasm{$lastline} ne '') {
+      $disasm{$lastline} .= "\n";
+    }
+    $disasm{$lastline} .= $running_disasm;
+  }
+
+  if ($html) {
+    printf $output (
+      "<h1>%s</h1>%s\n<pre onClick=\"jeprof_toggle_asm()\">\n" .
+      "Total:%6s %6s (flat / cumulative %s)\n",
+      HtmlEscape(ShortFunctionName($routine)),
+      HtmlEscape(CleanFileName($filename)),
+      Unparse($total1),
+      Unparse($total2),
+      Units());
+  } else {
+    printf $output (
+      "ROUTINE ====================== %s in %s\n" .
+      "%6s %6s Total %s (flat / cumulative)\n",
+      ShortFunctionName($routine),
+      CleanFileName($filename),
+      Unparse($total1),
+      Unparse($total2),
+      Units());
+  }
+  if (!open(FILE, "<$filename")) {
+    print STDERR "$filename: $!\n";
+    return 0;
+  }
+  my $l = 0;
+  while (<FILE>) {
+    s/\r//g;         # turn windows-looking lines into unix-looking lines
+    $l++;
+    if ($l >= $firstline - 5 &&
+        (($l <= $oldlastline + 5) || ($l <= $lastline))) {
+      chop;
+      my $text = $_;
+      if ($l == $firstline) { print $output $skip_marker; }
+      my $n1 = GetEntry($samples1, $l);
+      my $n2 = GetEntry($samples2, $l);
+      if ($html) {
+        # Emit a span that has one of the following classes:
+        #    livesrc -- has samples
+        #    deadsrc -- has disassembly, but with no samples
+        #    nop     -- has no matching disasembly
+        # Also emit an optional span containing disassembly.
+        my $dis = $disasm{$l};
+        my $asm = "";
+        if (defined($dis) && $dis ne '') {
+          $asm = "<span class=\"asm\">" . $dis . "</span>";
+        }
+        my $source_class = (($n1 + $n2 > 0)
+                            ? "livesrc"
+                            : (($asm ne "") ? "deadsrc" : "nop"));
+        printf $output (
+          "<span class=\"line\">%5d</span> " .
+          "<span class=\"%s\">%6s %6s %s</span>%s\n",
+          $l, $source_class,
+          HtmlPrintNumber($n1),
+          HtmlPrintNumber($n2),
+          HtmlEscape($text),
+          $asm);
+      } else {
+        printf $output(
+          "%6s %6s %4d: %s\n",
+          UnparseAlt($n1),
+          UnparseAlt($n2),
+          $l,
+          $text);
+      }
+      if ($l == $lastline)  { print $output $skip_marker; }
+    };
+  }
+  close(FILE);
+  if ($html) {
+    print $output "</pre>\n";
+  }
+  return 1;
+}
+
+# Return the source line for the specified file/linenumber.
+# Returns undef if not found.
+sub SourceLine {
+  my $file = shift;
+  my $line = shift;
+
+  # Look in cache
+  if (!defined($main::source_cache{$file})) {
+    if (100 < scalar keys(%main::source_cache)) {
+      # Clear the cache when it gets too big
+      $main::source_cache = ();
+    }
+
+    # Read all lines from the file
+    if (!open(FILE, "<$file")) {
+      print STDERR "$file: $!\n";
+      $main::source_cache{$file} = [];  # Cache the negative result
+      return undef;
+    }
+    my $lines = [];
+    push(@{$lines}, "");        # So we can use 1-based line numbers as indices
+    while (<FILE>) {
+      push(@{$lines}, $_);
+    }
+    close(FILE);
+
+    # Save the lines in the cache
+    $main::source_cache{$file} = $lines;
+  }
+
+  my $lines = $main::source_cache{$file};
+  if (($line < 0) || ($line > $#{$lines})) {
+    return undef;
+  } else {
+    return $lines->[$line];
+  }
+}
+
+# Print disassembly for one routine with interspersed source if available
+sub PrintDisassembledFunction {
+  my $prog = shift;
+  my $offset = shift;
+  my $routine = shift;
+  my $flat = shift;
+  my $cumulative = shift;
+  my $start_addr = shift;
+  my $end_addr = shift;
+  my $total = shift;
+
+  # Disassemble all instructions
+  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
+
+  # Make array of counts per instruction
+  my @flat_count = ();
+  my @cum_count = ();
+  my $flat_total = 0;
+  my $cum_total = 0;
+  foreach my $e (@instructions) {
+    # Add up counts for all address that fall inside this instruction
+    my $c1 = 0;
+    my $c2 = 0;
+    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
+      $c1 += GetEntry($flat, $a);
+      $c2 += GetEntry($cumulative, $a);
+    }
+    push(@flat_count, $c1);
+    push(@cum_count, $c2);
+    $flat_total += $c1;
+    $cum_total += $c2;
+  }
+
+  # Print header with total counts
+  printf("ROUTINE ====================== %s\n" .
+         "%6s %6s %s (flat, cumulative) %.1f%% of total\n",
+         ShortFunctionName($routine),
+         Unparse($flat_total),
+         Unparse($cum_total),
+         Units(),
+         ($cum_total * 100.0) / $total);
+
+  # Process instructions in order
+  my $current_file = "";
+  for (my $i = 0; $i <= $#instructions; ) {
+    my $e = $instructions[$i];
+
+    # Print the new file name whenever we switch files
+    if ($e->[1] ne $current_file) {
+      $current_file = $e->[1];
+      my $fname = $current_file;
+      $fname =~ s|^\./||;   # Trim leading "./"
+
+      # Shorten long file names
+      if (length($fname) >= 58) {
+        $fname = "..." . substr($fname, -55);
+      }
+      printf("-------------------- %s\n", $fname);
+    }
+
+    # TODO: Compute range of lines to print together to deal with
+    # small reorderings.
+    my $first_line = $e->[2];
+    my $last_line = $first_line;
+    my %flat_sum = ();
+    my %cum_sum = ();
+    for (my $l = $first_line; $l <= $last_line; $l++) {
+      $flat_sum{$l} = 0;
+      $cum_sum{$l} = 0;
+    }
+
+    # Find run of instructions for this range of source lines
+    my $first_inst = $i;
+    while (($i <= $#instructions) &&
+           ($instructions[$i]->[2] >= $first_line) &&
+           ($instructions[$i]->[2] <= $last_line)) {
+      $e = $instructions[$i];
+      $flat_sum{$e->[2]} += $flat_count[$i];
+      $cum_sum{$e->[2]} += $cum_count[$i];
+      $i++;
+    }
+    my $last_inst = $i - 1;
+
+    # Print source lines
+    for (my $l = $first_line; $l <= $last_line; $l++) {
+      my $line = SourceLine($current_file, $l);
+      if (!defined($line)) {
+        $line = "?\n";
+        next;
+      } else {
+        $line =~ s/^\s+//;
+      }
+      printf("%6s %6s %5d: %s",
+             UnparseAlt($flat_sum{$l}),
+             UnparseAlt($cum_sum{$l}),
+             $l,
+             $line);
+    }
+
+    # Print disassembly
+    for (my $x = $first_inst; $x <= $last_inst; $x++) {
+      my $e = $instructions[$x];
+      printf("%6s %6s    %8s: %6s\n",
+             UnparseAlt($flat_count[$x]),
+             UnparseAlt($cum_count[$x]),
+             UnparseAddress($offset, $e->[0]),
+             CleanDisassembly($e->[3]));
+    }
+  }
+}
+
+# Print DOT graph
+sub PrintDot {
+  my $prog = shift;
+  my $symbols = shift;
+  my $raw = shift;
+  my $flat = shift;
+  my $cumulative = shift;
+  my $overall_total = shift;
+
+  # Get total
+  my $local_total = TotalProfile($flat);
+  my $nodelimit = int($main::opt_nodefraction * $local_total);
+  my $edgelimit = int($main::opt_edgefraction * $local_total);
+  my $nodecount = $main::opt_nodecount;
+
+  # Find nodes to include
+  my @list = (sort { abs(GetEntry($cumulative, $b)) <=>
+                     abs(GetEntry($cumulative, $a))
+                     || $a cmp $b }
+              keys(%{$cumulative}));
+  my $last = $nodecount - 1;
+  if ($last > $#list) {
+    $last = $#list;
+  }
+  while (($last >= 0) &&
+         (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) {
+    $last--;
+  }
+  if ($last < 0) {
+    print STDERR "No nodes to print\n";
+    return 0;
+  }
+
+  if ($nodelimit > 0 || $edgelimit > 0) {
+    printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n",
+                   Unparse($nodelimit), Units(),
+                   Unparse($edgelimit), Units());
+  }
+
+  # Open DOT output file
+  my $output;
+  my $escaped_dot = ShellEscape(@DOT);
+  my $escaped_ps2pdf = ShellEscape(@PS2PDF);
+  if ($main::opt_gv) {
+    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "ps"));
+    $output = "| $escaped_dot -Tps2 >$escaped_outfile";
+  } elsif ($main::opt_evince) {
+    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "pdf"));
+    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - $escaped_outfile";
+  } elsif ($main::opt_ps) {
+    $output = "| $escaped_dot -Tps2";
+  } elsif ($main::opt_pdf) {
+    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - -";
+  } elsif ($main::opt_web || $main::opt_svg) {
+    # We need to post-process the SVG, so write to a temporary file always.
+    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "svg"));
+    $output = "| $escaped_dot -Tsvg >$escaped_outfile";
+  } elsif ($main::opt_gif) {
+    $output = "| $escaped_dot -Tgif";
+  } else {
+    $output = ">&STDOUT";
+  }
+  open(DOT, $output) || error("$output: $!\n");
+
+  # Title
+  printf DOT ("digraph \"%s; %s %s\" {\n",
+              $prog,
+              Unparse($overall_total),
+              Units());
+  if ($main::opt_pdf) {
+    # The output is more printable if we set the page size for dot.
+    printf DOT ("size=\"8,11\"\n");
+  }
+  printf DOT ("node [width=0.375,height=0.25];\n");
+
+  # Print legend
+  printf DOT ("Legend [shape=box,fontsize=24,shape=plaintext," .
+              "label=\"%s\\l%s\\l%s\\l%s\\l%s\\l\"];\n",
+              $prog,
+              sprintf("Total %s: %s", Units(), Unparse($overall_total)),
+              sprintf("Focusing on: %s", Unparse($local_total)),
+              sprintf("Dropped nodes with <= %s abs(%s)",
+                      Unparse($nodelimit), Units()),
+              sprintf("Dropped edges with <= %s %s",
+                      Unparse($edgelimit), Units())
+              );
+
+  # Print nodes
+  my %node = ();
+  my $nextnode = 1;
+  foreach my $a (@list[0..$last]) {
+    # Pick font size
+    my $f = GetEntry($flat, $a);
+    my $c = GetEntry($cumulative, $a);
+
+    my $fs = 8;
+    if ($local_total > 0) {
+      $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total)));
+    }
+
+    $node{$a} = $nextnode++;
+    my $sym = $a;
+    $sym =~ s/\s+/\\n/g;
+    $sym =~ s/::/\\n/g;
+
+    # Extra cumulative info to print for non-leaves
+    my $extra = "";
+    if ($f != $c) {
+      $extra = sprintf("\\rof %s (%s)",
+                       Unparse($c),
+                       Percent($c, $local_total));
+    }
+    my $style = "";
+    if ($main::opt_heapcheck) {
+      if ($f > 0) {
+        # make leak-causing nodes more visible (add a background)
+        $style = ",style=filled,fillcolor=gray"
+      } elsif ($f < 0) {
+        # make anti-leak-causing nodes (which almost never occur)
+        # stand out as well (triple border)
+        $style = ",peripheries=3"
+      }
+    }
+
+    printf DOT ("N%d [label=\"%s\\n%s (%s)%s\\r" .
+                "\",shape=box,fontsize=%.1f%s];\n",
+                $node{$a},
+                $sym,
+                Unparse($f),
+                Percent($f, $local_total),
+                $extra,
+                $fs,
+                $style,
+               );
+  }
+
+  # Get edges and counts per edge
+  my %edge = ();
+  my $n;
+  my $fullname_to_shortname_map = {};
+  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
+  foreach my $k (keys(%{$raw})) {
+    # TODO: omit low %age edges
+    $n = $raw->{$k};
+    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
+    for (my $i = 1; $i <= $#translated; $i++) {
+      my $src = $translated[$i];
+      my $dst = $translated[$i-1];
+      #next if ($src eq $dst);  # Avoid self-edges?
+      if (exists($node{$src}) && exists($node{$dst})) {
+        my $edge_label = "$src\001$dst";
+        if (!exists($edge{$edge_label})) {
+          $edge{$edge_label} = 0;
+        }
+        $edge{$edge_label} += $n;
+      }
+    }
+  }
+
+  # Print edges (process in order of decreasing counts)
+  my %indegree = ();   # Number of incoming edges added per node so far
+  my %outdegree = ();  # Number of outgoing edges added per node so far
+  foreach my $e (sort { $edge{$b} <=> $edge{$a} } keys(%edge)) {
+    my @x = split(/\001/, $e);
+    $n = $edge{$e};
+
+    # Initialize degree of kept incoming and outgoing edges if necessary
+    my $src = $x[0];
+    my $dst = $x[1];
+    if (!exists($outdegree{$src})) { $outdegree{$src} = 0; }
+    if (!exists($indegree{$dst})) { $indegree{$dst} = 0; }
+
+    my $keep;
+    if ($indegree{$dst} == 0) {
+      # Keep edge if needed for reachability
+      $keep = 1;
+    } elsif (abs($n) <= $edgelimit) {
+      # Drop if we are below --edgefraction
+      $keep = 0;
+    } elsif ($outdegree{$src} >= $main::opt_maxdegree ||
+             $indegree{$dst} >= $main::opt_maxdegree) {
+      # Keep limited number of in/out edges per node
+      $keep = 0;
+    } else {
+      $keep = 1;
+    }
+
+    if ($keep) {
+      $outdegree{$src}++;
+      $indegree{$dst}++;
+
+      # Compute line width based on edge count
+      my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0);
+      if ($fraction > 1) { $fraction = 1; }
+      my $w = $fraction * 2;
+      if ($w < 1 && ($main::opt_web || $main::opt_svg)) {
+        # SVG output treats line widths < 1 poorly.
+        $w = 1;
+      }
+
+      # Dot sometimes segfaults if given edge weights that are too large, so
+      # we cap the weights at a large value
+      my $edgeweight = abs($n) ** 0.7;
+      if ($edgeweight > 100000) { $edgeweight = 100000; }
+      $edgeweight = int($edgeweight);
+
+      my $style = sprintf("setlinewidth(%f)", $w);
+      if ($x[1] =~ m/\(inline\)/) {
+        $style .= ",dashed";
+      }
+
+      # Use a slightly squashed function of the edge count as the weight
+      printf DOT ("N%s -> N%s [label=%s, weight=%d, style=\"%s\"];\n",
+                  $node{$x[0]},
+                  $node{$x[1]},
+                  Unparse($n),
+                  $edgeweight,
+                  $style);
+    }
+  }
+
+  print DOT ("}\n");
+  close(DOT);
+
+  if ($main::opt_web || $main::opt_svg) {
+    # Rewrite SVG to be more usable inside web browser.
+    RewriteSvg(TempName($main::next_tmpfile, "svg"));
+  }
+
+  return 1;
+}
+
+sub RewriteSvg {
+  my $svgfile = shift;
+
+  open(SVG, $svgfile) || die "open temp svg: $!";
+  my @svg = <SVG>;
+  close(SVG);
+  unlink $svgfile;
+  my $svg = join('', @svg);
+
+  # Dot's SVG output is
+  #
+  #    <svg width="___" height="___"
+  #     viewBox="___" xmlns=...>
+  #    <g id="graph0" transform="...">
+  #    ...
+  #    </g>
+  #    </svg>
+  #
+  # Change it to
+  #
+  #    <svg width="100%" height="100%"
+  #     xmlns=...>
+  #    $svg_javascript
+  #    <g id="viewport" transform="translate(0,0)">
+  #    <g id="graph0" transform="...">
+  #    ...
+  #    </g>
+  #    </g>
+  #    </svg>
+
+  # Fix width, height; drop viewBox.
+  $svg =~ s/(?s)<svg width="[^"]+" height="[^"]+"(.*?)viewBox="[^"]+"/<svg width="100%" height="100%"$1/;
+
+  # Insert script, viewport <g> above first <g>
+  my $svg_javascript = SvgJavascript();
+  my $viewport = "<g id=\"viewport\" transform=\"translate(0,0)\">\n";
+  $svg =~ s/<g id="graph\d"/$svg_javascript$viewport$&/;
+
+  # Insert final </g> above </svg>.
+  $svg =~ s/(.*)(<\/svg>)/$1<\/g>$2/;
+  $svg =~ s/<g id="graph\d"(.*?)/<g id="viewport"$1/;
+
+  if ($main::opt_svg) {
+    # --svg: write to standard output.
+    print $svg;
+  } else {
+    # Write back to temporary file.
+    open(SVG, ">$svgfile") || die "open $svgfile: $!";
+    print SVG $svg;
+    close(SVG);
+  }
+}
+
+sub SvgJavascript {
+  return <<'EOF';
+<script type="text/ecmascript"><![CDATA[
+// SVGPan
+// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/
+// Local modification: if(true || ...) below to force panning, never moving.
+
+/**
+ *  SVGPan library 1.2
+ * ====================
+ *
+ * Given an unique existing element with id "viewport", including the
+ * the library into any SVG adds the following capabilities:
+ *
+ *  - Mouse panning
+ *  - Mouse zooming (using the wheel)
+ *  - Object dargging
+ *
+ * Known issues:
+ *
+ *  - Zooming (while panning) on Safari has still some issues
+ *
+ * Releases:
+ *
+ * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
+ *     Fixed a bug with browser mouse handler interaction
+ *
+ * 1.1, Wed Feb  3 17:39:33 GMT 2010, Zeng Xiaohui
+ *     Updated the zoom code to support the mouse wheel on Safari/Chrome
+ *
+ * 1.0, Andrea Leofreddi
+ *     First release
+ *
+ * This code is licensed under the following BSD license:
+ *
+ * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright notice, this list of
+ *       conditions and the following disclaimer.
+ *
+ *    2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *       of conditions and the following disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of Andrea Leofreddi.
+ */
+
+var root = document.documentElement;
+
+var state = 'none', stateTarget, stateOrigin, stateTf;
+
+setupHandlers(root);
+
+/**
+ * Register handlers
+ */
+function setupHandlers(root){
+       setAttributes(root, {
+               "onmouseup" : "add(evt)",
+               "onmousedown" : "handleMouseDown(evt)",
+               "onmousemove" : "handleMouseMove(evt)",
+               "onmouseup" : "handleMouseUp(evt)",
+               //"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element
+       });
+
+       if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)
+               window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari
+       else
+               window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others
+
+       var g = svgDoc.getElementById("svg");
+       g.width = "100%";
+       g.height = "100%";
+}
+
+/**
+ * Instance an SVGPoint object with given event coordinates.
+ */
+function getEventPoint(evt) {
+       var p = root.createSVGPoint();
+
+       p.x = evt.clientX;
+       p.y = evt.clientY;
+
+       return p;
+}
+
+/**
+ * Sets the current transform matrix of an element.
+ */
+function setCTM(element, matrix) {
+       var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
+
+       element.setAttribute("transform", s);
+}
+
+/**
+ * Dumps a matrix to a string (useful for debug).
+ */
+function dumpMatrix(matrix) {
+       var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n  " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n  0, 0, 1 ]";
+
+       return s;
+}
+
+/**
+ * Sets attributes of an element.
+ */
+function setAttributes(element, attributes){
+       for (i in attributes)
+               element.setAttributeNS(null, i, attributes[i]);
+}
+
+/**
+ * Handle mouse move event.
+ */
+function handleMouseWheel(evt) {
+       if(evt.preventDefault)
+               evt.preventDefault();
+
+       evt.returnValue = false;
+
+       var svgDoc = evt.target.ownerDocument;
+
+       var delta;
+
+       if(evt.wheelDelta)
+               delta = evt.wheelDelta / 3600; // Chrome/Safari
+       else
+               delta = evt.detail / -90; // Mozilla
+
+       var z = 1 + delta; // Zoom factor: 0.9/1.1
+
+       var g = svgDoc.getElementById("viewport");
+
+       var p = getEventPoint(evt);
+
+       p = p.matrixTransform(g.getCTM().inverse());
+
+       // Compute new scale matrix in current mouse position
+       var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);
+
+        setCTM(g, g.getCTM().multiply(k));
+
+       stateTf = stateTf.multiply(k.inverse());
+}
+
+/**
+ * Handle mouse move event.
+ */
+function handleMouseMove(evt) {
+       if(evt.preventDefault)
+               evt.preventDefault();
+
+       evt.returnValue = false;
+
+       var svgDoc = evt.target.ownerDocument;
+
+       var g = svgDoc.getElementById("viewport");
+
+       if(state == 'pan') {
+               // Pan mode
+               var p = getEventPoint(evt).matrixTransform(stateTf);
+
+               setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));
+       } else if(state == 'move') {
+               // Move mode
+               var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());
+
+               setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));
+
+               stateOrigin = p;
+       }
+}
+
+/**
+ * Handle click event.
+ */
+function handleMouseDown(evt) {
+       if(evt.preventDefault)
+               evt.preventDefault();
+
+       evt.returnValue = false;
+
+       var svgDoc = evt.target.ownerDocument;
+
+       var g = svgDoc.getElementById("viewport");
+
+       if(true || evt.target.tagName == "svg") {
+               // Pan mode
+               state = 'pan';
+
+               stateTf = g.getCTM().inverse();
+
+               stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
+       } else {
+               // Move mode
+               state = 'move';
+
+               stateTarget = evt.target;
+
+               stateTf = g.getCTM().inverse();
+
+               stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
+       }
+}
+
+/**
+ * Handle mouse button release event.
+ */
+function handleMouseUp(evt) {
+       if(evt.preventDefault)
+               evt.preventDefault();
+
+       evt.returnValue = false;
+
+       var svgDoc = evt.target.ownerDocument;
+
+       if(state == 'pan' || state == 'move') {
+               // Quit pan mode
+               state = '';
+       }
+}
+
+]]></script>
+EOF
+}
+
+# Provides a map from fullname to shortname for cases where the
+# shortname is ambiguous.  The symlist has both the fullname and
+# shortname for all symbols, which is usually fine, but sometimes --
+# such as overloaded functions -- two different fullnames can map to
+# the same shortname.  In that case, we use the address of the
+# function to disambiguate the two.  This function fills in a map that
+# maps fullnames to modified shortnames in such cases.  If a fullname
+# is not present in the map, the 'normal' shortname provided by the
+# symlist is the appropriate one to use.
+sub FillFullnameToShortnameMap {
+  my $symbols = shift;
+  my $fullname_to_shortname_map = shift;
+  my $shortnames_seen_once = {};
+  my $shortnames_seen_more_than_once = {};
+
+  foreach my $symlist (values(%{$symbols})) {
+    # TODO(csilvers): deal with inlined symbols too.
+    my $shortname = $symlist->[0];
+    my $fullname = $symlist->[2];
+    if ($fullname !~ /<[0-9a-fA-F]+>$/) {  # fullname doesn't end in an address
+      next;       # the only collisions we care about are when addresses differ
+    }
+    if (defined($shortnames_seen_once->{$shortname}) &&
+        $shortnames_seen_once->{$shortname} ne $fullname) {
+      $shortnames_seen_more_than_once->{$shortname} = 1;
+    } else {
+      $shortnames_seen_once->{$shortname} = $fullname;
+    }
+  }
+
+  foreach my $symlist (values(%{$symbols})) {
+    my $shortname = $symlist->[0];
+    my $fullname = $symlist->[2];
+    # TODO(csilvers): take in a list of addresses we care about, and only
+    # store in the map if $symlist->[1] is in that list.  Saves space.
+    next if defined($fullname_to_shortname_map->{$fullname});
+    if (defined($shortnames_seen_more_than_once->{$shortname})) {
+      if ($fullname =~ /<0*([^>]*)>$/) {   # fullname has address at end of it
+        $fullname_to_shortname_map->{$fullname} = "$shortname\@$1";
+      }
+    }
+  }
+}
+
+# Return a small number that identifies the argument.
+# Multiple calls with the same argument will return the same number.
+# Calls with different arguments will return different numbers.
+sub ShortIdFor {
+  my $key = shift;
+  my $id = $main::uniqueid{$key};
+  if (!defined($id)) {
+    $id = keys(%main::uniqueid) + 1;
+    $main::uniqueid{$key} = $id;
+  }
+  return $id;
+}
+
+# Translate a stack of addresses into a stack of symbols
+sub TranslateStack {
+  my $symbols = shift;
+  my $fullname_to_shortname_map = shift;
+  my $k = shift;
+
+  my @addrs = split(/\n/, $k);
+  my @result = ();
+  for (my $i = 0; $i <= $#addrs; $i++) {
+    my $a = $addrs[$i];
+
+    # Skip large addresses since they sometimes show up as fake entries on RH9
+    if (length($a) > 8 && $a gt "7fffffffffffffff") {
+      next;
+    }
+
+    if ($main::opt_disasm || $main::opt_list) {
+      # We want just the address for the key
+      push(@result, $a);
+      next;
+    }
+
+    my $symlist = $symbols->{$a};
+    if (!defined($symlist)) {
+      $symlist = [$a, "", $a];
+    }
+
+    # We can have a sequence of symbols for a particular entry
+    # (more than one symbol in the case of inlining).  Callers
+    # come before callees in symlist, so walk backwards since
+    # the translated stack should contain callees before callers.
+    for (my $j = $#{$symlist}; $j >= 2; $j -= 3) {
+      my $func = $symlist->[$j-2];
+      my $fileline = $symlist->[$j-1];
+      my $fullfunc = $symlist->[$j];
+      if (defined($fullname_to_shortname_map->{$fullfunc})) {
+        $func = $fullname_to_shortname_map->{$fullfunc};
+      }
+      if ($j > 2) {
+        $func = "$func (inline)";
+      }
+
+      # Do not merge nodes corresponding to Callback::Run since that
+      # causes confusing cycles in dot display.  Instead, we synthesize
+      # a unique name for this frame per caller.
+      if ($func =~ m/Callback.*::Run$/) {
+        my $caller = ($i > 0) ? $addrs[$i-1] : 0;
+        $func = "Run#" . ShortIdFor($caller);
+      }
+
+      if ($main::opt_addresses) {
+        push(@result, "$a $func $fileline");
+      } elsif ($main::opt_lines) {
+        if ($func eq '??' && $fileline eq '??:0') {
+          push(@result, "$a");
+        } else {
+          push(@result, "$func $fileline");
+        }
+      } elsif ($main::opt_functions) {
+        if ($func eq '??') {
+          push(@result, "$a");
+        } else {
+          push(@result, $func);
+        }
+      } elsif ($main::opt_files) {
+        if ($fileline eq '??:0' || $fileline eq '') {
+          push(@result, "$a");
+        } else {
+          my $f = $fileline;
+          $f =~ s/:\d+$//;
+          push(@result, $f);
+        }
+      } else {
+        push(@result, $a);
+        last;  # Do not print inlined info
+      }
+    }
+  }
+
+  # print join(",", @addrs), " => ", join(",", @result), "\n";
+  return @result;
+}
+
+# Generate percent string for a number and a total
+sub Percent {
+  my $num = shift;
+  my $tot = shift;
+  if ($tot != 0) {
+    return sprintf("%.1f%%", $num * 100.0 / $tot);
+  } else {
+    return ($num == 0) ? "nan" : (($num > 0) ? "+inf" : "-inf");
+  }
+}
+
+# Generate pretty-printed form of number
+sub Unparse {
+  my $num = shift;
+  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
+    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
+      return sprintf("%d", $num);
+    } else {
+      if ($main::opt_show_bytes) {
+        return sprintf("%d", $num);
+      } else {
+        return sprintf("%.1f", $num / 1048576.0);
+      }
+    }
+  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
+    return sprintf("%.3f", $num / 1e9); # Convert nanoseconds to seconds
+  } else {
+    return sprintf("%d", $num);
+  }
+}
+
+# Alternate pretty-printed form: 0 maps to "."
+sub UnparseAlt {
+  my $num = shift;
+  if ($num == 0) {
+    return ".";
+  } else {
+    return Unparse($num);
+  }
+}
+
+# Alternate pretty-printed form: 0 maps to ""
+sub HtmlPrintNumber {
+  my $num = shift;
+  if ($num == 0) {
+    return "";
+  } else {
+    return Unparse($num);
+  }
+}
+
+# Return output units
+sub Units {
+  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
+    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
+      return "objects";
+    } else {
+      if ($main::opt_show_bytes) {
+        return "B";
+      } else {
+        return "MB";
+      }
+    }
+  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
+    return "seconds";
+  } else {
+    return "samples";
+  }
+}
+
+##### Profile manipulation code #####
+
+# Generate flattened profile:
+# If count is charged to stack [a,b,c,d], in generated profile,
+# it will be charged to [a]
+sub FlatProfile {
+  my $profile = shift;
+  my $result = {};
+  foreach my $k (keys(%{$profile})) {
+    my $count = $profile->{$k};
+    my @addrs = split(/\n/, $k);
+    if ($#addrs >= 0) {
+      AddEntry($result, $addrs[0], $count);
+    }
+  }
+  return $result;
+}
+
+# Generate cumulative profile:
+# If count is charged to stack [a,b,c,d], in generated profile,
+# it will be charged to [a], [b], [c], [d]
+sub CumulativeProfile {
+  my $profile = shift;
+  my $result = {};
+  foreach my $k (keys(%{$profile})) {
+    my $count = $profile->{$k};
+    my @addrs = split(/\n/, $k);
+    foreach my $a (@addrs) {
+      AddEntry($result, $a, $count);
+    }
+  }
+  return $result;
+}
+
+# If the second-youngest PC on the stack is always the same, returns
+# that pc.  Otherwise, returns undef.
+sub IsSecondPcAlwaysTheSame {
+  my $profile = shift;
+
+  my $second_pc = undef;
+  foreach my $k (keys(%{$profile})) {
+    my @addrs = split(/\n/, $k);
+    if ($#addrs < 1) {
+      return undef;
+    }
+    if (not defined $second_pc) {
+      $second_pc = $addrs[1];
+    } else {
+      if ($second_pc ne $addrs[1]) {
+        return undef;
+      }
+    }
+  }
+  return $second_pc;
+}
+
+sub ExtractSymbolLocation {
+  my $symbols = shift;
+  my $address = shift;
+  # 'addr2line' outputs "??:0" for unknown locations; we do the
+  # same to be consistent.
+  my $location = "??:0:unknown";
+  if (exists $symbols->{$address}) {
+    my $file = $symbols->{$address}->[1];
+    if ($file eq "?") {
+      $file = "??:0"
+    }
+    $location = $file . ":" . $symbols->{$address}->[0];
+  }
+  return $location;
+}
+
+# Extracts a graph of calls.
+sub ExtractCalls {
+  my $symbols = shift;
+  my $profile = shift;
+
+  my $calls = {};
+  while( my ($stack_trace, $count) = each %$profile ) {
+    my @address = split(/\n/, $stack_trace);
+    my $destination = ExtractSymbolLocation($symbols, $address[0]);
+    AddEntry($calls, $destination, $count);
+    for (my $i = 1; $i <= $#address; $i++) {
+      my $source = ExtractSymbolLocation($symbols, $address[$i]);
+      my $call = "$source -> $destination";
+      AddEntry($calls, $call, $count);
+      $destination = $source;
+    }
+  }
+
+  return $calls;
+}
+
+sub RemoveUninterestingFrames {
+  my $symbols = shift;
+  my $profile = shift;
+
+  # List of function names to skip
+  my %skip = ();
+  my $skip_regexp = 'NOMATCH';
+  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
+    foreach my $name ('calloc',
+                      'cfree',
+                      'malloc',
+                      'free',
+                      'memalign',
+                      'posix_memalign',
+                      'aligned_alloc',
+                      'pvalloc',
+                      'valloc',
+                      'realloc',
+                      'mallocx', # jemalloc
+                      'rallocx', # jemalloc
+                      'xallocx', # jemalloc
+                      'dallocx', # jemalloc
+                      'sdallocx', # jemalloc
+                      'tc_calloc',
+                      'tc_cfree',
+                      'tc_malloc',
+                      'tc_free',
+                      'tc_memalign',
+                      'tc_posix_memalign',
+                      'tc_pvalloc',
+                      'tc_valloc',
+                      'tc_realloc',
+                      'tc_new',
+                      'tc_delete',
+                      'tc_newarray',
+                      'tc_deletearray',
+                      'tc_new_nothrow',
+                      'tc_newarray_nothrow',
+                      'do_malloc',
+                      '::do_malloc',   # new name -- got moved to an unnamed ns
+                      '::do_malloc_or_cpp_alloc',
+                      'DoSampledAllocation',
+                      'simple_alloc::allocate',
+                      '__malloc_alloc_template::allocate',
+                      '__builtin_delete',
+                      '__builtin_new',
+                      '__builtin_vec_delete',
+                      '__builtin_vec_new',
+                      'operator new',
+                      'operator new[]',
+                      # The entry to our memory-allocation routines on OS X
+                      'malloc_zone_malloc',
+                      'malloc_zone_calloc',
+                      'malloc_zone_valloc',
+                      'malloc_zone_realloc',
+                      'malloc_zone_memalign',
+                      'malloc_zone_free',
+                      # These mark the beginning/end of our custom sections
+                      '__start_google_malloc',
+                      '__stop_google_malloc',
+                      '__start_malloc_hook',
+                      '__stop_malloc_hook') {
+      $skip{$name} = 1;
+      $skip{"_" . $name} = 1;   # Mach (OS X) adds a _ prefix to everything
+    }
+    # TODO: Remove TCMalloc once everything has been
+    # moved into the tcmalloc:: namespace and we have flushed
+    # old code out of the system.
+    $skip_regexp = "TCMalloc|^tcmalloc::";
+  } elsif ($main::profile_type eq 'contention') {
+    foreach my $vname ('base::RecordLockProfileData',
+                       'base::SubmitMutexProfileData',
+                       'base::SubmitSpinLockProfileData',
+                       'Mutex::Unlock',
+                       'Mutex::UnlockSlow',
+                       'Mutex::ReaderUnlock',
+                       'MutexLock::~MutexLock',
+                       'SpinLock::Unlock',
+                       'SpinLock::SlowUnlock',
+                       'SpinLockHolder::~SpinLockHolder') {
+      $skip{$vname} = 1;
+    }
+  } elsif ($main::profile_type eq 'cpu') {
+    # Drop signal handlers used for CPU profile collection
+    # TODO(dpeng): this should not be necessary; it's taken
+    # care of by the general 2nd-pc mechanism below.
+    foreach my $name ('ProfileData::Add',           # historical
+                      'ProfileData::prof_handler',  # historical
+                      'CpuProfiler::prof_handler',
+                      '__FRAME_END__',
+                      '__pthread_sighandler',
+                      '__restore') {
+      $skip{$name} = 1;
+    }
+  } else {
+    # Nothing skipped for unknown types
+  }
+
+  if ($main::profile_type eq 'cpu') {
+    # If all the second-youngest program counters are the same,
+    # this STRONGLY suggests that it is an artifact of measurement,
+    # i.e., stack frames pushed by the CPU profiler signal handler.
+    # Hence, we delete them.
+    # (The topmost PC is read from the signal structure, not from
+    # the stack, so it does not get involved.)
+    while (my $second_pc = IsSecondPcAlwaysTheSame($profile)) {
+      my $result = {};
+      my $func = '';
+      if (exists($symbols->{$second_pc})) {
+        $second_pc = $symbols->{$second_pc}->[0];
+      }
+      print STDERR "Removing $second_pc from all stack traces.\n";
+      foreach my $k (keys(%{$profile})) {
+        my $count = $profile->{$k};
+        my @addrs = split(/\n/, $k);
+        splice @addrs, 1, 1;
+        my $reduced_path = join("\n", @addrs);
+        AddEntry($result, $reduced_path, $count);
+      }
+      $profile = $result;
+    }
+  }
+
+  my $result = {};
+  foreach my $k (keys(%{$profile})) {
+    my $count = $profile->{$k};
+    my @addrs = split(/\n/, $k);
+    my @path = ();
+    foreach my $a (@addrs) {
+      if (exists($symbols->{$a})) {
+        my $func = $symbols->{$a}->[0];
+        if ($skip{$func} || ($func =~ m/$skip_regexp/)) {
+          # Throw away the portion of the backtrace seen so far, under the
+          # assumption that previous frames were for functions internal to the
+          # allocator.
+          @path = ();
+          next;
+        }
+      }
+      push(@path, $a);
+    }
+    my $reduced_path = join("\n", @path);
+    AddEntry($result, $reduced_path, $count);
+  }
+  return $result;
+}
+
+# Reduce profile to granularity given by user
+sub ReduceProfile {
+  my $symbols = shift;
+  my $profile = shift;
+  my $result = {};
+  my $fullname_to_shortname_map = {};
+  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
+  foreach my $k (keys(%{$profile})) {
+    my $count = $profile->{$k};
+    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
+    my @path = ();
+    my %seen = ();
+    $seen{''} = 1;      # So that empty keys are skipped
+    foreach my $e (@translated) {
+      # To avoid double-counting due to recursion, skip a stack-trace
+      # entry if it has already been seen
+      if (!$seen{$e}) {
+        $seen{$e} = 1;
+        push(@path, $e);
+      }
+    }
+    my $reduced_path = join("\n", @path);
+    AddEntry($result, $reduced_path, $count);
+  }
+  return $result;
+}
+
+# Does the specified symbol array match the regexp?
+sub SymbolMatches {
+  my $sym = shift;
+  my $re = shift;
+  if (defined($sym)) {
+    for (my $i = 0; $i < $#{$sym}; $i += 3) {
+      if ($sym->[$i] =~ m/$re/ || $sym->[$i+1] =~ m/$re/) {
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
+
+# Focus only on paths involving specified regexps
+sub FocusProfile {
+  my $symbols = shift;
+  my $profile = shift;
+  my $focus = shift;
+  my $result = {};
+  foreach my $k (keys(%{$profile})) {
+    my $count = $profile->{$k};
+    my @addrs = split(/\n/, $k);
+    foreach my $a (@addrs) {
+      # Reply if it matches either the address/shortname/fileline
+      if (($a =~ m/$focus/) || SymbolMatches($symbols->{$a}, $focus)) {
+        AddEntry($result, $k, $count);
+        last;
+      }
+    }
+  }
+  return $result;
+}
+
+# Focus only on paths not involving specified regexps
+sub IgnoreProfile {
+  my $symbols = shift;
+  my $profile = shift;
+  my $ignore = shift;
+  my $result = {};
+  foreach my $k (keys(%{$profile})) {
+    my $count = $profile->{$k};
+    my @addrs = split(/\n/, $k);
+    my $matched = 0;
+    foreach my $a (@addrs) {
+      # Reply if it matches either the address/shortname/fileline
+      if (($a =~ m/$ignore/) || SymbolMatches($symbols->{$a}, $ignore)) {
+        $matched = 1;
+        last;
+      }
+    }
+    if (!$matched) {
+      AddEntry($result, $k, $count);
+    }
+  }
+  return $result;
+}
+
+# Get total count in profile
+sub TotalProfile {
+  my $profile = shift;
+  my $result = 0;
+  foreach my $k (keys(%{$profile})) {
+    $result += $profile->{$k};
+  }
+  return $result;
+}
+
+# Add A to B
+sub AddProfile {
+  my $A = shift;
+  my $B = shift;
+
+  my $R = {};
+  # add all keys in A
+  foreach my $k (keys(%{$A})) {
+    my $v = $A->{$k};
+    AddEntry($R, $k, $v);
+  }
+  # add all keys in B
+  foreach my $k (keys(%{$B})) {
+    my $v = $B->{$k};
+    AddEntry($R, $k, $v);
+  }
+  return $R;
+}
+
+# Merges symbol maps
+sub MergeSymbols {
+  my $A = shift;
+  my $B = shift;
+
+  my $R = {};
+  foreach my $k (keys(%{$A})) {
+    $R->{$k} = $A->{$k};
+  }
+  if (defined($B)) {
+    foreach my $k (keys(%{$B})) {
+      $R->{$k} = $B->{$k};
+    }
+  }
+  return $R;
+}
+
+
+# Add A to B
+sub AddPcs {
+  my $A = shift;
+  my $B = shift;
+
+  my $R = {};
+  # add all keys in A
+  foreach my $k (keys(%{$A})) {
+    $R->{$k} = 1
+  }
+  # add all keys in B
+  foreach my $k (keys(%{$B})) {
+    $R->{$k} = 1
+  }
+  return $R;
+}
+
+# Subtract B from A
+sub SubtractProfile {
+  my $A = shift;
+  my $B = shift;
+
+  my $R = {};
+  foreach my $k (keys(%{$A})) {
+    my $v = $A->{$k} - GetEntry($B, $k);
+    if ($v < 0 && $main::opt_drop_negative) {
+      $v = 0;
+    }
+    AddEntry($R, $k, $v);
+  }
+  if (!$main::opt_drop_negative) {
+    # Take care of when subtracted profile has more entries
+    foreach my $k (keys(%{$B})) {
+      if (!exists($A->{$k})) {
+        AddEntry($R, $k, 0 - $B->{$k});
+      }
+    }
+  }
+  return $R;
+}
+
+# Get entry from profile; zero if not present
+sub GetEntry {
+  my $profile = shift;
+  my $k = shift;
+  if (exists($profile->{$k})) {
+    return $profile->{$k};
+  } else {
+    return 0;
+  }
+}
+
+# Add entry to specified profile
+sub AddEntry {
+  my $profile = shift;
+  my $k = shift;
+  my $n = shift;
+  if (!exists($profile->{$k})) {
+    $profile->{$k} = 0;
+  }
+  $profile->{$k} += $n;
+}
+
+# Add a stack of entries to specified profile, and add them to the $pcs
+# list.
+sub AddEntries {
+  my $profile = shift;
+  my $pcs = shift;
+  my $stack = shift;
+  my $count = shift;
+  my @k = ();
+
+  foreach my $e (split(/\s+/, $stack)) {
+    my $pc = HexExtend($e);
+    $pcs->{$pc} = 1;
+    push @k, $pc;
+  }
+  AddEntry($profile, (join "\n", @k), $count);
+}
+
+##### Code to profile a server dynamically #####
+
+sub CheckSymbolPage {
+  my $url = SymbolPageURL();
+  my $command = ShellEscape(@URL_FETCHER, $url);
+  open(SYMBOL, "$command |") or error($command);
+  my $line = <SYMBOL>;
+  $line =~ s/\r//g;         # turn windows-looking lines into unix-looking lines
+  close(SYMBOL);
+  unless (defined($line)) {
+    error("$url doesn't exist\n");
+  }
+
+  if ($line =~ /^num_symbols:\s+(\d+)$/) {
+    if ($1 == 0) {
+      error("Stripped binary. No symbols available.\n");
+    }
+  } else {
+    error("Failed to get the number of symbols from $url\n");
+  }
+}
+
+sub IsProfileURL {
+  my $profile_name = shift;
+  if (-f $profile_name) {
+    printf STDERR "Using local file $profile_name.\n";
+    return 0;
+  }
+  return 1;
+}
+
+sub ParseProfileURL {
+  my $profile_name = shift;
+
+  if (!defined($profile_name) || $profile_name eq "") {
+    return ();
+  }
+
+  # Split profile URL - matches all non-empty strings, so no test.
+  $profile_name =~ m,^(https?://)?([^/]+)(.*?)(/|$PROFILES)?$,;
+
+  my $proto = $1 || "http://";
+  my $hostport = $2;
+  my $prefix = $3;
+  my $profile = $4 || "/";
+
+  my $host = $hostport;
+  $host =~ s/:.*//;
+
+  my $baseurl = "$proto$hostport$prefix";
+  return ($host, $baseurl, $profile);
+}
+
+# We fetch symbols from the first profile argument.
+sub SymbolPageURL {
+  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
+  return "$baseURL$SYMBOL_PAGE";
+}
+
+sub FetchProgramName() {
+  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
+  my $url = "$baseURL$PROGRAM_NAME_PAGE";
+  my $command_line = ShellEscape(@URL_FETCHER, $url);
+  open(CMDLINE, "$command_line |") or error($command_line);
+  my $cmdline = <CMDLINE>;
+  $cmdline =~ s/\r//g;   # turn windows-looking lines into unix-looking lines
+  close(CMDLINE);
+  error("Failed to get program name from $url\n") unless defined($cmdline);
+  $cmdline =~ s/\x00.+//;  # Remove argv[1] and latters.
+  $cmdline =~ s!\n!!g;  # Remove LFs.
+  return $cmdline;
+}
+
+# Gee, curl's -L (--location) option isn't reliable at least
+# with its 7.12.3 version.  Curl will forget to post data if
+# there is a redirection.  This function is a workaround for
+# curl.  Redirection happens on borg hosts.
+sub ResolveRedirectionForCurl {
+  my $url = shift;
+  my $command_line = ShellEscape(@URL_FETCHER, "--head", $url);
+  open(CMDLINE, "$command_line |") or error($command_line);
+  while (<CMDLINE>) {
+    s/\r//g;         # turn windows-looking lines into unix-looking lines
+    if (/^Location: (.*)/) {
+      $url = $1;
+    }
+  }
+  close(CMDLINE);
+  return $url;
+}
+
+# Add a timeout flat to URL_FETCHER.  Returns a new list.
+sub AddFetchTimeout {
+  my $timeout = shift;
+  my @fetcher = shift;
+  if (defined($timeout)) {
+    if (join(" ", @fetcher) =~ m/\bcurl -s/) {
+      push(@fetcher, "--max-time", sprintf("%d", $timeout));
+    } elsif (join(" ", @fetcher) =~ m/\brpcget\b/) {
+      push(@fetcher, sprintf("--deadline=%d", $timeout));
+    }
+  }
+  return @fetcher;
+}
+
+# Reads a symbol map from the file handle name given as $1, returning
+# the resulting symbol map.  Also processes variables relating to symbols.
+# Currently, the only variable processed is 'binary=<value>' which updates
+# $main::prog to have the correct program name.
+sub ReadSymbols {
+  my $in = shift;
+  my $map = {};
+  while (<$in>) {
+    s/\r//g;         # turn windows-looking lines into unix-looking lines
+    # Removes all the leading zeroes from the symbols, see comment below.
+    if (m/^0x0*([0-9a-f]+)\s+(.+)/) {
+      $map->{$1} = $2;
+    } elsif (m/^---/) {
+      last;
+    } elsif (m/^([a-z][^=]*)=(.*)$/ ) {
+      my ($variable, $value) = ($1, $2);
+      for ($variable, $value) {
+        s/^\s+//;
+        s/\s+$//;
+      }
+      if ($variable eq "binary") {
+        if ($main::prog ne $UNKNOWN_BINARY && $main::prog ne $value) {
+          printf STDERR ("Warning: Mismatched binary name '%s', using '%s'.\n",
+                         $main::prog, $value);
+        }
+        $main::prog = $value;
+      } else {
+        printf STDERR ("Ignoring unknown variable in symbols list: " .
+            "'%s' = '%s'\n", $variable, $value);
+      }
+    }
+  }
+  return $map;
+}
+
+# Fetches and processes symbols to prepare them for use in the profile output
+# code.  If the optional 'symbol_map' arg is not given, fetches symbols from
+# $SYMBOL_PAGE for all PC values found in profile.  Otherwise, the raw symbols
+# are assumed to have already been fetched into 'symbol_map' and are simply
+# extracted and processed.
+sub FetchSymbols {
+  my $pcset = shift;
+  my $symbol_map = shift;
+
+  my %seen = ();
+  my @pcs = grep { !$seen{$_}++ } keys(%$pcset);  # uniq
+
+  if (!defined($symbol_map)) {
+    my $post_data = join("+", sort((map {"0x" . "$_"} @pcs)));
+
+    open(POSTFILE, ">$main::tmpfile_sym");
+    print POSTFILE $post_data;
+    close(POSTFILE);
+
+    my $url = SymbolPageURL();
+
+    my $command_line;
+    if (join(" ", @URL_FETCHER) =~ m/\bcurl -s/) {
+      $url = ResolveRedirectionForCurl($url);
+      $command_line = ShellEscape(@URL_FETCHER, "-d", "\@$main::tmpfile_sym",
+                                  $url);
+    } else {
+      $command_line = (ShellEscape(@URL_FETCHER, "--post", $url)
+                       . " < " . ShellEscape($main::tmpfile_sym));
+    }
+    # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols.
+    my $escaped_cppfilt = ShellEscape($obj_tool_map{"c++filt"});
+    open(SYMBOL, "$command_line | $escaped_cppfilt |") or error($command_line);
+    $symbol_map = ReadSymbols(*SYMBOL{IO});
+    close(SYMBOL);
+  }
+
+  my $symbols = {};
+  foreach my $pc (@pcs) {
+    my $fullname;
+    # For 64 bits binaries, symbols are extracted with 8 leading zeroes.
+    # Then /symbol reads the long symbols in as uint64, and outputs
+    # the result with a "0x%08llx" format which get rid of the zeroes.
+    # By removing all the leading zeroes in both $pc and the symbols from
+    # /symbol, the symbols match and are retrievable from the map.
+    my $shortpc = $pc;
+    $shortpc =~ s/^0*//;
+    # Each line may have a list of names, which includes the function
+    # and also other functions it has inlined.  They are separated (in
+    # PrintSymbolizedProfile), by --, which is illegal in function names.
+    my $fullnames;
+    if (defined($symbol_map->{$shortpc})) {
+      $fullnames = $symbol_map->{$shortpc};
+    } else {
+      $fullnames = "0x" . $pc;  # Just use addresses
+    }
+    my $sym = [];
+    $symbols->{$pc} = $sym;
+    foreach my $fullname (split("--", $fullnames)) {
+      my $name = ShortFunctionName($fullname);
+      push(@{$sym}, $name, "?", $fullname);
+    }
+  }
+  return $symbols;
+}
+
+sub BaseName {
+  my $file_name = shift;
+  $file_name =~ s!^.*/!!;  # Remove directory name
+  return $file_name;
+}
+
+sub MakeProfileBaseName {
+  my ($binary_name, $profile_name) = @_;
+  my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
+  my $binary_shortname = BaseName($binary_name);
+  return sprintf("%s.%s.%s",
+                 $binary_shortname, $main::op_time, $host);
+}
+
+sub FetchDynamicProfile {
+  my $binary_name = shift;
+  my $profile_name = shift;
+  my $fetch_name_only = shift;
+  my $encourage_patience = shift;
+
+  if (!IsProfileURL($profile_name)) {
+    return $profile_name;
+  } else {
+    my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
+    if ($path eq "" || $path eq "/") {
+      # Missing type specifier defaults to cpu-profile
+      $path = $PROFILE_PAGE;
+    }
+
+    my $profile_file = MakeProfileBaseName($binary_name, $profile_name);
+
+    my $url = "$baseURL$path";
+    my $fetch_timeout = undef;
+    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE/) {
+      if ($path =~ m/[?]/) {
+        $url .= "&";
+      } else {
+        $url .= "?";
+      }
+      $url .= sprintf("seconds=%d", $main::opt_seconds);
+      $fetch_timeout = $main::opt_seconds * 1.01 + 60;
+    } else {
+      # For non-CPU profiles, we add a type-extension to
+      # the target profile file name.
+      my $suffix = $path;
+      $suffix =~ s,/,.,g;
+      $profile_file .= $suffix;
+    }
+
+    my $profile_dir = $ENV{"JEPROF_TMPDIR"} || ($ENV{HOME} . "/jeprof");
+    if (! -d $profile_dir) {
+      mkdir($profile_dir)
+          || die("Unable to create profile directory $profile_dir: $!\n");
+    }
+    my $tmp_profile = "$profile_dir/.tmp.$profile_file";
+    my $real_profile = "$profile_dir/$profile_file";
+
+    if ($fetch_name_only > 0) {
+      return $real_profile;
+    }
+
+    my @fetcher = AddFetchTimeout($fetch_timeout, @URL_FETCHER);
+    my $cmd = ShellEscape(@fetcher, $url) . " > " . ShellEscape($tmp_profile);
+    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE|$CENSUSPROFILE_PAGE/){
+      print STDERR "Gathering CPU profile from $url for $main::opt_seconds seconds to\n  ${real_profile}\n";
+      if ($encourage_patience) {
+        print STDERR "Be patient...\n";
+      }
+    } else {
+      print STDERR "Fetching $path profile from $url to\n  ${real_profile}\n";
+    }
+
+    (system($cmd) == 0) || error("Failed to get profile: $cmd: $!\n");
+    (system("mv", $tmp_profile, $real_profile) == 0) || error("Unable to rename profile\n");
+    print STDERR "Wrote profile to $real_profile\n";
+    $main::collected_profile = $real_profile;
+    return $main::collected_profile;
+  }
+}
+
+# Collect profiles in parallel
+sub FetchDynamicProfiles {
+  my $items = scalar(@main::pfile_args);
+  my $levels = log($items) / log(2);
+
+  if ($items == 1) {
+    $main::profile_files[0] = FetchDynamicProfile($main::prog, $main::pfile_args[0], 0, 1);
+  } else {
+    # math rounding issues
+    if ((2 ** $levels) < $items) {
+     $levels++;
+    }
+    my $count = scalar(@main::pfile_args);
+    for (my $i = 0; $i < $count; $i++) {
+      $main::profile_files[$i] = FetchDynamicProfile($main::prog, $main::pfile_args[$i], 1, 0);
+    }
+    print STDERR "Fetching $count profiles, Be patient...\n";
+    FetchDynamicProfilesRecurse($levels, 0, 0);
+    $main::collected_profile = join(" \\\n    ", @main::profile_files);
+  }
+}
+
+# Recursively fork a process to get enough processes
+# collecting profiles
+sub FetchDynamicProfilesRecurse {
+  my $maxlevel = shift;
+  my $level = shift;
+  my $position = shift;
+
+  if (my $pid = fork()) {
+    $position = 0 | ($position << 1);
+    TryCollectProfile($maxlevel, $level, $position);
+    wait;
+  } else {
+    $position = 1 | ($position << 1);
+    TryCollectProfile($maxlevel, $level, $position);
+    cleanup();
+    exit(0);
+  }
+}
+
+# Collect a single profile
+sub TryCollectProfile {
+  my $maxlevel = shift;
+  my $level = shift;
+  my $position = shift;
+
+  if ($level >= ($maxlevel - 1)) {
+    if ($position < scalar(@main::pfile_args)) {
+      FetchDynamicProfile($main::prog, $main::pfile_args[$position], 0, 0);
+    }
+  } else {
+    FetchDynamicProfilesRecurse($maxlevel, $level+1, $position);
+  }
+}
+
+##### Parsing code #####
+
+# Provide a small streaming-read module to handle very large
+# cpu-profile files.  Stream in chunks along a sliding window.
+# Provides an interface to get one 'slot', correctly handling
+# endian-ness differences.  A slot is one 32-bit or 64-bit word
+# (depending on the input profile).  We tell endianness and bit-size
+# for the profile by looking at the first 8 bytes: in cpu profiles,
+# the second slot is always 3 (we'll accept anything that's not 0).
+BEGIN {
+  package CpuProfileStream;
+
+  sub new {
+    my ($class, $file, $fname) = @_;
+    my $self = { file        => $file,
+                 base        => 0,
+                 stride      => 512 * 1024,   # must be a multiple of bitsize/8
+                 slots       => [],
+                 unpack_code => "",           # N for big-endian, V for little
+                 perl_is_64bit => 1,          # matters if profile is 64-bit
+    };
+    bless $self, $class;
+    # Let unittests adjust the stride
+    if ($main::opt_test_stride > 0) {
+      $self->{stride} = $main::opt_test_stride;
+    }
+    # Read the first two slots to figure out bitsize and endianness.
+    my $slots = $self->{slots};
+    my $str;
+    read($self->{file}, $str, 8);
+    # Set the global $address_length based on what we see here.
+    # 8 is 32-bit (8 hexadecimal chars); 16 is 64-bit (16 hexadecimal chars).
+    $address_length = ($str eq (chr(0)x8)) ? 16 : 8;
+    if ($address_length == 8) {
+      if (substr($str, 6, 2) eq chr(0)x2) {
+        $self->{unpack_code} = 'V';  # Little-endian.
+      } elsif (substr($str, 4, 2) eq chr(0)x2) {
+        $self->{unpack_code} = 'N';  # Big-endian
+      } else {
+        ::error("$fname: header size >= 2**16\n");
+      }
+      @$slots = unpack($self->{unpack_code} . "*", $str);
+    } else {
+      # If we're a 64-bit profile, check if we're a 64-bit-capable
+      # perl.  Otherwise, each slot will be represented as a float
+      # instead of an int64, losing precision and making all the
+      # 64-bit addresses wrong.  We won't complain yet, but will
+      # later if we ever see a value that doesn't fit in 32 bits.
+      my $has_q = 0;
+      eval { $has_q = pack("Q", "1") ? 1 : 1; };
+      if (!$has_q) {
+        $self->{perl_is_64bit} = 0;
+      }
+      read($self->{file}, $str, 8);
+      if (substr($str, 4, 4) eq chr(0)x4) {
+        # We'd love to use 'Q', but it's a) not universal, b) not endian-proof.
+        $self->{unpack_code} = 'V';  # Little-endian.
+      } elsif (substr($str, 0, 4) eq chr(0)x4) {
+        $self->{unpack_code} = 'N';  # Big-endian
+      } else {
+        ::error("$fname: header size >= 2**32\n");
+      }
+      my @pair = unpack($self->{unpack_code} . "*", $str);
+      # Since we know one of the pair is 0, it's fine to just add them.
+      @$slots = (0, $pair[0] + $pair[1]);
+    }
+    return $self;
+  }
+
+  # Load more data when we access slots->get(X) which is not yet in memory.
+  sub overflow {
+    my ($self) = @_;
+    my $slots = $self->{slots};
+    $self->{base} += $#$slots + 1;   # skip over data we're replacing
+    my $str;
+    read($self->{file}, $str, $self->{stride});
+    if ($address_length == 8) {      # the 32-bit case
+      # This is the easy case: unpack provides 32-bit unpacking primitives.
+      @$slots = unpack($self->{unpack_code} . "*", $str);
+    } else {
+      # We need to unpack 32 bits at a time and combine.
+      my @b32_values = unpack($self->{unpack_code} . "*", $str);
+      my @b64_values = ();
+      for (my $i = 0; $i < $#b32_values; $i += 2) {
+        # TODO(csilvers): if this is a 32-bit perl, the math below
+        #    could end up in a too-large int, which perl will promote
+        #    to a double, losing necessary precision.  Deal with that.
+        #    Right now, we just die.
+        my ($lo, $hi) = ($b32_values[$i], $b32_values[$i+1]);
+        if ($self->{unpack_code} eq 'N') {    # big-endian
+          ($lo, $hi) = ($hi, $lo);
+        }
+        my $value = $lo + $hi * (2**32);
+        if (!$self->{perl_is_64bit} &&   # check value is exactly represented
+            (($value % (2**32)) != $lo || int($value / (2**32)) != $hi)) {
+          ::error("Need a 64-bit perl to process this 64-bit profile.\n");
+        }
+        push(@b64_values, $value);
+      }
+      @$slots = @b64_values;
+    }
+  }
+
+  # Access the i-th long in the file (logically), or -1 at EOF.
+  sub get {
+    my ($self, $idx) = @_;
+    my $slots = $self->{slots};
+    while ($#$slots >= 0) {
+      if ($idx < $self->{base}) {
+        # The only time we expect a reference to $slots[$i - something]
+        # after referencing $slots[$i] is reading the very first header.
+        # Since $stride > |header|, that shouldn't cause any lookback
+        # errors.  And everything after the header is sequential.
+        print STDERR "Unexpected look-back reading CPU profile";
+        return -1;   # shrug, don't know what better to return
+      } elsif ($idx > $self->{base} + $#$slots) {
+        $self->overflow();
+      } else {
+        return $slots->[$idx - $self->{base}];
+      }
+    }
+    # If we get here, $slots is [], which means we've reached EOF
+    return -1;  # unique since slots is supposed to hold unsigned numbers
+  }
+}
+
+# Reads the top, 'header' section of a profile, and returns the last
+# line of the header, commonly called a 'header line'.  The header
+# section of a profile consists of zero or more 'command' lines that
+# are instructions to jeprof, which jeprof executes when reading the
+# header.  All 'command' lines start with a %.  After the command
+# lines is the 'header line', which is a profile-specific line that
+# indicates what type of profile it is, and perhaps other global
+# information about the profile.  For instance, here's a header line
+# for a heap profile:
+#   heap profile:     53:    38236 [  5525:  1284029] @ heapprofile
+# For historical reasons, the CPU profile does not contain a text-
+# readable header line.  If the profile looks like a CPU profile,
+# this function returns "".  If no header line could be found, this
+# function returns undef.
+#
+# The following commands are recognized:
+#   %warn -- emit the rest of this line to stderr, prefixed by 'WARNING:'
+#
+# The input file should be in binmode.
+sub ReadProfileHeader {
+  local *PROFILE = shift;
+  my $firstchar = "";
+  my $line = "";
+  read(PROFILE, $firstchar, 1);
+  seek(PROFILE, -1, 1);                    # unread the firstchar
+  if ($firstchar !~ /[[:print:]]/) {       # is not a text character
+    return "";
+  }
+  while (defined($line = <PROFILE>)) {
+    $line =~ s/\r//g;   # turn windows-looking lines into unix-looking lines
+    if ($line =~ /^%warn\s+(.*)/) {        # 'warn' command
+      # Note this matches both '%warn blah\n' and '%warn\n'.
+      print STDERR "WARNING: $1\n";        # print the rest of the line
+    } elsif ($line =~ /^%/) {
+      print STDERR "Ignoring unknown command from profile header: $line";
+    } else {
+      # End of commands, must be the header line.
+      return $line;
+    }
+  }
+  return undef;     # got to EOF without seeing a header line
+}
+
+sub IsSymbolizedProfileFile {
+  my $file_name = shift;
+  if (!(-e $file_name) || !(-r $file_name)) {
+    return 0;
+  }
+  # Check if the file contains a symbol-section marker.
+  open(TFILE, "<$file_name");
+  binmode TFILE;
+  my $firstline = ReadProfileHeader(*TFILE);
+  close(TFILE);
+  if (!$firstline) {
+    return 0;
+  }
+  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
+  my $symbol_marker = $&;
+  return $firstline =~ /^--- *$symbol_marker/;
+}
+
+# Parse profile generated by common/profiler.cc and return a reference
+# to a map:
+#      $result->{version}     Version number of profile file
+#      $result->{period}      Sampling period (in microseconds)
+#      $result->{profile}     Profile object
+#      $result->{threads}     Map of thread IDs to profile objects
+#      $result->{map}         Memory map info from profile
+#      $result->{pcs}         Hash of all PC values seen, key is hex address
+sub ReadProfile {
+  my $prog = shift;
+  my $fname = shift;
+  my $result;            # return value
+
+  $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
+  my $contention_marker = $&;
+  $GROWTH_PAGE  =~ m,[^/]+$,;    # matches everything after the last slash
+  my $growth_marker = $&;
+  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
+  my $symbol_marker = $&;
+  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
+  my $profile_marker = $&;
+
+  # Look at first line to see if it is a heap or a CPU profile.
+  # CPU profile may start with no header at all, and just binary data
+  # (starting with \0\0\0\0) -- in that case, don't try to read the
+  # whole firstline, since it may be gigabytes(!) of data.
+  open(PROFILE, "<$fname") || error("$fname: $!\n");
+  binmode PROFILE;      # New perls do UTF-8 processing
+  my $header = ReadProfileHeader(*PROFILE);
+  if (!defined($header)) {   # means "at EOF"
+    error("Profile is empty.\n");
+  }
+
+  my $symbols;
+  if ($header =~ m/^--- *$symbol_marker/o) {
+    # Verify that the user asked for a symbolized profile
+    if (!$main::use_symbolized_profile) {
+      # we have both a binary and symbolized profiles, abort
+      error("FATAL ERROR: Symbolized profile\n   $fname\ncannot be used with " .
+            "a binary arg. Try again without passing\n   $prog\n");
+    }
+    # Read the symbol section of the symbolized profile file.
+    $symbols = ReadSymbols(*PROFILE{IO});
+    # Read the next line to get the header for the remaining profile.
+    $header = ReadProfileHeader(*PROFILE) || "";
+  }
+
+  $main::profile_type = '';
+  if ($header =~ m/^heap profile:.*$growth_marker/o) {
+    $main::profile_type = 'growth';
+    $result =  ReadHeapProfile($prog, *PROFILE, $header);
+  } elsif ($header =~ m/^heap profile:/) {
+    $main::profile_type = 'heap';
+    $result =  ReadHeapProfile($prog, *PROFILE, $header);
+  } elsif ($header =~ m/^heap/) {
+    $main::profile_type = 'heap';
+    $result = ReadThreadedHeapProfile($prog, $fname, $header);
+  } elsif ($header =~ m/^--- *$contention_marker/o) {
+    $main::profile_type = 'contention';
+    $result = ReadSynchProfile($prog, *PROFILE);
+  } elsif ($header =~ m/^--- *Stacks:/) {
+    print STDERR
+      "Old format contention profile: mistakenly reports " .
+      "condition variable signals as lock contentions.\n";
+    $main::profile_type = 'contention';
+    $result = ReadSynchProfile($prog, *PROFILE);
+  } elsif ($header =~ m/^--- *$profile_marker/) {
+    # the binary cpu profile data starts immediately after this line
+    $main::profile_type = 'cpu';
+    $result = ReadCPUProfile($prog, $fname, *PROFILE);
+  } else {
+    if (defined($symbols)) {
+      # a symbolized profile contains a format we don't recognize, bail out
+      error("$fname: Cannot recognize profile section after symbols.\n");
+    }
+    # no ascii header present -- must be a CPU profile
+    $main::profile_type = 'cpu';
+    $result = ReadCPUProfile($prog, $fname, *PROFILE);
+  }
+
+  close(PROFILE);
+
+  # if we got symbols along with the profile, return those as well
+  if (defined($symbols)) {
+    $result->{symbols} = $symbols;
+  }
+
+  return $result;
+}
+
+# Subtract one from caller pc so we map back to call instr.
+# However, don't do this if we're reading a symbolized profile
+# file, in which case the subtract-one was done when the file
+# was written.
+#
+# We apply the same logic to all readers, though ReadCPUProfile uses an
+# independent implementation.
+sub FixCallerAddresses {
+  my $stack = shift;
+  if ($main::use_symbolized_profile) {
+    return $stack;
+  } else {
+    $stack =~ /(\s)/;
+    my $delimiter = $1;
+    my @addrs = split(' ', $stack);
+    my @fixedaddrs;
+    $#fixedaddrs = $#addrs;
+    if ($#addrs >= 0) {
+      $fixedaddrs[0] = $addrs[0];
+    }
+    for (my $i = 1; $i <= $#addrs; $i++) {
+      $fixedaddrs[$i] = AddressSub($addrs[$i], "0x1");
+    }
+    return join $delimiter, @fixedaddrs;
+  }
+}
+
+# CPU profile reader
+sub ReadCPUProfile {
+  my $prog = shift;
+  my $fname = shift;       # just used for logging
+  local *PROFILE = shift;
+  my $version;
+  my $period;
+  my $i;
+  my $profile = {};
+  my $pcs = {};
+
+  # Parse string into array of slots.
+  my $slots = CpuProfileStream->new(*PROFILE, $fname);
+
+  # Read header.  The current header version is a 5-element structure
+  # containing:
+  #   0: header count (always 0)
+  #   1: header "words" (after this one: 3)
+  #   2: format version (0)
+  #   3: sampling period (usec)
+  #   4: unused padding (always 0)
+  if ($slots->get(0) != 0 ) {
+    error("$fname: not a profile file, or old format profile file\n");
+  }
+  $i = 2 + $slots->get(1);
+  $version = $slots->get(2);
+  $period = $slots->get(3);
+  # Do some sanity checking on these header values.
+  if ($version > (2**32) || $period > (2**32) || $i > (2**32) || $i < 5) {
+    error("$fname: not a profile file, or corrupted profile file\n");
+  }
+
+  # Parse profile
+  while ($slots->get($i) != -1) {
+    my $n = $slots->get($i++);
+    my $d = $slots->get($i++);
+    if ($d > (2**16)) {  # TODO(csilvers): what's a reasonable max-stack-depth?
+      my $addr = sprintf("0%o", $i * ($address_length == 8 ? 4 : 8));
+      print STDERR "At index $i (address $addr):\n";
+      error("$fname: stack trace depth >= 2**32\n");
+    }
+    if ($slots->get($i) == 0) {
+      # End of profile data marker
+      $i += $d;
+      last;
+    }
+
+    # Make key out of the stack entries
+    my @k = ();
+    for (my $j = 0; $j < $d; $j++) {
+      my $pc = $slots->get($i+$j);
+      # Subtract one from caller pc so we map back to call instr.
+      # However, don't do this if we're reading a symbolized profile
+      # file, in which case the subtract-one was done when the file
+      # was written.
+      if ($j > 0 && !$main::use_symbolized_profile) {
+        $pc--;
+      }
+      $pc = sprintf("%0*x", $address_length, $pc);
+      $pcs->{$pc} = 1;
+      push @k, $pc;
+    }
+
+    AddEntry($profile, (join "\n", @k), $n);
+    $i += $d;
+  }
+
+  # Parse map
+  my $map = '';
+  seek(PROFILE, $i * 4, 0);
+  read(PROFILE, $map, (stat PROFILE)[7]);
+
+  my $r = {};
+  $r->{version} = $version;
+  $r->{period} = $period;
+  $r->{profile} = $profile;
+  $r->{libs} = ParseLibraries($prog, $map, $pcs);
+  $r->{pcs} = $pcs;
+
+  return $r;
+}
+
+sub HeapProfileIndex {
+  my $index = 1;
+  if ($main::opt_inuse_space) {
+    $index = 1;
+  } elsif ($main::opt_inuse_objects) {
+    $index = 0;
+  } elsif ($main::opt_alloc_space) {
+    $index = 3;
+  } elsif ($main::opt_alloc_objects) {
+    $index = 2;
+  }
+  return $index;
+}
+
+sub ReadMappedLibraries {
+  my $fh = shift;
+  my $map = "";
+  # Read the /proc/self/maps data
+  while (<$fh>) {
+    s/\r//g;         # turn windows-looking lines into unix-looking lines
+    $map .= $_;
+  }
+  return $map;
+}
+
+sub ReadMemoryMap {
+  my $fh = shift;
+  my $map = "";
+  # Read /proc/self/maps data as formatted by DumpAddressMap()
+  my $buildvar = "";
+  while (<PROFILE>) {
+    s/\r//g;         # turn windows-looking lines into unix-looking lines
+    # Parse "build=<dir>" specification if supplied
+    if (m/^\s*build=(.*)\n/) {
+      $buildvar = $1;
+    }
+
+    # Expand "$build" variable if available
+    $_ =~ s/\$build\b/$buildvar/g;
+
+    $map .= $_;
+  }
+  return $map;
+}
+
+sub AdjustSamples {
+  my ($sample_adjustment, $sampling_algorithm, $n1, $s1, $n2, $s2) = @_;
+  if ($sample_adjustment) {
+    if ($sampling_algorithm == 2) {
+      # Remote-heap version 2
+      # The sampling frequency is the rate of a Poisson process.
+      # This means that the probability of sampling an allocation of
+      # size X with sampling rate Y is 1 - exp(-X/Y)
+      if ($n1 != 0) {
+        my $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
+        my $scale_factor = 1/(1 - exp(-$ratio));
+        $n1 *= $scale_factor;
+        $s1 *= $scale_factor;
+      }
+      if ($n2 != 0) {
+        my $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
+        my $scale_factor = 1/(1 - exp(-$ratio));
+        $n2 *= $scale_factor;
+        $s2 *= $scale_factor;
+      }
+    } else {
+      # Remote-heap version 1
+      my $ratio;
+      $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
+      if ($ratio < 1) {
+        $n1 /= $ratio;
+        $s1 /= $ratio;
+      }
+      $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
+      if ($ratio < 1) {
+        $n2 /= $ratio;
+        $s2 /= $ratio;
+      }
+    }
+  }
+  return ($n1, $s1, $n2, $s2);
+}
+
+sub ReadHeapProfile {
+  my $prog = shift;
+  local *PROFILE = shift;
+  my $header = shift;
+
+  my $index = HeapProfileIndex();
+
+  # Find the type of this profile.  The header line looks like:
+  #    heap profile:   1246:  8800744 [  1246:  8800744] @ <heap-url>/266053
+  # There are two pairs <count: size>, the first inuse objects/space, and the
+  # second allocated objects/space.  This is followed optionally by a profile
+  # type, and if that is present, optionally by a sampling frequency.
+  # For remote heap profiles (v1):
+  # The interpretation of the sampling frequency is that the profiler, for
+  # each sample, calculates a uniformly distributed random integer less than
+  # the given value, and records the next sample after that many bytes have
+  # been allocated.  Therefore, the expected sample interval is half of the
+  # given frequency.  By default, if not specified, the expected sample
+  # interval is 128KB.  Only remote-heap-page profiles are adjusted for
+  # sample size.
+  # For remote heap profiles (v2):
+  # The sampling frequency is the rate of a Poisson process. This means that
+  # the probability of sampling an allocation of size X with sampling rate Y
+  # is 1 - exp(-X/Y)
+  # For version 2, a typical header line might look like this:
+  # heap profile:   1922: 127792360 [  1922: 127792360] @ <heap-url>_v2/524288
+  # the trailing number (524288) is the sampling rate. (Version 1 showed
+  # double the 'rate' here)
+  my $sampling_algorithm = 0;
+  my $sample_adjustment = 0;
+  chomp($header);
+  my $type = "unknown";
+  if ($header =~ m"^heap profile:\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\](\s*@\s*([^/]*)(/(\d+))?)?") {
+    if (defined($6) && ($6 ne '')) {
+      $type = $6;
+      my $sample_period = $8;
+      # $type is "heapprofile" for profiles generated by the
+      # heap-profiler, and either "heap" or "heap_v2" for profiles
+      # generated by sampling directly within tcmalloc.  It can also
+      # be "growth" for heap-growth profiles.  The first is typically
+      # found for profiles generated locally, and the others for
+      # remote profiles.
+      if (($type eq "heapprofile") || ($type !~ /heap/) ) {
+        # No need to adjust for the sampling rate with heap-profiler-derived data
+        $sampling_algorithm = 0;
+      } elsif ($type =~ /_v2/) {
+        $sampling_algorithm = 2;     # version 2 sampling
+        if (defined($sample_period) && ($sample_period ne '')) {
+          $sample_adjustment = int($sample_period);
+        }
+      } else {
+        $sampling_algorithm = 1;     # version 1 sampling
+        if (defined($sample_period) && ($sample_period ne '')) {
+          $sample_adjustment = int($sample_period)/2;
+        }
+      }
+    } else {
+      # We detect whether or not this is a remote-heap profile by checking
+      # that the total-allocated stats ($n2,$s2) are exactly the
+      # same as the in-use stats ($n1,$s1).  It is remotely conceivable
+      # that a non-remote-heap profile may pass this check, but it is hard
+      # to imagine how that could happen.
+      # In this case it's so old it's guaranteed to be remote-heap version 1.
+      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
+      if (($n1 == $n2) && ($s1 == $s2)) {
+        # This is likely to be a remote-heap based sample profile
+        $sampling_algorithm = 1;
+      }
+    }
+  }
+
+  if ($sampling_algorithm > 0) {
+    # For remote-heap generated profiles, adjust the counts and sizes to
+    # account for the sample rate (we sample once every 128KB by default).
+    if ($sample_adjustment == 0) {
+      # Turn on profile adjustment.
+      $sample_adjustment = 128*1024;
+      print STDERR "Adjusting heap profiles for 1-in-128KB sampling rate\n";
+    } else {
+      printf STDERR ("Adjusting heap profiles for 1-in-%d sampling rate\n",
+                     $sample_adjustment);
+    }
+    if ($sampling_algorithm > 1) {
+      # We don't bother printing anything for the original version (version 1)
+      printf STDERR "Heap version $sampling_algorithm\n";
+    }
+  }
+
+  my $profile = {};
+  my $pcs = {};
+  my $map = "";
+
+  while (<PROFILE>) {
+    s/\r//g;         # turn windows-looking lines into unix-looking lines
+    if (/^MAPPED_LIBRARIES:/) {
+      $map .= ReadMappedLibraries(*PROFILE);
+      last;
+    }
+
+    if (/^--- Memory map:/) {
+      $map .= ReadMemoryMap(*PROFILE);
+      last;
+    }
+
+    # Read entry of the form:
+    #  <count1>: <bytes1> [<count2>: <bytes2>] @ a1 a2 a3 ... an
+    s/^\s*//;
+    s/\s*$//;
+    if (m/^\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]\s+@\s+(.*)$/) {
+      my $stack = $5;
+      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
+      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,
+                                 $n1, $s1, $n2, $s2);
+      AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
+    }
+  }
+
+  my $r = {};
+  $r->{version} = "heap";
+  $r->{period} = 1;
+  $r->{profile} = $profile;
+  $r->{libs} = ParseLibraries($prog, $map, $pcs);
+  $r->{pcs} = $pcs;
+  return $r;
+}
+
+sub ReadThreadedHeapProfile {
+  my ($prog, $fname, $header) = @_;
+
+  my $index = HeapProfileIndex();
+  my $sampling_algorithm = 0;
+  my $sample_adjustment = 0;
+  chomp($header);
+  my $type = "unknown";
+  # Assuming a very specific type of header for now.
+  if ($header =~ m"^heap_v2/(\d+)") {
+    $type = "_v2";
+    $sampling_algorithm = 2;
+    $sample_adjustment = int($1);
+  }
+  if ($type ne "_v2" || !defined($sample_adjustment)) {
+    die "Threaded heap profiles require v2 sampling with a sample rate\n";
+  }
+
+  my $profile = {};
+  my $thread_profiles = {};
+  my $pcs = {};
+  my $map = "";
+  my $stack = "";
+
+  while (<PROFILE>) {
+    s/\r//g;
+    if (/^MAPPED_LIBRARIES:/) {
+      $map .= ReadMappedLibraries(*PROFILE);
+      last;
+    }
+
+    if (/^--- Memory map:/) {
+      $map .= ReadMemoryMap(*PROFILE);
+      last;
+    }
+
+    # Read entry of the form:
+    # @ a1 a2 ... an
+    #   t*: <count1>: <bytes1> [<count2>: <bytes2>]
+    #   t1: <count1>: <bytes1> [<count2>: <bytes2>]
+    #     ...
+    #   tn: <count1>: <bytes1> [<count2>: <bytes2>]
+    s/^\s*//;
+    s/\s*$//;
+    if (m/^@\s+(.*)$/) {
+      $stack = $1;
+    } elsif (m/^\s*(t(\*|\d+)):\s+(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]$/) {
+      if ($stack eq "") {
+        # Still in the header, so this is just a per-thread summary.
+        next;
+      }
+      my $thread = $2;
+      my ($n1, $s1, $n2, $s2) = ($3, $4, $5, $6);
+      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,
+                                 $n1, $s1, $n2, $s2);
+      if ($thread eq "*") {
+        AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
+      } else {
+        if (!exists($thread_profiles->{$thread})) {
+          $thread_profiles->{$thread} = {};
+        }
+        AddEntries($thread_profiles->{$thread}, $pcs,
+                   FixCallerAddresses($stack), $counts[$index]);
+      }
+    }
+  }
+
+  my $r = {};
+  $r->{version} = "heap";
+  $r->{period} = 1;
+  $r->{profile} = $profile;
+  $r->{threads} = $thread_profiles;
+  $r->{libs} = ParseLibraries($prog, $map, $pcs);
+  $r->{pcs} = $pcs;
+  return $r;
+}
+
+sub ReadSynchProfile {
+  my $prog = shift;
+  local *PROFILE = shift;
+  my $header = shift;
+
+  my $map = '';
+  my $profile = {};
+  my $pcs = {};
+  my $sampling_period = 1;
+  my $cyclespernanosec = 2.8;   # Default assumption for old binaries
+  my $seen_clockrate = 0;
+  my $line;
+
+  my $index = 0;
+  if ($main::opt_total_delay) {
+    $index = 0;
+  } elsif ($main::opt_contentions) {
+    $index = 1;
+  } elsif ($main::opt_mean_delay) {
+    $index = 2;
+  }
+
+  while ( $line = <PROFILE> ) {
+    $line =~ s/\r//g;      # turn windows-looking lines into unix-looking lines
+    if ( $line =~ /^\s*(\d+)\s+(\d+) \@\s*(.*?)\s*$/ ) {
+      my ($cycles, $count, $stack) = ($1, $2, $3);
+
+      # Convert cycles to nanoseconds
+      $cycles /= $cyclespernanosec;
+
+      # Adjust for sampling done by application
+      $cycles *= $sampling_period;
+      $count *= $sampling_period;
+
+      my @values = ($cycles, $count, $cycles / $count);
+      AddEntries($profile, $pcs, FixCallerAddresses($stack), $values[$index]);
+
+    } elsif ( $line =~ /^(slow release).*thread \d+  \@\s*(.*?)\s*$/ ||
+              $line =~ /^\s*(\d+) \@\s*(.*?)\s*$/ ) {
+      my ($cycles, $stack) = ($1, $2);
+      if ($cycles !~ /^\d+$/) {
+        next;
+      }
+
+      # Convert cycles to nanoseconds
+      $cycles /= $cyclespernanosec;
+
+      # Adjust for sampling done by application
+      $cycles *= $sampling_period;
+
+      AddEntries($profile, $pcs, FixCallerAddresses($stack), $cycles);
+
+    } elsif ( $line =~ m/^([a-z][^=]*)=(.*)$/ ) {
+      my ($variable, $value) = ($1,$2);
+      for ($variable, $value) {
+        s/^\s+//;
+        s/\s+$//;
+      }
+      if ($variable eq "cycles/second") {
+        $cyclespernanosec = $value / 1e9;
+        $seen_clockrate = 1;
+      } elsif ($variable eq "sampling period") {
+        $sampling_period = $value;
+      } elsif ($variable eq "ms since reset") {
+        # Currently nothing is done with this value in jeprof
+        # So we just silently ignore it for now
+      } elsif ($variable eq "discarded samples") {
+        # Currently nothing is done with this value in jeprof
+        # So we just silently ignore it for now
+      } else {
+        printf STDERR ("Ignoring unnknown variable in /contention output: " .
+                       "'%s' = '%s'\n",$variable,$value);
+      }
+    } else {
+      # Memory map entry
+      $map .= $line;
+    }
+  }
+
+  if (!$seen_clockrate) {
+    printf STDERR ("No cycles/second entry in profile; Guessing %.1f GHz\n",
+                   $cyclespernanosec);
+  }
+
+  my $r = {};
+  $r->{version} = 0;
+  $r->{period} = $sampling_period;
+  $r->{profile} = $profile;
+  $r->{libs} = ParseLibraries($prog, $map, $pcs);
+  $r->{pcs} = $pcs;
+  return $r;
+}
+
+# Given a hex value in the form "0x1abcd" or "1abcd", return either
+# "0001abcd" or "000000000001abcd", depending on the current (global)
+# address length.
+sub HexExtend {
+  my $addr = shift;
+
+  $addr =~ s/^(0x)?0*//;
+  my $zeros_needed = $address_length - length($addr);
+  if ($zeros_needed < 0) {
+    printf STDERR "Warning: address $addr is longer than address length $address_length\n";
+    return $addr;
+  }
+  return ("0" x $zeros_needed) . $addr;
+}
+
+##### Symbol extraction #####
+
+# Aggressively search the lib_prefix values for the given library
+# If all else fails, just return the name of the library unmodified.
+# If the lib_prefix is "/my/path,/other/path" and $file is "/lib/dir/mylib.so"
+# it will search the following locations in this order, until it finds a file:
+#   /my/path/lib/dir/mylib.so
+#   /other/path/lib/dir/mylib.so
+#   /my/path/dir/mylib.so
+#   /other/path/dir/mylib.so
+#   /my/path/mylib.so
+#   /other/path/mylib.so
+#   /lib/dir/mylib.so              (returned as last resort)
+sub FindLibrary {
+  my $file = shift;
+  my $suffix = $file;
+
+  # Search for the library as described above
+  do {
+    foreach my $prefix (@prefix_list) {
+      my $fullpath = $prefix . $suffix;
+      if (-e $fullpath) {
+        return $fullpath;
+      }
+    }
+  } while ($suffix =~ s|^/[^/]+/|/|);
+  return $file;
+}
+
+# Return path to library with debugging symbols.
+# For libc libraries, the copy in /usr/lib/debug contains debugging symbols
+sub DebuggingLibrary {
+  my $file = shift;
+  if ($file =~ m|^/|) {
+      if (-f "/usr/lib/debug$file") {
+        return "/usr/lib/debug$file";
+      } elsif (-f "/usr/lib/debug$file.debug") {
+        return "/usr/lib/debug$file.debug";
+      }
+  }
+  return undef;
+}
+
+# Parse text section header of a library using objdump
+sub ParseTextSectionHeaderFromObjdump {
+  my $lib = shift;
+
+  my $size = undef;
+  my $vma;
+  my $file_offset;
+  # Get objdump output from the library file to figure out how to
+  # map between mapped addresses and addresses in the library.
+  my $cmd = ShellEscape($obj_tool_map{"objdump"}, "-h", $lib);
+  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
+  while (<OBJDUMP>) {
+    s/\r//g;         # turn windows-looking lines into unix-looking lines
+    # Idx Name          Size      VMA       LMA       File off  Algn
+    #  10 .text         00104b2c  420156f0  420156f0  000156f0  2**4
+    # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file
+    # offset may still be 8.  But AddressSub below will still handle that.
+    my @x = split;
+    if (($#x >= 6) && ($x[1] eq '.text')) {
+      $size = $x[2];
+      $vma = $x[3];
+      $file_offset = $x[5];
+      last;
+    }
+  }
+  close(OBJDUMP);
+
+  if (!defined($size)) {
+    return undef;
+  }
+
+  my $r = {};
+  $r->{size} = $size;
+  $r->{vma} = $vma;
+  $r->{file_offset} = $file_offset;
+
+  return $r;
+}
+
+# Parse text section header of a library using otool (on OS X)
+sub ParseTextSectionHeaderFromOtool {
+  my $lib = shift;
+
+  my $size = undef;
+  my $vma = undef;
+  my $file_offset = undef;
+  # Get otool output from the library file to figure out how to
+  # map between mapped addresses and addresses in the library.
+  my $command = ShellEscape($obj_tool_map{"otool"}, "-l", $lib);
+  open(OTOOL, "$command |") || error("$command: $!\n");
+  my $cmd = "";
+  my $sectname = "";
+  my $segname = "";
+  foreach my $line (<OTOOL>) {
+    $line =~ s/\r//g;      # turn windows-looking lines into unix-looking lines
+    # Load command <#>
+    #       cmd LC_SEGMENT
+    # [...]
+    # Section
+    #   sectname __text
+    #    segname __TEXT
+    #       addr 0x000009f8
+    #       size 0x00018b9e
+    #     offset 2552
+    #      align 2^2 (4)
+    # We will need to strip off the leading 0x from the hex addresses,
+    # and convert the offset into hex.
+    if ($line =~ /Load command/) {
+      $cmd = "";
+      $sectname = "";
+      $segname = "";
+    } elsif ($line =~ /Section/) {
+      $sectname = "";
+      $segname = "";
+    } elsif ($line =~ /cmd (\w+)/) {
+      $cmd = $1;
+    } elsif ($line =~ /sectname (\w+)/) {
+      $sectname = $1;
+    } elsif ($line =~ /segname (\w+)/) {
+      $segname = $1;
+    } elsif (!(($cmd eq "LC_SEGMENT" || $cmd eq "LC_SEGMENT_64") &&
+               $sectname eq "__text" &&
+               $segname eq "__TEXT")) {
+      next;
+    } elsif ($line =~ /\baddr 0x([0-9a-fA-F]+)/) {
+      $vma = $1;
+    } elsif ($line =~ /\bsize 0x([0-9a-fA-F]+)/) {
+      $size = $1;
+    } elsif ($line =~ /\boffset ([0-9]+)/) {
+      $file_offset = sprintf("%016x", $1);
+    }
+    if (defined($vma) && defined($size) && defined($file_offset)) {
+      last;
+    }
+  }
+  close(OTOOL);
+
+  if (!defined($vma) || !defined($size) || !defined($file_offset)) {
+     return undef;
+  }
+
+  my $r = {};
+  $r->{size} = $size;
+  $r->{vma} = $vma;
+  $r->{file_offset} = $file_offset;
+
+  return $r;
+}
+
+sub ParseTextSectionHeader {
+  # obj_tool_map("otool") is only defined if we're in a Mach-O environment
+  if (defined($obj_tool_map{"otool"})) {
+    my $r = ParseTextSectionHeaderFromOtool(@_);
+    if (defined($r)){
+      return $r;
+    }
+  }
+  # If otool doesn't work, or we don't have it, fall back to objdump
+  return ParseTextSectionHeaderFromObjdump(@_);
+}
+
+# Split /proc/pid/maps dump into a list of libraries
+sub ParseLibraries {
+  return if $main::use_symbol_page;  # We don't need libraries info.
+  my $prog = shift;
+  my $map = shift;
+  my $pcs = shift;
+
+  my $result = [];
+  my $h = "[a-f0-9]+";
+  my $zero_offset = HexExtend("0");
+
+  my $buildvar = "";
+  foreach my $l (split("\n", $map)) {
+    if ($l =~ m/^\s*build=(.*)$/) {
+      $buildvar = $1;
+    }
+
+    my $start;
+    my $finish;
+    my $offset;
+    my $lib;
+    if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?)$/i) {
+      # Full line from /proc/self/maps.  Example:
+      #   40000000-40015000 r-xp 00000000 03:01 12845071   /lib/ld-2.3.2.so
+      $start = HexExtend($1);
+      $finish = HexExtend($2);
+      $offset = HexExtend($3);
+      $lib = $4;
+      $lib =~ s|\\|/|g;     # turn windows-style paths into unix-style paths
+    } elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) {
+      # Cooked line from DumpAddressMap.  Example:
+      #   40000000-40015000: /lib/ld-2.3.2.so
+      $start = HexExtend($1);
+      $finish = HexExtend($2);
+      $offset = $zero_offset;
+      $lib = $3;
+    }
+    # FreeBSD 10.0 virtual memory map /proc/curproc/map as defined in
+    # function procfs_doprocmap (sys/fs/procfs/procfs_map.c)
+    #
+    # Example:
+    # 0x800600000 0x80061a000 26 0 0xfffff800035a0000 r-x 75 33 0x1004 COW NC vnode /libexec/ld-elf.s
+    # o.1 NCH -1
+    elsif ($l =~ /^(0x$h)\s(0x$h)\s\d+\s\d+\s0x$h\sr-x\s\d+\s\d+\s0x\d+\s(COW|NCO)\s(NC|NNC)\svnode\s(\S+\.so(\.\d+)*)/) {
+      $start = HexExtend($1);
+      $finish = HexExtend($2);
+      $offset = $zero_offset;
+      $lib = FindLibrary($5);
+
+    } else {
+      next;
+    }
+
+    # Expand "$build" variable if available
+    $lib =~ s/\$build\b/$buildvar/g;
+
+    $lib = FindLibrary($lib);
+
+    # Check for pre-relocated libraries, which use pre-relocated symbol tables
+    # and thus require adjusting the offset that we'll use to translate
+    # VM addresses into symbol table addresses.
+    # Only do this if we're not going to fetch the symbol table from a
+    # debugging copy of the library.
+    if (!DebuggingLibrary($lib)) {
+      my $text = ParseTextSectionHeader($lib);
+      if (defined($text)) {
+         my $vma_offset = AddressSub($text->{vma}, $text->{file_offset});
+         $offset = AddressAdd($offset, $vma_offset);
+      }
+    }
+
+    if($main::opt_debug) { printf STDERR "$start:$finish ($offset) $lib\n"; }
+    push(@{$result}, [$lib, $start, $finish, $offset]);
+  }
+
+  # Append special entry for additional library (not relocated)
+  if ($main::opt_lib ne "") {
+    my $text = ParseTextSectionHeader($main::opt_lib);
+    if (defined($text)) {
+       my $start = $text->{vma};
+       my $finish = AddressAdd($start, $text->{size});
+
+       push(@{$result}, [$main::opt_lib, $start, $finish, $start]);
+    }
+  }
+
+  # Append special entry for the main program.  This covers
+  # 0..max_pc_value_seen, so that we assume pc values not found in one
+  # of the library ranges will be treated as coming from the main
+  # program binary.
+  my $min_pc = HexExtend("0");
+  my $max_pc = $min_pc;          # find the maximal PC value in any sample
+  foreach my $pc (keys(%{$pcs})) {
+    if (HexExtend($pc) gt $max_pc) { $max_pc = HexExtend($pc); }
+  }
+  push(@{$result}, [$prog, $min_pc, $max_pc, $zero_offset]);
+
+  return $result;
+}
+
+# Add two hex addresses of length $address_length.
+# Run jeprof --test for unit test if this is changed.
+sub AddressAdd {
+  my $addr1 = shift;
+  my $addr2 = shift;
+  my $sum;
+
+  if ($address_length == 8) {
+    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
+    $sum = (hex($addr1)+hex($addr2)) % (0x10000000 * 16);
+    return sprintf("%08x", $sum);
+
+  } else {
+    # Do the addition in 7-nibble chunks to trivialize carry handling.
+
+    if ($main::opt_debug and $main::opt_test) {
+      print STDERR "AddressAdd $addr1 + $addr2 = ";
+    }
+
+    my $a1 = substr($addr1,-7);
+    $addr1 = substr($addr1,0,-7);
+    my $a2 = substr($addr2,-7);
+    $addr2 = substr($addr2,0,-7);
+    $sum = hex($a1) + hex($a2);
+    my $c = 0;
+    if ($sum > 0xfffffff) {
+      $c = 1;
+      $sum -= 0x10000000;
+    }
+    my $r = sprintf("%07x", $sum);
+
+    $a1 = substr($addr1,-7);
+    $addr1 = substr($addr1,0,-7);
+    $a2 = substr($addr2,-7);
+    $addr2 = substr($addr2,0,-7);
+    $sum = hex($a1) + hex($a2) + $c;
+    $c = 0;
+    if ($sum > 0xfffffff) {
+      $c = 1;
+      $sum -= 0x10000000;
+    }
+    $r = sprintf("%07x", $sum) . $r;
+
+    $sum = hex($addr1) + hex($addr2) + $c;
+    if ($sum > 0xff) { $sum -= 0x100; }
+    $r = sprintf("%02x", $sum) . $r;
+
+    if ($main::opt_debug and $main::opt_test) { print STDERR "$r\n"; }
+
+    return $r;
+  }
+}
+
+
+# Subtract two hex addresses of length $address_length.
+# Run jeprof --test for unit test if this is changed.
+sub AddressSub {
+  my $addr1 = shift;
+  my $addr2 = shift;
+  my $diff;
+
+  if ($address_length == 8) {
+    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
+    $diff = (hex($addr1)-hex($addr2)) % (0x10000000 * 16);
+    return sprintf("%08x", $diff);
+
+  } else {
+    # Do the addition in 7-nibble chunks to trivialize borrow handling.
+    # if ($main::opt_debug) { print STDERR "AddressSub $addr1 - $addr2 = "; }
+
+    my $a1 = hex(substr($addr1,-7));
+    $addr1 = substr($addr1,0,-7);
+    my $a2 = hex(substr($addr2,-7));
+    $addr2 = substr($addr2,0,-7);
+    my $b = 0;
+    if ($a2 > $a1) {
+      $b = 1;
+      $a1 += 0x10000000;
+    }
+    $diff = $a1 - $a2;
+    my $r = sprintf("%07x", $diff);
+
+    $a1 = hex(substr($addr1,-7));
+    $addr1 = substr($addr1,0,-7);
+    $a2 = hex(substr($addr2,-7)) + $b;
+    $addr2 = substr($addr2,0,-7);
+    $b = 0;
+    if ($a2 > $a1) {
+      $b = 1;
+      $a1 += 0x10000000;
+    }
+    $diff = $a1 - $a2;
+    $r = sprintf("%07x", $diff) . $r;
+
+    $a1 = hex($addr1);
+    $a2 = hex($addr2) + $b;
+    if ($a2 > $a1) { $a1 += 0x100; }
+    $diff = $a1 - $a2;
+    $r = sprintf("%02x", $diff) . $r;
+
+    # if ($main::opt_debug) { print STDERR "$r\n"; }
+
+    return $r;
+  }
+}
+
+# Increment a hex addresses of length $address_length.
+# Run jeprof --test for unit test if this is changed.
+sub AddressInc {
+  my $addr = shift;
+  my $sum;
+
+  if ($address_length == 8) {
+    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
+    $sum = (hex($addr)+1) % (0x10000000 * 16);
+    return sprintf("%08x", $sum);
+
+  } else {
+    # Do the addition in 7-nibble chunks to trivialize carry handling.
+    # We are always doing this to step through the addresses in a function,
+    # and will almost never overflow the first chunk, so we check for this
+    # case and exit early.
+
+    # if ($main::opt_debug) { print STDERR "AddressInc $addr1 = "; }
+
+    my $a1 = substr($addr,-7);
+    $addr = substr($addr,0,-7);
+    $sum = hex($a1) + 1;
+    my $r = sprintf("%07x", $sum);
+    if ($sum <= 0xfffffff) {
+      $r = $addr . $r;
+      # if ($main::opt_debug) { print STDERR "$r\n"; }
+      return HexExtend($r);
+    } else {
+      $r = "0000000";
+    }
+
+    $a1 = substr($addr,-7);
+    $addr = substr($addr,0,-7);
+    $sum = hex($a1) + 1;
+    $r = sprintf("%07x", $sum) . $r;
+    if ($sum <= 0xfffffff) {
+      $r = $addr . $r;
+      # if ($main::opt_debug) { print STDERR "$r\n"; }
+      return HexExtend($r);
+    } else {
+      $r = "00000000000000";
+    }
+
+    $sum = hex($addr) + 1;
+    if ($sum > 0xff) { $sum -= 0x100; }
+    $r = sprintf("%02x", $sum) . $r;
+
+    # if ($main::opt_debug) { print STDERR "$r\n"; }
+    return $r;
+  }
+}
+
+# Extract symbols for all PC values found in profile
+sub ExtractSymbols {
+  my $libs = shift;
+  my $pcset = shift;
+
+  my $symbols = {};
+
+  # Map each PC value to the containing library.  To make this faster,
+  # we sort libraries by their starting pc value (highest first), and
+  # advance through the libraries as we advance the pc.  Sometimes the
+  # addresses of libraries may overlap with the addresses of the main
+  # binary, so to make sure the libraries 'win', we iterate over the
+  # libraries in reverse order (which assumes the binary doesn't start
+  # in the middle of a library, which seems a fair assumption).
+  my @pcs = (sort { $a cmp $b } keys(%{$pcset}));  # pcset is 0-extended strings
+  foreach my $lib (sort {$b->[1] cmp $a->[1]} @{$libs}) {
+    my $libname = $lib->[0];
+    my $start = $lib->[1];
+    my $finish = $lib->[2];
+    my $offset = $lib->[3];
+
+    # Use debug library if it exists
+    my $debug_libname = DebuggingLibrary($libname);
+    if ($debug_libname) {
+        $libname = $debug_libname;
+    }
+
+    # Get list of pcs that belong in this library.
+    my $contained = [];
+    my ($start_pc_index, $finish_pc_index);
+    # Find smallest finish_pc_index such that $finish < $pc[$finish_pc_index].
+    for ($finish_pc_index = $#pcs + 1; $finish_pc_index > 0;
+         $finish_pc_index--) {
+      last if $pcs[$finish_pc_index - 1] le $finish;
+    }
+    # Find smallest start_pc_index such that $start <= $pc[$start_pc_index].
+    for ($start_pc_index = $finish_pc_index; $start_pc_index > 0;
+         $start_pc_index--) {
+      last if $pcs[$start_pc_index - 1] lt $start;
+    }
+    # This keeps PC values higher than $pc[$finish_pc_index] in @pcs,
+    # in case there are overlaps in libraries and the main binary.
+    @{$contained} = splice(@pcs, $start_pc_index,
+                           $finish_pc_index - $start_pc_index);
+    # Map to symbols
+    MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols);
+  }
+
+  return $symbols;
+}
+
+# Map list of PC values to symbols for a given image
+sub MapToSymbols {
+  my $image = shift;
+  my $offset = shift;
+  my $pclist = shift;
+  my $symbols = shift;
+
+  my $debug = 0;
+
+  # Ignore empty binaries
+  if ($#{$pclist} < 0) { return; }
+
+  # Figure out the addr2line command to use
+  my $addr2line = $obj_tool_map{"addr2line"};
+  my $cmd = ShellEscape($addr2line, "-f", "-C", "-e", $image);
+  if (exists $obj_tool_map{"addr2line_pdb"}) {
+    $addr2line = $obj_tool_map{"addr2line_pdb"};
+    $cmd = ShellEscape($addr2line, "--demangle", "-f", "-C", "-e", $image);
+  }
+
+  # If "addr2line" isn't installed on the system at all, just use
+  # nm to get what info we can (function names, but not line numbers).
+  if (system(ShellEscape($addr2line, "--help") . " >$dev_null 2>&1") != 0) {
+    MapSymbolsWithNM($image, $offset, $pclist, $symbols);
+    return;
+  }
+
+  # "addr2line -i" can produce a variable number of lines per input
+  # address, with no separator that allows us to tell when data for
+  # the next address starts.  So we find the address for a special
+  # symbol (_fini) and interleave this address between all real
+  # addresses passed to addr2line.  The name of this special symbol
+  # can then be used as a separator.
+  $sep_address = undef;  # May be filled in by MapSymbolsWithNM()
+  my $nm_symbols = {};
+  MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols);
+  if (defined($sep_address)) {
+    # Only add " -i" to addr2line if the binary supports it.
+    # addr2line --help returns 0, but not if it sees an unknown flag first.
+    if (system("$cmd -i --help >$dev_null 2>&1") == 0) {
+      $cmd .= " -i";
+    } else {
+      $sep_address = undef;   # no need for sep_address if we don't support -i
+    }
+  }
+
+  # Make file with all PC values with intervening 'sep_address' so
+  # that we can reliably detect the end of inlined function list
+  open(ADDRESSES, ">$main::tmpfile_sym") || error("$main::tmpfile_sym: $!\n");
+  if ($debug) { print("---- $image ---\n"); }
+  for (my $i = 0; $i <= $#{$pclist}; $i++) {
+    # addr2line always reads hex addresses, and does not need '0x' prefix.
+    if ($debug) { printf STDERR ("%s\n", $pclist->[$i]); }
+    printf ADDRESSES ("%s\n", AddressSub($pclist->[$i], $offset));
+    if (defined($sep_address)) {
+      printf ADDRESSES ("%s\n", $sep_address);
+    }
+  }
+  close(ADDRESSES);
+  if ($debug) {
+    print("----\n");
+    system("cat", $main::tmpfile_sym);
+    print("----\n");
+    system("$cmd < " . ShellEscape($main::tmpfile_sym));
+    print("----\n");
+  }
+
+  open(SYMBOLS, "$cmd <" . ShellEscape($main::tmpfile_sym) . " |")
+      || error("$cmd: $!\n");
+  my $count = 0;   # Index in pclist
+  while (<SYMBOLS>) {
+    # Read fullfunction and filelineinfo from next pair of lines
+    s/\r?\n$//g;
+    my $fullfunction = $_;
+    $_ = <SYMBOLS>;
+    s/\r?\n$//g;
+    my $filelinenum = $_;
+
+    if (defined($sep_address) && $fullfunction eq $sep_symbol) {
+      # Terminating marker for data for this address
+      $count++;
+      next;
+    }
+
+    $filelinenum =~ s|\\|/|g; # turn windows-style paths into unix-style paths
+
+    my $pcstr = $pclist->[$count];
+    my $function = ShortFunctionName($fullfunction);
+    my $nms = $nm_symbols->{$pcstr};
+    if (defined($nms)) {
+      if ($fullfunction eq '??') {
+        # nm found a symbol for us.
+        $function = $nms->[0];
+        $fullfunction = $nms->[2];
+      } else {
+       # MapSymbolsWithNM tags each routine with its starting address,
+       # useful in case the image has multiple occurrences of this
+       # routine.  (It uses a syntax that resembles template paramters,
+       # that are automatically stripped out by ShortFunctionName().)
+       # addr2line does not provide the same information.  So we check
+       # if nm disambiguated our symbol, and if so take the annotated
+       # (nm) version of the routine-name.  TODO(csilvers): this won't
+       # catch overloaded, inlined symbols, which nm doesn't see.
+       # Better would be to do a check similar to nm's, in this fn.
+       if ($nms->[2] =~ m/^\Q$function\E/) {  # sanity check it's the right fn
+         $function = $nms->[0];
+         $fullfunction = $nms->[2];
+       }
+      }
+    }
+
+    # Prepend to accumulated symbols for pcstr
+    # (so that caller comes before callee)
+    my $sym = $symbols->{$pcstr};
+    if (!defined($sym)) {
+      $sym = [];
+      $symbols->{$pcstr} = $sym;
+    }
+    unshift(@{$sym}, $function, $filelinenum, $fullfunction);
+    if ($debug) { printf STDERR ("%s => [%s]\n", $pcstr, join(" ", @{$sym})); }
+    if (!defined($sep_address)) {
+      # Inlining is off, so this entry ends immediately
+      $count++;
+    }
+  }
+  close(SYMBOLS);
+}
+
+# Use nm to map the list of referenced PCs to symbols.  Return true iff we
+# are able to read procedure information via nm.
+sub MapSymbolsWithNM {
+  my $image = shift;
+  my $offset = shift;
+  my $pclist = shift;
+  my $symbols = shift;
+
+  # Get nm output sorted by increasing address
+  my $symbol_table = GetProcedureBoundaries($image, ".");
+  if (!%{$symbol_table}) {
+    return 0;
+  }
+  # Start addresses are already the right length (8 or 16 hex digits).
+  my @names = sort { $symbol_table->{$a}->[0] cmp $symbol_table->{$b}->[0] }
+    keys(%{$symbol_table});
+
+  if ($#names < 0) {
+    # No symbols: just use addresses
+    foreach my $pc (@{$pclist}) {
+      my $pcstr = "0x" . $pc;
+      $symbols->{$pc} = [$pcstr, "?", $pcstr];
+    }
+    return 0;
+  }
+
+  # Sort addresses so we can do a join against nm output
+  my $index = 0;
+  my $fullname = $names[0];
+  my $name = ShortFunctionName($fullname);
+  foreach my $pc (sort { $a cmp $b } @{$pclist}) {
+    # Adjust for mapped offset
+    my $mpc = AddressSub($pc, $offset);
+    while (($index < $#names) && ($mpc ge $symbol_table->{$fullname}->[1])){
+      $index++;
+      $fullname = $names[$index];
+      $name = ShortFunctionName($fullname);
+    }
+    if ($mpc lt $symbol_table->{$fullname}->[1]) {
+      $symbols->{$pc} = [$name, "?", $fullname];
+    } else {
+      my $pcstr = "0x" . $pc;
+      $symbols->{$pc} = [$pcstr, "?", $pcstr];
+    }
+  }
+  return 1;
+}
+
+sub ShortFunctionName {
+  my $function = shift;
+  while ($function =~ s/\([^()]*\)(\s*const)?//g) { }   # Argument types
+  while ($function =~ s/<[^<>]*>//g)  { }    # Remove template arguments
+  $function =~ s/^.*\s+(\w+::)/$1/;          # Remove leading type
+  return $function;
+}
+
+# Trim overly long symbols found in disassembler output
+sub CleanDisassembly {
+  my $d = shift;
+  while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
+  while ($d =~ s/(\w+)<[^<>]*>/$1/g)  { }       # Remove template arguments
+  return $d;
+}
+
+# Clean file name for display
+sub CleanFileName {
+  my ($f) = @_;
+  $f =~ s|^/proc/self/cwd/||;
+  $f =~ s|^\./||;
+  return $f;
+}
+
+# Make address relative to section and clean up for display
+sub UnparseAddress {
+  my ($offset, $address) = @_;
+  $address = AddressSub($address, $offset);
+  $address =~ s/^0x//;
+  $address =~ s/^0*//;
+  return $address;
+}
+
+##### Miscellaneous #####
+
+# Find the right versions of the above object tools to use.  The
+# argument is the program file being analyzed, and should be an ELF
+# 32-bit or ELF 64-bit executable file.  The location of the tools
+# is determined by considering the following options in this order:
+#   1) --tools option, if set
+#   2) JEPROF_TOOLS environment variable, if set
+#   3) the environment
+sub ConfigureObjTools {
+  my $prog_file = shift;
+
+  # Check for the existence of $prog_file because /usr/bin/file does not
+  # predictably return error status in prod.
+  (-e $prog_file)  || error("$prog_file does not exist.\n");
+
+  my $file_type = undef;
+  if (-e "/usr/bin/file") {
+    # Follow symlinks (at least for systems where "file" supports that).
+    my $escaped_prog_file = ShellEscape($prog_file);
+    $file_type = `/usr/bin/file -L $escaped_prog_file 2>$dev_null ||
+                  /usr/bin/file $escaped_prog_file`;
+  } elsif ($^O == "MSWin32") {
+    $file_type = "MS Windows";
+  } else {
+    print STDERR "WARNING: Can't determine the file type of $prog_file";
+  }
+
+  if ($file_type =~ /64-bit/) {
+    # Change $address_length to 16 if the program file is ELF 64-bit.
+    # We can't detect this from many (most?) heap or lock contention
+    # profiles, since the actual addresses referenced are generally in low
+    # memory even for 64-bit programs.
+    $address_length = 16;
+  }
+
+  if ($file_type =~ /MS Windows/) {
+    # For windows, we provide a version of nm and addr2line as part of
+    # the opensource release, which is capable of parsing
+    # Windows-style PDB executables.  It should live in the path, or
+    # in the same directory as jeprof.
+    $obj_tool_map{"nm_pdb"} = "nm-pdb";
+    $obj_tool_map{"addr2line_pdb"} = "addr2line-pdb";
+  }
+
+  if ($file_type =~ /Mach-O/) {
+    # OS X uses otool to examine Mach-O files, rather than objdump.
+    $obj_tool_map{"otool"} = "otool";
+    $obj_tool_map{"addr2line"} = "false";  # no addr2line
+    $obj_tool_map{"objdump"} = "false";  # no objdump
+  }
+
+  # Go fill in %obj_tool_map with the pathnames to use:
+  foreach my $tool (keys %obj_tool_map) {
+    $obj_tool_map{$tool} = ConfigureTool($obj_tool_map{$tool});
+  }
+}
+
+# Returns the path of a caller-specified object tool.  If --tools or
+# JEPROF_TOOLS are specified, then returns the full path to the tool
+# with that prefix.  Otherwise, returns the path unmodified (which
+# means we will look for it on PATH).
+sub ConfigureTool {
+  my $tool = shift;
+  my $path;
+
+  # --tools (or $JEPROF_TOOLS) is a comma separated list, where each
+  # item is either a) a pathname prefix, or b) a map of the form
+  # <tool>:<path>.  First we look for an entry of type (b) for our
+  # tool.  If one is found, we use it.  Otherwise, we consider all the
+  # pathname prefixes in turn, until one yields an existing file.  If
+  # none does, we use a default path.
+  my $tools = $main::opt_tools || $ENV{"JEPROF_TOOLS"} || "";
+  if ($tools =~ m/(,|^)\Q$tool\E:([^,]*)/) {
+    $path = $2;
+    # TODO(csilvers): sanity-check that $path exists?  Hard if it's relative.
+  } elsif ($tools ne '') {
+    foreach my $prefix (split(',', $tools)) {
+      next if ($prefix =~ /:/);    # ignore "tool:fullpath" entries in the list
+      if (-x $prefix . $tool) {
+        $path = $prefix . $tool;
+        last;
+      }
+    }
+    if (!$path) {
+      error("No '$tool' found with prefix specified by " .
+            "--tools (or \$JEPROF_TOOLS) '$tools'\n");
+    }
+  } else {
+    # ... otherwise use the version that exists in the same directory as
+    # jeprof.  If there's nothing there, use $PATH.
+    $0 =~ m,[^/]*$,;     # this is everything after the last slash
+    my $dirname = $`;    # this is everything up to and including the last slash
+    if (-x "$dirname$tool") {
+      $path = "$dirname$tool";
+    } else {
+      $path = $tool;
+    }
+  }
+  if ($main::opt_debug) { print STDERR "Using '$path' for '$tool'.\n"; }
+  return $path;
+}
+
+sub ShellEscape {
+  my @escaped_words = ();
+  foreach my $word (@_) {
+    my $escaped_word = $word;
+    if ($word =~ m![^a-zA-Z0-9/.,_=-]!) {  # check for anything not in whitelist
+      $escaped_word =~ s/'/'\\''/;
+      $escaped_word = "'$escaped_word'";
+    }
+    push(@escaped_words, $escaped_word);
+  }
+  return join(" ", @escaped_words);
+}
+
+sub cleanup {
+  unlink($main::tmpfile_sym);
+  unlink(keys %main::tempnames);
+
+  # We leave any collected profiles in $HOME/jeprof in case the user wants
+  # to look at them later.  We print a message informing them of this.
+  if ((scalar(@main::profile_files) > 0) &&
+      defined($main::collected_profile)) {
+    if (scalar(@main::profile_files) == 1) {
+      print STDERR "Dynamically gathered profile is in $main::collected_profile\n";
+    }
+    print STDERR "If you want to investigate this profile further, you can do:\n";
+    print STDERR "\n";
+    print STDERR "  jeprof \\\n";
+    print STDERR "    $main::prog \\\n";
+    print STDERR "    $main::collected_profile\n";
+    print STDERR "\n";
+  }
+}
+
+sub sighandler {
+  cleanup();
+  exit(1);
+}
+
+sub error {
+  my $msg = shift;
+  print STDERR $msg;
+  cleanup();
+  exit(1);
+}
+
+
+# Run $nm_command and get all the resulting procedure boundaries whose
+# names match "$regexp" and returns them in a hashtable mapping from
+# procedure name to a two-element vector of [start address, end address]
+sub GetProcedureBoundariesViaNm {
+  my $escaped_nm_command = shift;    # shell-escaped
+  my $regexp = shift;
+
+  my $symbol_table = {};
+  open(NM, "$escaped_nm_command |") || error("$escaped_nm_command: $!\n");
+  my $last_start = "0";
+  my $routine = "";
+  while (<NM>) {
+    s/\r//g;         # turn windows-looking lines into unix-looking lines
+    if (m/^\s*([0-9a-f]+) (.) (..*)/) {
+      my $start_val = $1;
+      my $type = $2;
+      my $this_routine = $3;
+
+      # It's possible for two symbols to share the same address, if
+      # one is a zero-length variable (like __start_google_malloc) or
+      # one symbol is a weak alias to another (like __libc_malloc).
+      # In such cases, we want to ignore all values except for the
+      # actual symbol, which in nm-speak has type "T".  The logic
+      # below does this, though it's a bit tricky: what happens when
+      # we have a series of lines with the same address, is the first
+      # one gets queued up to be processed.  However, it won't
+      # *actually* be processed until later, when we read a line with
+      # a different address.  That means that as long as we're reading
+      # lines with the same address, we have a chance to replace that
+      # item in the queue, which we do whenever we see a 'T' entry --
+      # that is, a line with type 'T'.  If we never see a 'T' entry,
+      # we'll just go ahead and process the first entry (which never
+      # got touched in the queue), and ignore the others.
+      if ($start_val eq $last_start && $type =~ /t/i) {
+        # We are the 'T' symbol at this address, replace previous symbol.
+        $routine = $this_routine;
+        next;
+      } elsif ($start_val eq $last_start) {
+        # We're not the 'T' symbol at this address, so ignore us.
+        next;
+      }
+
+      if ($this_routine eq $sep_symbol) {
+        $sep_address = HexExtend($start_val);
+      }
+
+      # Tag this routine with the starting address in case the image
+      # has multiple occurrences of this routine.  We use a syntax
+      # that resembles template parameters that are automatically
+      # stripped out by ShortFunctionName()
+      $this_routine .= "<$start_val>";
+
+      if (defined($routine) && $routine =~ m/$regexp/) {
+        $symbol_table->{$routine} = [HexExtend($last_start),
+                                     HexExtend($start_val)];
+      }
+      $last_start = $start_val;
+      $routine = $this_routine;
+    } elsif (m/^Loaded image name: (.+)/) {
+      # The win32 nm workalike emits information about the binary it is using.
+      if ($main::opt_debug) { print STDERR "Using Image $1\n"; }
+    } elsif (m/^PDB file name: (.+)/) {
+      # The win32 nm workalike emits information about the pdb it is using.
+      if ($main::opt_debug) { print STDERR "Using PDB $1\n"; }
+    }
+  }
+  close(NM);
+  # Handle the last line in the nm output.  Unfortunately, we don't know
+  # how big this last symbol is, because we don't know how big the file
+  # is.  For now, we just give it a size of 0.
+  # TODO(csilvers): do better here.
+  if (defined($routine) && $routine =~ m/$regexp/) {
+    $symbol_table->{$routine} = [HexExtend($last_start),
+                                 HexExtend($last_start)];
+  }
+  return $symbol_table;
+}
+
+# Gets the procedure boundaries for all routines in "$image" whose names
+# match "$regexp" and returns them in a hashtable mapping from procedure
+# name to a two-element vector of [start address, end address].
+# Will return an empty map if nm is not installed or not working properly.
+sub GetProcedureBoundaries {
+  my $image = shift;
+  my $regexp = shift;
+
+  # If $image doesn't start with /, then put ./ in front of it.  This works
+  # around an obnoxious bug in our probing of nm -f behavior.
+  # "nm -f $image" is supposed to fail on GNU nm, but if:
+  #
+  # a. $image starts with [BbSsPp] (for example, bin/foo/bar), AND
+  # b. you have a.out in your current directory (a not uncommon occurence)
+  #
+  # then "nm -f $image" succeeds because -f only looks at the first letter of
+  # the argument, which looks valid because it's [BbSsPp], and then since
+  # there's no image provided, it looks for a.out and finds it.
+  #
+  # This regex makes sure that $image starts with . or /, forcing the -f
+  # parsing to fail since . and / are not valid formats.
+  $image =~ s#^[^/]#./$&#;
+
+  # For libc libraries, the copy in /usr/lib/debug contains debugging symbols
+  my $debugging = DebuggingLibrary($image);
+  if ($debugging) {
+    $image = $debugging;
+  }
+
+  my $nm = $obj_tool_map{"nm"};
+  my $cppfilt = $obj_tool_map{"c++filt"};
+
+  # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm
+  # binary doesn't support --demangle.  In addition, for OS X we need
+  # to use the -f flag to get 'flat' nm output (otherwise we don't sort
+  # properly and get incorrect results).  Unfortunately, GNU nm uses -f
+  # in an incompatible way.  So first we test whether our nm supports
+  # --demangle and -f.
+  my $demangle_flag = "";
+  my $cppfilt_flag = "";
+  my $to_devnull = ">$dev_null 2>&1";
+  if (system(ShellEscape($nm, "--demangle", "image") . $to_devnull) == 0) {
+    # In this mode, we do "nm --demangle <foo>"
+    $demangle_flag = "--demangle";
+    $cppfilt_flag = "";
+  } elsif (system(ShellEscape($cppfilt, $image) . $to_devnull) == 0) {
+    # In this mode, we do "nm <foo> | c++filt"
+    $cppfilt_flag = " | " . ShellEscape($cppfilt);
+  };
+  my $flatten_flag = "";
+  if (system(ShellEscape($nm, "-f", $image) . $to_devnull) == 0) {
+    $flatten_flag = "-f";
+  }
+
+  # Finally, in the case $imagie isn't a debug library, we try again with
+  # -D to at least get *exported* symbols.  If we can't use --demangle,
+  # we use c++filt instead, if it exists on this system.
+  my @nm_commands = (ShellEscape($nm, "-n", $flatten_flag, $demangle_flag,
+                                 $image) . " 2>$dev_null $cppfilt_flag",
+                     ShellEscape($nm, "-D", "-n", $flatten_flag, $demangle_flag,
+                                 $image) . " 2>$dev_null $cppfilt_flag",
+                     # 6nm is for Go binaries
+                     ShellEscape("6nm", "$image") . " 2>$dev_null | sort",
+                     );
+
+  # If the executable is an MS Windows PDB-format executable, we'll
+  # have set up obj_tool_map("nm_pdb").  In this case, we actually
+  # want to use both unix nm and windows-specific nm_pdb, since
+  # PDB-format executables can apparently include dwarf .o files.
+  if (exists $obj_tool_map{"nm_pdb"}) {
+    push(@nm_commands,
+         ShellEscape($obj_tool_map{"nm_pdb"}, "--demangle", $image)
+         . " 2>$dev_null");
+  }
+
+  foreach my $nm_command (@nm_commands) {
+    my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp);
+    return $symbol_table if (%{$symbol_table});
+  }
+  my $symbol_table = {};
+  return $symbol_table;
+}
+
+
+# The test vectors for AddressAdd/Sub/Inc are 8-16-nibble hex strings.
+# To make them more readable, we add underscores at interesting places.
+# This routine removes the underscores, producing the canonical representation
+# used by jeprof to represent addresses, particularly in the tested routines.
+sub CanonicalHex {
+  my $arg = shift;
+  return join '', (split '_',$arg);
+}
+
+
+# Unit test for AddressAdd:
+sub AddressAddUnitTest {
+  my $test_data_8 = shift;
+  my $test_data_16 = shift;
+  my $error_count = 0;
+  my $fail_count = 0;
+  my $pass_count = 0;
+  # print STDERR "AddressAddUnitTest: ", 1+$#{$test_data_8}, " tests\n";
+
+  # First a few 8-nibble addresses.  Note that this implementation uses
+  # plain old arithmetic, so a quick sanity check along with verifying what
+  # happens to overflow (we want it to wrap):
+  $address_length = 8;
+  foreach my $row (@{$test_data_8}) {
+    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
+    my $sum = AddressAdd ($row->[0], $row->[1]);
+    if ($sum ne $row->[2]) {
+      printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
+             $row->[0], $row->[1], $row->[2];
+      ++$fail_count;
+    } else {
+      ++$pass_count;
+    }
+  }
+  printf STDERR "AddressAdd 32-bit tests: %d passes, %d failures\n",
+         $pass_count, $fail_count;
+  $error_count = $fail_count;
+  $fail_count = 0;
+  $pass_count = 0;
+
+  # Now 16-nibble addresses.
+  $address_length = 16;
+  foreach my $row (@{$test_data_16}) {
+    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
+    my $sum = AddressAdd (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
+    my $expected = join '', (split '_',$row->[2]);
+    if ($sum ne CanonicalHex($row->[2])) {
+      printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
+             $row->[0], $row->[1], $row->[2];
+      ++$fail_count;
+    } else {
+      ++$pass_count;
+    }
+  }
+  printf STDERR "AddressAdd 64-bit tests: %d passes, %d failures\n",
+         $pass_count, $fail_count;
+  $error_count += $fail_count;
+
+  return $error_count;
+}
+
+
+# Unit test for AddressSub:
+sub AddressSubUnitTest {
+  my $test_data_8 = shift;
+  my $test_data_16 = shift;
+  my $error_count = 0;
+  my $fail_count = 0;
+  my $pass_count = 0;
+  # print STDERR "AddressSubUnitTest: ", 1+$#{$test_data_8}, " tests\n";
+
+  # First a few 8-nibble addresses.  Note that this implementation uses
+  # plain old arithmetic, so a quick sanity check along with verifying what
+  # happens to overflow (we want it to wrap):
+  $address_length = 8;
+  foreach my $row (@{$test_data_8}) {
+    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
+    my $sum = AddressSub ($row->[0], $row->[1]);
+    if ($sum ne $row->[3]) {
+      printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
+             $row->[0], $row->[1], $row->[3];
+      ++$fail_count;
+    } else {
+      ++$pass_count;
+    }
+  }
+  printf STDERR "AddressSub 32-bit tests: %d passes, %d failures\n",
+         $pass_count, $fail_count;
+  $error_count = $fail_count;
+  $fail_count = 0;
+  $pass_count = 0;
+
+  # Now 16-nibble addresses.
+  $address_length = 16;
+  foreach my $row (@{$test_data_16}) {
+    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
+    my $sum = AddressSub (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
+    if ($sum ne CanonicalHex($row->[3])) {
+      printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
+             $row->[0], $row->[1], $row->[3];
+      ++$fail_count;
+    } else {
+      ++$pass_count;
+    }
+  }
+  printf STDERR "AddressSub 64-bit tests: %d passes, %d failures\n",
+         $pass_count, $fail_count;
+  $error_count += $fail_count;
+
+  return $error_count;
+}
+
+
+# Unit test for AddressInc:
+sub AddressIncUnitTest {
+  my $test_data_8 = shift;
+  my $test_data_16 = shift;
+  my $error_count = 0;
+  my $fail_count = 0;
+  my $pass_count = 0;
+  # print STDERR "AddressIncUnitTest: ", 1+$#{$test_data_8}, " tests\n";
+
+  # First a few 8-nibble addresses.  Note that this implementation uses
+  # plain old arithmetic, so a quick sanity check along with verifying what
+  # happens to overflow (we want it to wrap):
+  $address_length = 8;
+  foreach my $row (@{$test_data_8}) {
+    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
+    my $sum = AddressInc ($row->[0]);
+    if ($sum ne $row->[4]) {
+      printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
+             $row->[0], $row->[4];
+      ++$fail_count;
+    } else {
+      ++$pass_count;
+    }
+  }
+  printf STDERR "AddressInc 32-bit tests: %d passes, %d failures\n",
+         $pass_count, $fail_count;
+  $error_count = $fail_count;
+  $fail_count = 0;
+  $pass_count = 0;
+
+  # Now 16-nibble addresses.
+  $address_length = 16;
+  foreach my $row (@{$test_data_16}) {
+    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
+    my $sum = AddressInc (CanonicalHex($row->[0]));
+    if ($sum ne CanonicalHex($row->[4])) {
+      printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
+             $row->[0], $row->[4];
+      ++$fail_count;
+    } else {
+      ++$pass_count;
+    }
+  }
+  printf STDERR "AddressInc 64-bit tests: %d passes, %d failures\n",
+         $pass_count, $fail_count;
+  $error_count += $fail_count;
+
+  return $error_count;
+}
+
+
+# Driver for unit tests.
+# Currently just the address add/subtract/increment routines for 64-bit.
+sub RunUnitTests {
+  my $error_count = 0;
+
+  # This is a list of tuples [a, b, a+b, a-b, a+1]
+  my $unit_test_data_8 = [
+    [qw(aaaaaaaa 50505050 fafafafa 5a5a5a5a aaaaaaab)],
+    [qw(50505050 aaaaaaaa fafafafa a5a5a5a6 50505051)],
+    [qw(ffffffff aaaaaaaa aaaaaaa9 55555555 00000000)],
+    [qw(00000001 ffffffff 00000000 00000002 00000002)],
+    [qw(00000001 fffffff0 fffffff1 00000011 00000002)],
+  ];
+  my $unit_test_data_16 = [
+    # The implementation handles data in 7-nibble chunks, so those are the
+    # interesting boundaries.
+    [qw(aaaaaaaa 50505050
+        00_000000f_afafafa 00_0000005_a5a5a5a 00_000000a_aaaaaab)],
+    [qw(50505050 aaaaaaaa
+        00_000000f_afafafa ff_ffffffa_5a5a5a6 00_0000005_0505051)],
+    [qw(ffffffff aaaaaaaa
+        00_000001a_aaaaaa9 00_0000005_5555555 00_0000010_0000000)],
+    [qw(00000001 ffffffff
+        00_0000010_0000000 ff_ffffff0_0000002 00_0000000_0000002)],
+    [qw(00000001 fffffff0
+        00_000000f_ffffff1 ff_ffffff0_0000011 00_0000000_0000002)],
+
+    [qw(00_a00000a_aaaaaaa 50505050
+        00_a00000f_afafafa 00_a000005_a5a5a5a 00_a00000a_aaaaaab)],
+    [qw(0f_fff0005_0505050 aaaaaaaa
+        0f_fff000f_afafafa 0f_ffefffa_5a5a5a6 0f_fff0005_0505051)],
+    [qw(00_000000f_fffffff 01_800000a_aaaaaaa
+        01_800001a_aaaaaa9 fe_8000005_5555555 00_0000010_0000000)],
+    [qw(00_0000000_0000001 ff_fffffff_fffffff
+        00_0000000_0000000 00_0000000_0000002 00_0000000_0000002)],
+    [qw(00_0000000_0000001 ff_fffffff_ffffff0
+        ff_fffffff_ffffff1 00_0000000_0000011 00_0000000_0000002)],
+  ];
+
+  $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16);
+  $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16);
+  $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16);
+  if ($error_count > 0) {
+    print STDERR $error_count, " errors: FAILED\n";
+  } else {
+    print STDERR "PASS\n";
+  }
+  exit ($error_count);
+}
diff --git a/src/jemalloc/bin/pprof b/src/jemalloc/bin/pprof
deleted file mode 100755 (executable)
index 5a4c6cd..0000000
+++ /dev/null
@@ -1,5507 +0,0 @@
-#! /usr/bin/env perl
-
-# Copyright (c) 1998-2007, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# ---
-# Program for printing the profile generated by common/profiler.cc,
-# or by the heap profiler (common/debugallocation.cc)
-#
-# The profile contains a sequence of entries of the form:
-#       <count> <stack trace>
-# This program parses the profile, and generates user-readable
-# output.
-#
-# Examples:
-#
-# % tools/pprof "program" "profile"
-#   Enters "interactive" mode
-#
-# % tools/pprof --text "program" "profile"
-#   Generates one line per procedure
-#
-# % tools/pprof --gv "program" "profile"
-#   Generates annotated call-graph and displays via "gv"
-#
-# % tools/pprof --gv --focus=Mutex "program" "profile"
-#   Restrict to code paths that involve an entry that matches "Mutex"
-#
-# % tools/pprof --gv --focus=Mutex --ignore=string "program" "profile"
-#   Restrict to code paths that involve an entry that matches "Mutex"
-#   and does not match "string"
-#
-# % tools/pprof --list=IBF_CheckDocid "program" "profile"
-#   Generates disassembly listing of all routines with at least one
-#   sample that match the --list=<regexp> pattern.  The listing is
-#   annotated with the flat and cumulative sample counts at each line.
-#
-# % tools/pprof --disasm=IBF_CheckDocid "program" "profile"
-#   Generates disassembly listing of all routines with at least one
-#   sample that match the --disasm=<regexp> pattern.  The listing is
-#   annotated with the flat and cumulative sample counts at each PC value.
-#
-# TODO: Use color to indicate files?
-
-use strict;
-use warnings;
-use Getopt::Long;
-
-my $PPROF_VERSION = "2.0";
-
-# These are the object tools we use which can come from a
-# user-specified location using --tools, from the PPROF_TOOLS
-# environment variable, or from the environment.
-my %obj_tool_map = (
-  "objdump" => "objdump",
-  "nm" => "nm",
-  "addr2line" => "addr2line",
-  "c++filt" => "c++filt",
-  ## ConfigureObjTools may add architecture-specific entries:
-  #"nm_pdb" => "nm-pdb",       # for reading windows (PDB-format) executables
-  #"addr2line_pdb" => "addr2line-pdb",                                # ditto
-  #"otool" => "otool",         # equivalent of objdump on OS X
-);
-# NOTE: these are lists, so you can put in commandline flags if you want.
-my @DOT = ("dot");          # leave non-absolute, since it may be in /usr/local
-my @GV = ("gv");
-my @EVINCE = ("evince");    # could also be xpdf or perhaps acroread
-my @KCACHEGRIND = ("kcachegrind");
-my @PS2PDF = ("ps2pdf");
-# These are used for dynamic profiles
-my @URL_FETCHER = ("curl", "-s");
-
-# These are the web pages that servers need to support for dynamic profiles
-my $HEAP_PAGE = "/pprof/heap";
-my $PROFILE_PAGE = "/pprof/profile";   # must support cgi-param "?seconds=#"
-my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\?.*)?"; # must support cgi-param
-                                                # ?seconds=#&event=x&period=n
-my $GROWTH_PAGE = "/pprof/growth";
-my $CONTENTION_PAGE = "/pprof/contention";
-my $WALL_PAGE = "/pprof/wall(?:\\?.*)?";  # accepts options like namefilter
-my $FILTEREDPROFILE_PAGE = "/pprof/filteredprofile(?:\\?.*)?";
-my $CENSUSPROFILE_PAGE = "/pprof/censusprofile(?:\\?.*)?"; # must support cgi-param
-                                                       # "?seconds=#",
-                                                       # "?tags_regexp=#" and
-                                                       # "?type=#".
-my $SYMBOL_PAGE = "/pprof/symbol";     # must support symbol lookup via POST
-my $PROGRAM_NAME_PAGE = "/pprof/cmdline";
-
-# These are the web pages that can be named on the command line.
-# All the alternatives must begin with /.
-my $PROFILES = "($HEAP_PAGE|$PROFILE_PAGE|$PMUPROFILE_PAGE|" .
-               "$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|" .
-               "$FILTEREDPROFILE_PAGE|$CENSUSPROFILE_PAGE)";
-
-# default binary name
-my $UNKNOWN_BINARY = "(unknown)";
-
-# There is a pervasive dependency on the length (in hex characters,
-# i.e., nibbles) of an address, distinguishing between 32-bit and
-# 64-bit profiles.  To err on the safe size, default to 64-bit here:
-my $address_length = 16;
-
-my $dev_null = "/dev/null";
-if (! -e $dev_null && $^O =~ /MSWin/) {    # $^O is the OS perl was built for
-  $dev_null = "nul";
-}
-
-# A list of paths to search for shared object files
-my @prefix_list = ();
-
-# Special routine name that should not have any symbols.
-# Used as separator to parse "addr2line -i" output.
-my $sep_symbol = '_fini';
-my $sep_address = undef;
-
-##### Argument parsing #####
-
-sub usage_string {
-  return <<EOF;
-Usage:
-pprof [options] <program> <profiles>
-   <profiles> is a space separated list of profile names.
-pprof [options] <symbolized-profiles>
-   <symbolized-profiles> is a list of profile files where each file contains
-   the necessary symbol mappings  as well as profile data (likely generated
-   with --raw).
-pprof [options] <profile>
-   <profile> is a remote form.  Symbols are obtained from host:port$SYMBOL_PAGE
-
-   Each name can be:
-   /path/to/profile        - a path to a profile file
-   host:port[/<service>]   - a location of a service to get profile from
-
-   The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
-                         $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
-                         $CENSUSPROFILE_PAGE, or /pprof/filteredprofile.
-   For instance:
-     pprof http://myserver.com:80$HEAP_PAGE
-   If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
-pprof --symbols <program>
-   Maps addresses to symbol names.  In this mode, stdin should be a
-   list of library mappings, in the same format as is found in the heap-
-   and cpu-profile files (this loosely matches that of /proc/self/maps
-   on linux), followed by a list of hex addresses to map, one per line.
-
-   For more help with querying remote servers, including how to add the
-   necessary server-side support code, see this filename (or one like it):
-
-   /usr/doc/gperftools-$PPROF_VERSION/pprof_remote_servers.html
-
-Options:
-   --cum               Sort by cumulative data
-   --base=<base>       Subtract <base> from <profile> before display
-   --interactive       Run in interactive mode (interactive "help" gives help) [default]
-   --seconds=<n>       Length of time for dynamic profiles [default=30 secs]
-   --add_lib=<file>    Read additional symbols and line info from the given library
-   --lib_prefix=<dir>  Comma separated list of library path prefixes
-
-Reporting Granularity:
-   --addresses         Report at address level
-   --lines             Report at source line level
-   --functions         Report at function level [default]
-   --files             Report at source file level
-
-Output type:
-   --text              Generate text report
-   --callgrind         Generate callgrind format to stdout
-   --gv                Generate Postscript and display
-   --evince            Generate PDF and display
-   --web               Generate SVG and display
-   --list=<regexp>     Generate source listing of matching routines
-   --disasm=<regexp>   Generate disassembly of matching routines
-   --symbols           Print demangled symbol names found at given addresses
-   --dot               Generate DOT file to stdout
-   --ps                Generate Postcript to stdout
-   --pdf               Generate PDF to stdout
-   --svg               Generate SVG to stdout
-   --gif               Generate GIF to stdout
-   --raw               Generate symbolized pprof data (useful with remote fetch)
-
-Heap-Profile Options:
-   --inuse_space       Display in-use (mega)bytes [default]
-   --inuse_objects     Display in-use objects
-   --alloc_space       Display allocated (mega)bytes
-   --alloc_objects     Display allocated objects
-   --show_bytes        Display space in bytes
-   --drop_negative     Ignore negative differences
-
-Contention-profile options:
-   --total_delay       Display total delay at each region [default]
-   --contentions       Display number of delays at each region
-   --mean_delay        Display mean delay at each region
-
-Call-graph Options:
-   --nodecount=<n>     Show at most so many nodes [default=80]
-   --nodefraction=<f>  Hide nodes below <f>*total [default=.005]
-   --edgefraction=<f>  Hide edges below <f>*total [default=.001]
-   --maxdegree=<n>     Max incoming/outgoing edges per node [default=8]
-   --focus=<regexp>    Focus on nodes matching <regexp>
-   --thread=<n>        Show profile for thread <n>
-   --ignore=<regexp>   Ignore nodes matching <regexp>
-   --scale=<n>         Set GV scaling [default=0]
-   --heapcheck         Make nodes with non-0 object counts
-                       (i.e. direct leak generators) more visible
-
-Miscellaneous:
-   --tools=<prefix or binary:fullpath>[,...]   \$PATH for object tool pathnames
-   --test              Run unit tests
-   --help              This message
-   --version           Version information
-
-Environment Variables:
-   PPROF_TMPDIR        Profiles directory. Defaults to \$HOME/pprof
-   PPROF_TOOLS         Prefix for object tools pathnames
-
-Examples:
-
-pprof /bin/ls ls.prof
-                       Enters "interactive" mode
-pprof --text /bin/ls ls.prof
-                       Outputs one line per procedure
-pprof --web /bin/ls ls.prof
-                       Displays annotated call-graph in web browser
-pprof --gv /bin/ls ls.prof
-                       Displays annotated call-graph via 'gv'
-pprof --gv --focus=Mutex /bin/ls ls.prof
-                       Restricts to code paths including a .*Mutex.* entry
-pprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof
-                       Code paths including Mutex but not string
-pprof --list=getdir /bin/ls ls.prof
-                       (Per-line) annotated source listing for getdir()
-pprof --disasm=getdir /bin/ls ls.prof
-                       (Per-PC) annotated disassembly for getdir()
-
-pprof http://localhost:1234/
-                       Enters "interactive" mode
-pprof --text localhost:1234
-                       Outputs one line per procedure for localhost:1234
-pprof --raw localhost:1234 > ./local.raw
-pprof --text ./local.raw
-                       Fetches a remote profile for later analysis and then
-                       analyzes it in text mode.
-EOF
-}
-
-sub version_string {
-  return <<EOF
-pprof (part of gperftools $PPROF_VERSION)
-
-Copyright 1998-2007 Google Inc.
-
-This is BSD licensed software; see the source for copying conditions
-and license information.
-There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.
-EOF
-}
-
-sub usage {
-  my $msg = shift;
-  print STDERR "$msg\n\n";
-  print STDERR usage_string();
-  print STDERR "\nFATAL ERROR: $msg\n";    # just as a reminder
-  exit(1);
-}
-
-sub Init() {
-  # Setup tmp-file name and handler to clean it up.
-  # We do this in the very beginning so that we can use
-  # error() and cleanup() function anytime here after.
-  $main::tmpfile_sym = "/tmp/pprof$$.sym";
-  $main::tmpfile_ps = "/tmp/pprof$$";
-  $main::next_tmpfile = 0;
-  $SIG{'INT'} = \&sighandler;
-
-  # Cache from filename/linenumber to source code
-  $main::source_cache = ();
-
-  $main::opt_help = 0;
-  $main::opt_version = 0;
-
-  $main::opt_cum = 0;
-  $main::opt_base = '';
-  $main::opt_addresses = 0;
-  $main::opt_lines = 0;
-  $main::opt_functions = 0;
-  $main::opt_files = 0;
-  $main::opt_lib_prefix = "";
-
-  $main::opt_text = 0;
-  $main::opt_callgrind = 0;
-  $main::opt_list = "";
-  $main::opt_disasm = "";
-  $main::opt_symbols = 0;
-  $main::opt_gv = 0;
-  $main::opt_evince = 0;
-  $main::opt_web = 0;
-  $main::opt_dot = 0;
-  $main::opt_ps = 0;
-  $main::opt_pdf = 0;
-  $main::opt_gif = 0;
-  $main::opt_svg = 0;
-  $main::opt_raw = 0;
-
-  $main::opt_nodecount = 80;
-  $main::opt_nodefraction = 0.005;
-  $main::opt_edgefraction = 0.001;
-  $main::opt_maxdegree = 8;
-  $main::opt_focus = '';
-  $main::opt_thread = undef;
-  $main::opt_ignore = '';
-  $main::opt_scale = 0;
-  $main::opt_heapcheck = 0;
-  $main::opt_seconds = 30;
-  $main::opt_lib = "";
-
-  $main::opt_inuse_space   = 0;
-  $main::opt_inuse_objects = 0;
-  $main::opt_alloc_space   = 0;
-  $main::opt_alloc_objects = 0;
-  $main::opt_show_bytes    = 0;
-  $main::opt_drop_negative = 0;
-  $main::opt_interactive   = 0;
-
-  $main::opt_total_delay = 0;
-  $main::opt_contentions = 0;
-  $main::opt_mean_delay = 0;
-
-  $main::opt_tools   = "";
-  $main::opt_debug   = 0;
-  $main::opt_test    = 0;
-
-  # These are undocumented flags used only by unittests.
-  $main::opt_test_stride = 0;
-
-  # Are we using $SYMBOL_PAGE?
-  $main::use_symbol_page = 0;
-
-  # Files returned by TempName.
-  %main::tempnames = ();
-
-  # Type of profile we are dealing with
-  # Supported types:
-  #     cpu
-  #     heap
-  #     growth
-  #     contention
-  $main::profile_type = '';     # Empty type means "unknown"
-
-  GetOptions("help!"          => \$main::opt_help,
-             "version!"       => \$main::opt_version,
-             "cum!"           => \$main::opt_cum,
-             "base=s"         => \$main::opt_base,
-             "seconds=i"      => \$main::opt_seconds,
-             "add_lib=s"      => \$main::opt_lib,
-             "lib_prefix=s"   => \$main::opt_lib_prefix,
-             "functions!"     => \$main::opt_functions,
-             "lines!"         => \$main::opt_lines,
-             "addresses!"     => \$main::opt_addresses,
-             "files!"         => \$main::opt_files,
-             "text!"          => \$main::opt_text,
-             "callgrind!"     => \$main::opt_callgrind,
-             "list=s"         => \$main::opt_list,
-             "disasm=s"       => \$main::opt_disasm,
-             "symbols!"       => \$main::opt_symbols,
-             "gv!"            => \$main::opt_gv,
-             "evince!"        => \$main::opt_evince,
-             "web!"           => \$main::opt_web,
-             "dot!"           => \$main::opt_dot,
-             "ps!"            => \$main::opt_ps,
-             "pdf!"           => \$main::opt_pdf,
-             "svg!"           => \$main::opt_svg,
-             "gif!"           => \$main::opt_gif,
-             "raw!"           => \$main::opt_raw,
-             "interactive!"   => \$main::opt_interactive,
-             "nodecount=i"    => \$main::opt_nodecount,
-             "nodefraction=f" => \$main::opt_nodefraction,
-             "edgefraction=f" => \$main::opt_edgefraction,
-             "maxdegree=i"    => \$main::opt_maxdegree,
-             "focus=s"        => \$main::opt_focus,
-             "thread=i"       => \$main::opt_thread,
-             "ignore=s"       => \$main::opt_ignore,
-             "scale=i"        => \$main::opt_scale,
-             "heapcheck"      => \$main::opt_heapcheck,
-             "inuse_space!"   => \$main::opt_inuse_space,
-             "inuse_objects!" => \$main::opt_inuse_objects,
-             "alloc_space!"   => \$main::opt_alloc_space,
-             "alloc_objects!" => \$main::opt_alloc_objects,
-             "show_bytes!"    => \$main::opt_show_bytes,
-             "drop_negative!" => \$main::opt_drop_negative,
-             "total_delay!"   => \$main::opt_total_delay,
-             "contentions!"   => \$main::opt_contentions,
-             "mean_delay!"    => \$main::opt_mean_delay,
-             "tools=s"        => \$main::opt_tools,
-             "test!"          => \$main::opt_test,
-             "debug!"         => \$main::opt_debug,
-             # Undocumented flags used only by unittests:
-             "test_stride=i"  => \$main::opt_test_stride,
-      ) || usage("Invalid option(s)");
-
-  # Deal with the standard --help and --version
-  if ($main::opt_help) {
-    print usage_string();
-    exit(0);
-  }
-
-  if ($main::opt_version) {
-    print version_string();
-    exit(0);
-  }
-
-  # Disassembly/listing/symbols mode requires address-level info
-  if ($main::opt_disasm || $main::opt_list || $main::opt_symbols) {
-    $main::opt_functions = 0;
-    $main::opt_lines = 0;
-    $main::opt_addresses = 1;
-    $main::opt_files = 0;
-  }
-
-  # Check heap-profiling flags
-  if ($main::opt_inuse_space +
-      $main::opt_inuse_objects +
-      $main::opt_alloc_space +
-      $main::opt_alloc_objects > 1) {
-    usage("Specify at most on of --inuse/--alloc options");
-  }
-
-  # Check output granularities
-  my $grains =
-      $main::opt_functions +
-      $main::opt_lines +
-      $main::opt_addresses +
-      $main::opt_files +
-      0;
-  if ($grains > 1) {
-    usage("Only specify one output granularity option");
-  }
-  if ($grains == 0) {
-    $main::opt_functions = 1;
-  }
-
-  # Check output modes
-  my $modes =
-      $main::opt_text +
-      $main::opt_callgrind +
-      ($main::opt_list eq '' ? 0 : 1) +
-      ($main::opt_disasm eq '' ? 0 : 1) +
-      ($main::opt_symbols == 0 ? 0 : 1) +
-      $main::opt_gv +
-      $main::opt_evince +
-      $main::opt_web +
-      $main::opt_dot +
-      $main::opt_ps +
-      $main::opt_pdf +
-      $main::opt_svg +
-      $main::opt_gif +
-      $main::opt_raw +
-      $main::opt_interactive +
-      0;
-  if ($modes > 1) {
-    usage("Only specify one output mode");
-  }
-  if ($modes == 0) {
-    if (-t STDOUT) {  # If STDOUT is a tty, activate interactive mode
-      $main::opt_interactive = 1;
-    } else {
-      $main::opt_text = 1;
-    }
-  }
-
-  if ($main::opt_test) {
-    RunUnitTests();
-    # Should not return
-    exit(1);
-  }
-
-  # Binary name and profile arguments list
-  $main::prog = "";
-  @main::pfile_args = ();
-
-  # Remote profiling without a binary (using $SYMBOL_PAGE instead)
-  if (@ARGV > 0) {
-    if (IsProfileURL($ARGV[0])) {
-      $main::use_symbol_page = 1;
-    } elsif (IsSymbolizedProfileFile($ARGV[0])) {
-      $main::use_symbolized_profile = 1;
-      $main::prog = $UNKNOWN_BINARY;  # will be set later from the profile file
-    }
-  }
-
-  if ($main::use_symbol_page || $main::use_symbolized_profile) {
-    # We don't need a binary!
-    my %disabled = ('--lines' => $main::opt_lines,
-                    '--disasm' => $main::opt_disasm);
-    for my $option (keys %disabled) {
-      usage("$option cannot be used without a binary") if $disabled{$option};
-    }
-    # Set $main::prog later...
-    scalar(@ARGV) || usage("Did not specify profile file");
-  } elsif ($main::opt_symbols) {
-    # --symbols needs a binary-name (to run nm on, etc) but not profiles
-    $main::prog = shift(@ARGV) || usage("Did not specify program");
-  } else {
-    $main::prog = shift(@ARGV) || usage("Did not specify program");
-    scalar(@ARGV) || usage("Did not specify profile file");
-  }
-
-  # Parse profile file/location arguments
-  foreach my $farg (@ARGV) {
-    if ($farg =~ m/(.*)\@([0-9]+)(|\/.*)$/ ) {
-      my $machine = $1;
-      my $num_machines = $2;
-      my $path = $3;
-      for (my $i = 0; $i < $num_machines; $i++) {
-        unshift(@main::pfile_args, "$i.$machine$path");
-      }
-    } else {
-      unshift(@main::pfile_args, $farg);
-    }
-  }
-
-  if ($main::use_symbol_page) {
-    unless (IsProfileURL($main::pfile_args[0])) {
-      error("The first profile should be a remote form to use $SYMBOL_PAGE\n");
-    }
-    CheckSymbolPage();
-    $main::prog = FetchProgramName();
-  } elsif (!$main::use_symbolized_profile) {  # may not need objtools!
-    ConfigureObjTools($main::prog)
-  }
-
-  # Break the opt_lib_prefix into the prefix_list array
-  @prefix_list = split (',', $main::opt_lib_prefix);
-
-  # Remove trailing / from the prefixes, in the list to prevent
-  # searching things like /my/path//lib/mylib.so
-  foreach (@prefix_list) {
-    s|/+$||;
-  }
-}
-
-sub FilterAndPrint {
-  my ($profile, $symbols, $libs, $thread) = @_;
-
-  # Get total data in profile
-  my $total = TotalProfile($profile);
-
-  # Remove uniniteresting stack items
-  $profile = RemoveUninterestingFrames($symbols, $profile);
-
-  # Focus?
-  if ($main::opt_focus ne '') {
-    $profile = FocusProfile($symbols, $profile, $main::opt_focus);
-  }
-
-  # Ignore?
-  if ($main::opt_ignore ne '') {
-    $profile = IgnoreProfile($symbols, $profile, $main::opt_ignore);
-  }
-
-  my $calls = ExtractCalls($symbols, $profile);
-
-  # Reduce profiles to required output granularity, and also clean
-  # each stack trace so a given entry exists at most once.
-  my $reduced = ReduceProfile($symbols, $profile);
-
-  # Get derived profiles
-  my $flat = FlatProfile($reduced);
-  my $cumulative = CumulativeProfile($reduced);
-
-  # Print
-  if (!$main::opt_interactive) {
-    if ($main::opt_disasm) {
-      PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm);
-    } elsif ($main::opt_list) {
-      PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
-    } elsif ($main::opt_text) {
-      # Make sure the output is empty when have nothing to report
-      # (only matters when --heapcheck is given but we must be
-      # compatible with old branches that did not pass --heapcheck always):
-      if ($total != 0) {
-        printf("Total%s: %s %s\n",
-               (defined($thread) ? " (t$thread)" : ""),
-               Unparse($total), Units());
-      }
-      PrintText($symbols, $flat, $cumulative, -1);
-    } elsif ($main::opt_raw) {
-      PrintSymbolizedProfile($symbols, $profile, $main::prog);
-    } elsif ($main::opt_callgrind) {
-      PrintCallgrind($calls);
-    } else {
-      if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
-        if ($main::opt_gv) {
-          RunGV(TempName($main::next_tmpfile, "ps"), "");
-        } elsif ($main::opt_evince) {
-          RunEvince(TempName($main::next_tmpfile, "pdf"), "");
-        } elsif ($main::opt_web) {
-          my $tmp = TempName($main::next_tmpfile, "svg");
-          RunWeb($tmp);
-          # The command we run might hand the file name off
-          # to an already running browser instance and then exit.
-          # Normally, we'd remove $tmp on exit (right now),
-          # but fork a child to remove $tmp a little later, so that the
-          # browser has time to load it first.
-          delete $main::tempnames{$tmp};
-          if (fork() == 0) {
-            sleep 5;
-            unlink($tmp);
-            exit(0);
-          }
-        }
-      } else {
-        cleanup();
-        exit(1);
-      }
-    }
-  } else {
-    InteractiveMode($profile, $symbols, $libs, $total);
-  }
-}
-
-sub Main() {
-  Init();
-  $main::collected_profile = undef;
-  @main::profile_files = ();
-  $main::op_time = time();
-
-  # Printing symbols is special and requires a lot less info that most.
-  if ($main::opt_symbols) {
-    PrintSymbols(*STDIN);   # Get /proc/maps and symbols output from stdin
-    return;
-  }
-
-  # Fetch all profile data
-  FetchDynamicProfiles();
-
-  # this will hold symbols that we read from the profile files
-  my $symbol_map = {};
-
-  # Read one profile, pick the last item on the list
-  my $data = ReadProfile($main::prog, pop(@main::profile_files));
-  my $profile = $data->{profile};
-  my $pcs = $data->{pcs};
-  my $libs = $data->{libs};   # Info about main program and shared libraries
-  $symbol_map = MergeSymbols($symbol_map, $data->{symbols});
-
-  # Add additional profiles, if available.
-  if (scalar(@main::profile_files) > 0) {
-    foreach my $pname (@main::profile_files) {
-      my $data2 = ReadProfile($main::prog, $pname);
-      $profile = AddProfile($profile, $data2->{profile});
-      $pcs = AddPcs($pcs, $data2->{pcs});
-      $symbol_map = MergeSymbols($symbol_map, $data2->{symbols});
-    }
-  }
-
-  # Subtract base from profile, if specified
-  if ($main::opt_base ne '') {
-    my $base = ReadProfile($main::prog, $main::opt_base);
-    $profile = SubtractProfile($profile, $base->{profile});
-    $pcs = AddPcs($pcs, $base->{pcs});
-    $symbol_map = MergeSymbols($symbol_map, $base->{symbols});
-  }
-
-  # Collect symbols
-  my $symbols;
-  if ($main::use_symbolized_profile) {
-    $symbols = FetchSymbols($pcs, $symbol_map);
-  } elsif ($main::use_symbol_page) {
-    $symbols = FetchSymbols($pcs);
-  } else {
-    # TODO(csilvers): $libs uses the /proc/self/maps data from profile1,
-    # which may differ from the data from subsequent profiles, especially
-    # if they were run on different machines.  Use appropriate libs for
-    # each pc somehow.
-    $symbols = ExtractSymbols($libs, $pcs);
-  }
-
-  if (!defined($main::opt_thread)) {
-    FilterAndPrint($profile, $symbols, $libs);
-  }
-  if (defined($data->{threads})) {
-    foreach my $thread (sort { $a <=> $b } keys(%{$data->{threads}})) {
-      if (!defined($main::opt_thread) || $main::opt_thread == $thread) {
-        my $thread_profile = $data->{threads}{$thread};
-        FilterAndPrint($thread_profile, $symbols, $libs, $thread);
-      }
-    }
-  }
-
-  cleanup();
-  exit(0);
-}
-
-##### Entry Point #####
-
-Main();
-
-# Temporary code to detect if we're running on a Goobuntu system.
-# These systems don't have the right stuff installed for the special
-# Readline libraries to work, so as a temporary workaround, we default
-# to using the normal stdio code, rather than the fancier readline-based
-# code
-sub ReadlineMightFail {
-  if (-e '/lib/libtermcap.so.2') {
-    return 0;  # libtermcap exists, so readline should be okay
-  } else {
-    return 1;
-  }
-}
-
-sub RunGV {
-  my $fname = shift;
-  my $bg = shift;       # "" or " &" if we should run in background
-  if (!system(ShellEscape(@GV, "--version") . " >$dev_null 2>&1")) {
-    # Options using double dash are supported by this gv version.
-    # Also, turn on noantialias to better handle bug in gv for
-    # postscript files with large dimensions.
-    # TODO: Maybe we should not pass the --noantialias flag
-    # if the gv version is known to work properly without the flag.
-    system(ShellEscape(@GV, "--scale=$main::opt_scale", "--noantialias", $fname)
-           . $bg);
-  } else {
-    # Old gv version - only supports options that use single dash.
-    print STDERR ShellEscape(@GV, "-scale", $main::opt_scale) . "\n";
-    system(ShellEscape(@GV, "-scale", "$main::opt_scale", $fname) . $bg);
-  }
-}
-
-sub RunEvince {
-  my $fname = shift;
-  my $bg = shift;       # "" or " &" if we should run in background
-  system(ShellEscape(@EVINCE, $fname) . $bg);
-}
-
-sub RunWeb {
-  my $fname = shift;
-  print STDERR "Loading web page file:///$fname\n";
-
-  if (`uname` =~ /Darwin/) {
-    # OS X: open will use standard preference for SVG files.
-    system("/usr/bin/open", $fname);
-    return;
-  }
-
-  # Some kind of Unix; try generic symlinks, then specific browsers.
-  # (Stop once we find one.)
-  # Works best if the browser is already running.
-  my @alt = (
-    "/etc/alternatives/gnome-www-browser",
-    "/etc/alternatives/x-www-browser",
-    "google-chrome",
-    "firefox",
-  );
-  foreach my $b (@alt) {
-    if (system($b, $fname) == 0) {
-      return;
-    }
-  }
-
-  print STDERR "Could not load web browser.\n";
-}
-
-sub RunKcachegrind {
-  my $fname = shift;
-  my $bg = shift;       # "" or " &" if we should run in background
-  print STDERR "Starting '@KCACHEGRIND " . $fname . $bg . "'\n";
-  system(ShellEscape(@KCACHEGRIND, $fname) . $bg);
-}
-
-
-##### Interactive helper routines #####
-
-sub InteractiveMode {
-  $| = 1;  # Make output unbuffered for interactive mode
-  my ($orig_profile, $symbols, $libs, $total) = @_;
-
-  print STDERR "Welcome to pprof!  For help, type 'help'.\n";
-
-  # Use ReadLine if it's installed and input comes from a console.
-  if ( -t STDIN &&
-       !ReadlineMightFail() &&
-       defined(eval {require Term::ReadLine}) ) {
-    my $term = new Term::ReadLine 'pprof';
-    while ( defined ($_ = $term->readline('(pprof) '))) {
-      $term->addhistory($_) if /\S/;
-      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
-        last;    # exit when we get an interactive command to quit
-      }
-    }
-  } else {       # don't have readline
-    while (1) {
-      print STDERR "(pprof) ";
-      $_ = <STDIN>;
-      last if ! defined $_ ;
-      s/\r//g;         # turn windows-looking lines into unix-looking lines
-
-      # Save some flags that might be reset by InteractiveCommand()
-      my $save_opt_lines = $main::opt_lines;
-
-      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
-        last;    # exit when we get an interactive command to quit
-      }
-
-      # Restore flags
-      $main::opt_lines = $save_opt_lines;
-    }
-  }
-}
-
-# Takes two args: orig profile, and command to run.
-# Returns 1 if we should keep going, or 0 if we were asked to quit
-sub InteractiveCommand {
-  my($orig_profile, $symbols, $libs, $total, $command) = @_;
-  $_ = $command;                # just to make future m//'s easier
-  if (!defined($_)) {
-    print STDERR "\n";
-    return 0;
-  }
-  if (m/^\s*quit/) {
-    return 0;
-  }
-  if (m/^\s*help/) {
-    InteractiveHelpMessage();
-    return 1;
-  }
-  # Clear all the mode options -- mode is controlled by "$command"
-  $main::opt_text = 0;
-  $main::opt_callgrind = 0;
-  $main::opt_disasm = 0;
-  $main::opt_list = 0;
-  $main::opt_gv = 0;
-  $main::opt_evince = 0;
-  $main::opt_cum = 0;
-
-  if (m/^\s*(text|top)(\d*)\s*(.*)/) {
-    $main::opt_text = 1;
-
-    my $line_limit = ($2 ne "") ? int($2) : 10;
-
-    my $routine;
-    my $ignore;
-    ($routine, $ignore) = ParseInteractiveArgs($3);
-
-    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
-    my $reduced = ReduceProfile($symbols, $profile);
-
-    # Get derived profiles
-    my $flat = FlatProfile($reduced);
-    my $cumulative = CumulativeProfile($reduced);
-
-    PrintText($symbols, $flat, $cumulative, $line_limit);
-    return 1;
-  }
-  if (m/^\s*callgrind\s*([^ \n]*)/) {
-    $main::opt_callgrind = 1;
-
-    # Get derived profiles
-    my $calls = ExtractCalls($symbols, $orig_profile);
-    my $filename = $1;
-    if ( $1 eq '' ) {
-      $filename = TempName($main::next_tmpfile, "callgrind");
-    }
-    PrintCallgrind($calls, $filename);
-    if ( $1 eq '' ) {
-      RunKcachegrind($filename, " & ");
-      $main::next_tmpfile++;
-    }
-
-    return 1;
-  }
-  if (m/^\s*(web)?list\s*(.+)/) {
-    my $html = (defined($1) && ($1 eq "web"));
-    $main::opt_list = 1;
-
-    my $routine;
-    my $ignore;
-    ($routine, $ignore) = ParseInteractiveArgs($2);
-
-    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
-    my $reduced = ReduceProfile($symbols, $profile);
-
-    # Get derived profiles
-    my $flat = FlatProfile($reduced);
-    my $cumulative = CumulativeProfile($reduced);
-
-    PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
-    return 1;
-  }
-  if (m/^\s*disasm\s*(.+)/) {
-    $main::opt_disasm = 1;
-
-    my $routine;
-    my $ignore;
-    ($routine, $ignore) = ParseInteractiveArgs($1);
-
-    # Process current profile to account for various settings
-    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
-    my $reduced = ReduceProfile($symbols, $profile);
-
-    # Get derived profiles
-    my $flat = FlatProfile($reduced);
-    my $cumulative = CumulativeProfile($reduced);
-
-    PrintDisassembly($libs, $flat, $cumulative, $routine);
-    return 1;
-  }
-  if (m/^\s*(gv|web|evince)\s*(.*)/) {
-    $main::opt_gv = 0;
-    $main::opt_evince = 0;
-    $main::opt_web = 0;
-    if ($1 eq "gv") {
-      $main::opt_gv = 1;
-    } elsif ($1 eq "evince") {
-      $main::opt_evince = 1;
-    } elsif ($1 eq "web") {
-      $main::opt_web = 1;
-    }
-
-    my $focus;
-    my $ignore;
-    ($focus, $ignore) = ParseInteractiveArgs($2);
-
-    # Process current profile to account for various settings
-    my $profile = ProcessProfile($total, $orig_profile, $symbols,
-                                 $focus, $ignore);
-    my $reduced = ReduceProfile($symbols, $profile);
-
-    # Get derived profiles
-    my $flat = FlatProfile($reduced);
-    my $cumulative = CumulativeProfile($reduced);
-
-    if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
-      if ($main::opt_gv) {
-        RunGV(TempName($main::next_tmpfile, "ps"), " &");
-      } elsif ($main::opt_evince) {
-        RunEvince(TempName($main::next_tmpfile, "pdf"), " &");
-      } elsif ($main::opt_web) {
-        RunWeb(TempName($main::next_tmpfile, "svg"));
-      }
-      $main::next_tmpfile++;
-    }
-    return 1;
-  }
-  if (m/^\s*$/) {
-    return 1;
-  }
-  print STDERR "Unknown command: try 'help'.\n";
-  return 1;
-}
-
-
-sub ProcessProfile {
-  my $total_count = shift;
-  my $orig_profile = shift;
-  my $symbols = shift;
-  my $focus = shift;
-  my $ignore = shift;
-
-  # Process current profile to account for various settings
-  my $profile = $orig_profile;
-  printf("Total: %s %s\n", Unparse($total_count), Units());
-  if ($focus ne '') {
-    $profile = FocusProfile($symbols, $profile, $focus);
-    my $focus_count = TotalProfile($profile);
-    printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n",
-           $focus,
-           Unparse($focus_count), Units(),
-           Unparse($total_count), ($focus_count*100.0) / $total_count);
-  }
-  if ($ignore ne '') {
-    $profile = IgnoreProfile($symbols, $profile, $ignore);
-    my $ignore_count = TotalProfile($profile);
-    printf("After ignoring '%s': %s %s of %s (%0.1f%%)\n",
-           $ignore,
-           Unparse($ignore_count), Units(),
-           Unparse($total_count),
-           ($ignore_count*100.0) / $total_count);
-  }
-
-  return $profile;
-}
-
-sub InteractiveHelpMessage {
-  print STDERR <<ENDOFHELP;
-Interactive pprof mode
-
-Commands:
-  gv
-  gv [focus] [-ignore1] [-ignore2]
-      Show graphical hierarchical display of current profile.  Without
-      any arguments, shows all samples in the profile.  With the optional
-      "focus" argument, restricts the samples shown to just those where
-      the "focus" regular expression matches a routine name on the stack
-      trace.
-
-  web
-  web [focus] [-ignore1] [-ignore2]
-      Like GV, but displays profile in your web browser instead of using
-      Ghostview. Works best if your web browser is already running.
-      To change the browser that gets used:
-      On Linux, set the /etc/alternatives/gnome-www-browser symlink.
-      On OS X, change the Finder association for SVG files.
-
-  list [routine_regexp] [-ignore1] [-ignore2]
-      Show source listing of routines whose names match "routine_regexp"
-
-  weblist [routine_regexp] [-ignore1] [-ignore2]
-     Displays a source listing of routines whose names match "routine_regexp"
-     in a web browser.  You can click on source lines to view the
-     corresponding disassembly.
-
-  top [--cum] [-ignore1] [-ignore2]
-  top20 [--cum] [-ignore1] [-ignore2]
-  top37 [--cum] [-ignore1] [-ignore2]
-      Show top lines ordered by flat profile count, or cumulative count
-      if --cum is specified.  If a number is present after 'top', the
-      top K routines will be shown (defaults to showing the top 10)
-
-  disasm [routine_regexp] [-ignore1] [-ignore2]
-      Show disassembly of routines whose names match "routine_regexp",
-      annotated with sample counts.
-
-  callgrind
-  callgrind [filename]
-      Generates callgrind file. If no filename is given, kcachegrind is called.
-
-  help - This listing
-  quit or ^D - End pprof
-
-For commands that accept optional -ignore tags, samples where any routine in
-the stack trace matches the regular expression in any of the -ignore
-parameters will be ignored.
-
-Further pprof details are available at this location (or one similar):
-
- /usr/doc/gperftools-$PPROF_VERSION/cpu_profiler.html
- /usr/doc/gperftools-$PPROF_VERSION/heap_profiler.html
-
-ENDOFHELP
-}
-sub ParseInteractiveArgs {
-  my $args = shift;
-  my $focus = "";
-  my $ignore = "";
-  my @x = split(/ +/, $args);
-  foreach $a (@x) {
-    if ($a =~ m/^(--|-)lines$/) {
-      $main::opt_lines = 1;
-    } elsif ($a =~ m/^(--|-)cum$/) {
-      $main::opt_cum = 1;
-    } elsif ($a =~ m/^-(.*)/) {
-      $ignore .= (($ignore ne "") ? "|" : "" ) . $1;
-    } else {
-      $focus .= (($focus ne "") ? "|" : "" ) . $a;
-    }
-  }
-  if ($ignore ne "") {
-    print STDERR "Ignoring samples in call stacks that match '$ignore'\n";
-  }
-  return ($focus, $ignore);
-}
-
-##### Output code #####
-
-sub TempName {
-  my $fnum = shift;
-  my $ext = shift;
-  my $file = "$main::tmpfile_ps.$fnum.$ext";
-  $main::tempnames{$file} = 1;
-  return $file;
-}
-
-# Print profile data in packed binary format (64-bit) to standard out
-sub PrintProfileData {
-  my $profile = shift;
-
-  # print header (64-bit style)
-  # (zero) (header-size) (version) (sample-period) (zero)
-  print pack('L*', 0, 0, 3, 0, 0, 0, 1, 0, 0, 0);
-
-  foreach my $k (keys(%{$profile})) {
-    my $count = $profile->{$k};
-    my @addrs = split(/\n/, $k);
-    if ($#addrs >= 0) {
-      my $depth = $#addrs + 1;
-      # int(foo / 2**32) is the only reliable way to get rid of bottom
-      # 32 bits on both 32- and 64-bit systems.
-      print pack('L*', $count & 0xFFFFFFFF, int($count / 2**32));
-      print pack('L*', $depth & 0xFFFFFFFF, int($depth / 2**32));
-
-      foreach my $full_addr (@addrs) {
-        my $addr = $full_addr;
-        $addr =~ s/0x0*//;  # strip off leading 0x, zeroes
-        if (length($addr) > 16) {
-          print STDERR "Invalid address in profile: $full_addr\n";
-          next;
-        }
-        my $low_addr = substr($addr, -8);       # get last 8 hex chars
-        my $high_addr = substr($addr, -16, 8);  # get up to 8 more hex chars
-        print pack('L*', hex('0x' . $low_addr), hex('0x' . $high_addr));
-      }
-    }
-  }
-}
-
-# Print symbols and profile data
-sub PrintSymbolizedProfile {
-  my $symbols = shift;
-  my $profile = shift;
-  my $prog = shift;
-
-  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
-  my $symbol_marker = $&;
-
-  print '--- ', $symbol_marker, "\n";
-  if (defined($prog)) {
-    print 'binary=', $prog, "\n";
-  }
-  while (my ($pc, $name) = each(%{$symbols})) {
-    my $sep = ' ';
-    print '0x', $pc;
-    # We have a list of function names, which include the inlined
-    # calls.  They are separated (and terminated) by --, which is
-    # illegal in function names.
-    for (my $j = 2; $j <= $#{$name}; $j += 3) {
-      print $sep, $name->[$j];
-      $sep = '--';
-    }
-    print "\n";
-  }
-  print '---', "\n";
-
-  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
-  my $profile_marker = $&;
-  print '--- ', $profile_marker, "\n";
-  if (defined($main::collected_profile)) {
-    # if used with remote fetch, simply dump the collected profile to output.
-    open(SRC, "<$main::collected_profile");
-    while (<SRC>) {
-      print $_;
-    }
-    close(SRC);
-  } else {
-    # dump a cpu-format profile to standard out
-    PrintProfileData($profile);
-  }
-}
-
-# Print text output
-sub PrintText {
-  my $symbols = shift;
-  my $flat = shift;
-  my $cumulative = shift;
-  my $line_limit = shift;
-
-  my $total = TotalProfile($flat);
-
-  # Which profile to sort by?
-  my $s = $main::opt_cum ? $cumulative : $flat;
-
-  my $running_sum = 0;
-  my $lines = 0;
-  foreach my $k (sort { GetEntry($s, $b) <=> GetEntry($s, $a) || $a cmp $b }
-                 keys(%{$cumulative})) {
-    my $f = GetEntry($flat, $k);
-    my $c = GetEntry($cumulative, $k);
-    $running_sum += $f;
-
-    my $sym = $k;
-    if (exists($symbols->{$k})) {
-      $sym = $symbols->{$k}->[0] . " " . $symbols->{$k}->[1];
-      if ($main::opt_addresses) {
-        $sym = $k . " " . $sym;
-      }
-    }
-
-    if ($f != 0 || $c != 0) {
-      printf("%8s %6s %6s %8s %6s %s\n",
-             Unparse($f),
-             Percent($f, $total),
-             Percent($running_sum, $total),
-             Unparse($c),
-             Percent($c, $total),
-             $sym);
-    }
-    $lines++;
-    last if ($line_limit >= 0 && $lines >= $line_limit);
-  }
-}
-
-# Callgrind format has a compression for repeated function and file
-# names.  You show the name the first time, and just use its number
-# subsequently.  This can cut down the file to about a third or a
-# quarter of its uncompressed size.  $key and $val are the key/value
-# pair that would normally be printed by callgrind; $map is a map from
-# value to number.
-sub CompressedCGName {
-  my($key, $val, $map) = @_;
-  my $idx = $map->{$val};
-  # For very short keys, providing an index hurts rather than helps.
-  if (length($val) <= 3) {
-    return "$key=$val\n";
-  } elsif (defined($idx)) {
-    return "$key=($idx)\n";
-  } else {
-    # scalar(keys $map) gives the number of items in the map.
-    $idx = scalar(keys(%{$map})) + 1;
-    $map->{$val} = $idx;
-    return "$key=($idx) $val\n";
-  }
-}
-
-# Print the call graph in a way that's suiteable for callgrind.
-sub PrintCallgrind {
-  my $calls = shift;
-  my $filename;
-  my %filename_to_index_map;
-  my %fnname_to_index_map;
-
-  if ($main::opt_interactive) {
-    $filename = shift;
-    print STDERR "Writing callgrind file to '$filename'.\n"
-  } else {
-    $filename = "&STDOUT";
-  }
-  open(CG, ">$filename");
-  printf CG ("events: Hits\n\n");
-  foreach my $call ( map { $_->[0] }
-                     sort { $a->[1] cmp $b ->[1] ||
-                            $a->[2] <=> $b->[2] }
-                     map { /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
-                           [$_, $1, $2] }
-                     keys %$calls ) {
-    my $count = int($calls->{$call});
-    $call =~ /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
-    my ( $caller_file, $caller_line, $caller_function,
-         $callee_file, $callee_line, $callee_function ) =
-       ( $1, $2, $3, $5, $6, $7 );
-
-    # TODO(csilvers): for better compression, collect all the
-    # caller/callee_files and functions first, before printing
-    # anything, and only compress those referenced more than once.
-    printf CG CompressedCGName("fl", $caller_file, \%filename_to_index_map);
-    printf CG CompressedCGName("fn", $caller_function, \%fnname_to_index_map);
-    if (defined $6) {
-      printf CG CompressedCGName("cfl", $callee_file, \%filename_to_index_map);
-      printf CG CompressedCGName("cfn", $callee_function, \%fnname_to_index_map);
-      printf CG ("calls=$count $callee_line\n");
-    }
-    printf CG ("$caller_line $count\n\n");
-  }
-}
-
-# Print disassembly for all all routines that match $main::opt_disasm
-sub PrintDisassembly {
-  my $libs = shift;
-  my $flat = shift;
-  my $cumulative = shift;
-  my $disasm_opts = shift;
-
-  my $total = TotalProfile($flat);
-
-  foreach my $lib (@{$libs}) {
-    my $symbol_table = GetProcedureBoundaries($lib->[0], $disasm_opts);
-    my $offset = AddressSub($lib->[1], $lib->[3]);
-    foreach my $routine (sort ByName keys(%{$symbol_table})) {
-      my $start_addr = $symbol_table->{$routine}->[0];
-      my $end_addr = $symbol_table->{$routine}->[1];
-      # See if there are any samples in this routine
-      my $length = hex(AddressSub($end_addr, $start_addr));
-      my $addr = AddressAdd($start_addr, $offset);
-      for (my $i = 0; $i < $length; $i++) {
-        if (defined($cumulative->{$addr})) {
-          PrintDisassembledFunction($lib->[0], $offset,
-                                    $routine, $flat, $cumulative,
-                                    $start_addr, $end_addr, $total);
-          last;
-        }
-        $addr = AddressInc($addr);
-      }
-    }
-  }
-}
-
-# Return reference to array of tuples of the form:
-#       [start_address, filename, linenumber, instruction, limit_address]
-# E.g.,
-#       ["0x806c43d", "/foo/bar.cc", 131, "ret", "0x806c440"]
-sub Disassemble {
-  my $prog = shift;
-  my $offset = shift;
-  my $start_addr = shift;
-  my $end_addr = shift;
-
-  my $objdump = $obj_tool_map{"objdump"};
-  my $cmd = ShellEscape($objdump, "-C", "-d", "-l", "--no-show-raw-insn",
-                        "--start-address=0x$start_addr",
-                        "--stop-address=0x$end_addr", $prog);
-  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
-  my @result = ();
-  my $filename = "";
-  my $linenumber = -1;
-  my $last = ["", "", "", ""];
-  while (<OBJDUMP>) {
-    s/\r//g;         # turn windows-looking lines into unix-looking lines
-    chop;
-    if (m|\s*([^:\s]+):(\d+)\s*$|) {
-      # Location line of the form:
-      #   <filename>:<linenumber>
-      $filename = $1;
-      $linenumber = $2;
-    } elsif (m/^ +([0-9a-f]+):\s*(.*)/) {
-      # Disassembly line -- zero-extend address to full length
-      my $addr = HexExtend($1);
-      my $k = AddressAdd($addr, $offset);
-      $last->[4] = $k;   # Store ending address for previous instruction
-      $last = [$k, $filename, $linenumber, $2, $end_addr];
-      push(@result, $last);
-    }
-  }
-  close(OBJDUMP);
-  return @result;
-}
-
-# The input file should contain lines of the form /proc/maps-like
-# output (same format as expected from the profiles) or that looks
-# like hex addresses (like "0xDEADBEEF").  We will parse all
-# /proc/maps output, and for all the hex addresses, we will output
-# "short" symbol names, one per line, in the same order as the input.
-sub PrintSymbols {
-  my $maps_and_symbols_file = shift;
-
-  # ParseLibraries expects pcs to be in a set.  Fine by us...
-  my @pclist = ();   # pcs in sorted order
-  my $pcs = {};
-  my $map = "";
-  foreach my $line (<$maps_and_symbols_file>) {
-    $line =~ s/\r//g;    # turn windows-looking lines into unix-looking lines
-    if ($line =~ /\b(0x[0-9a-f]+)\b/i) {
-      push(@pclist, HexExtend($1));
-      $pcs->{$pclist[-1]} = 1;
-    } else {
-      $map .= $line;
-    }
-  }
-
-  my $libs = ParseLibraries($main::prog, $map, $pcs);
-  my $symbols = ExtractSymbols($libs, $pcs);
-
-  foreach my $pc (@pclist) {
-    # ->[0] is the shortname, ->[2] is the full name
-    print(($symbols->{$pc}->[0] || "??") . "\n");
-  }
-}
-
-
-# For sorting functions by name
-sub ByName {
-  return ShortFunctionName($a) cmp ShortFunctionName($b);
-}
-
-# Print source-listing for all all routines that match $list_opts
-sub PrintListing {
-  my $total = shift;
-  my $libs = shift;
-  my $flat = shift;
-  my $cumulative = shift;
-  my $list_opts = shift;
-  my $html = shift;
-
-  my $output = \*STDOUT;
-  my $fname = "";
-
-  if ($html) {
-    # Arrange to write the output to a temporary file
-    $fname = TempName($main::next_tmpfile, "html");
-    $main::next_tmpfile++;
-    if (!open(TEMP, ">$fname")) {
-      print STDERR "$fname: $!\n";
-      return;
-    }
-    $output = \*TEMP;
-    print $output HtmlListingHeader();
-    printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
-                    $main::prog, Unparse($total), Units());
-  }
-
-  my $listed = 0;
-  foreach my $lib (@{$libs}) {
-    my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
-    my $offset = AddressSub($lib->[1], $lib->[3]);
-    foreach my $routine (sort ByName keys(%{$symbol_table})) {
-      # Print if there are any samples in this routine
-      my $start_addr = $symbol_table->{$routine}->[0];
-      my $end_addr = $symbol_table->{$routine}->[1];
-      my $length = hex(AddressSub($end_addr, $start_addr));
-      my $addr = AddressAdd($start_addr, $offset);
-      for (my $i = 0; $i < $length; $i++) {
-        if (defined($cumulative->{$addr})) {
-          $listed += PrintSource(
-            $lib->[0], $offset,
-            $routine, $flat, $cumulative,
-            $start_addr, $end_addr,
-            $html,
-            $output);
-          last;
-        }
-        $addr = AddressInc($addr);
-      }
-    }
-  }
-
-  if ($html) {
-    if ($listed > 0) {
-      print $output HtmlListingFooter();
-      close($output);
-      RunWeb($fname);
-    } else {
-      close($output);
-      unlink($fname);
-    }
-  }
-}
-
-sub HtmlListingHeader {
-  return <<'EOF';
-<DOCTYPE html>
-<html>
-<head>
-<title>Pprof listing</title>
-<style type="text/css">
-body {
-  font-family: sans-serif;
-}
-h1 {
-  font-size: 1.5em;
-  margin-bottom: 4px;
-}
-.legend {
-  font-size: 1.25em;
-}
-.line {
-  color: #aaaaaa;
-}
-.nop {
-  color: #aaaaaa;
-}
-.unimportant {
-  color: #cccccc;
-}
-.disasmloc {
-  color: #000000;
-}
-.deadsrc {
-  cursor: pointer;
-}
-.deadsrc:hover {
-  background-color: #eeeeee;
-}
-.livesrc {
-  color: #0000ff;
-  cursor: pointer;
-}
-.livesrc:hover {
-  background-color: #eeeeee;
-}
-.asm {
-  color: #008800;
-  display: none;
-}
-</style>
-<script type="text/javascript">
-function pprof_toggle_asm(e) {
-  var target;
-  if (!e) e = window.event;
-  if (e.target) target = e.target;
-  else if (e.srcElement) target = e.srcElement;
-
-  if (target) {
-    var asm = target.nextSibling;
-    if (asm && asm.className == "asm") {
-      asm.style.display = (asm.style.display == "block" ? "" : "block");
-      e.preventDefault();
-      return false;
-    }
-  }
-}
-</script>
-</head>
-<body>
-EOF
-}
-
-sub HtmlListingFooter {
-  return <<'EOF';
-</body>
-</html>
-EOF
-}
-
-sub HtmlEscape {
-  my $text = shift;
-  $text =~ s/&/&amp;/g;
-  $text =~ s/</&lt;/g;
-  $text =~ s/>/&gt;/g;
-  return $text;
-}
-
-# Returns the indentation of the line, if it has any non-whitespace
-# characters.  Otherwise, returns -1.
-sub Indentation {
-  my $line = shift;
-  if (m/^(\s*)\S/) {
-    return length($1);
-  } else {
-    return -1;
-  }
-}
-
-# If the symbol table contains inlining info, Disassemble() may tag an
-# instruction with a location inside an inlined function.  But for
-# source listings, we prefer to use the location in the function we
-# are listing.  So use MapToSymbols() to fetch full location
-# information for each instruction and then pick out the first
-# location from a location list (location list contains callers before
-# callees in case of inlining).
-#
-# After this routine has run, each entry in $instructions contains:
-#   [0] start address
-#   [1] filename for function we are listing
-#   [2] line number for function we are listing
-#   [3] disassembly
-#   [4] limit address
-#   [5] most specific filename (may be different from [1] due to inlining)
-#   [6] most specific line number (may be different from [2] due to inlining)
-sub GetTopLevelLineNumbers {
-  my ($lib, $offset, $instructions) = @_;
-  my $pcs = [];
-  for (my $i = 0; $i <= $#{$instructions}; $i++) {
-    push(@{$pcs}, $instructions->[$i]->[0]);
-  }
-  my $symbols = {};
-  MapToSymbols($lib, $offset, $pcs, $symbols);
-  for (my $i = 0; $i <= $#{$instructions}; $i++) {
-    my $e = $instructions->[$i];
-    push(@{$e}, $e->[1]);
-    push(@{$e}, $e->[2]);
-    my $addr = $e->[0];
-    my $sym = $symbols->{$addr};
-    if (defined($sym)) {
-      if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\d+)$/) {
-        $e->[1] = $1;  # File name
-        $e->[2] = $2;  # Line number
-      }
-    }
-  }
-}
-
-# Print source-listing for one routine
-sub PrintSource {
-  my $prog = shift;
-  my $offset = shift;
-  my $routine = shift;
-  my $flat = shift;
-  my $cumulative = shift;
-  my $start_addr = shift;
-  my $end_addr = shift;
-  my $html = shift;
-  my $output = shift;
-
-  # Disassemble all instructions (just to get line numbers)
-  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
-  GetTopLevelLineNumbers($prog, $offset, \@instructions);
-
-  # Hack 1: assume that the first source file encountered in the
-  # disassembly contains the routine
-  my $filename = undef;
-  for (my $i = 0; $i <= $#instructions; $i++) {
-    if ($instructions[$i]->[2] >= 0) {
-      $filename = $instructions[$i]->[1];
-      last;
-    }
-  }
-  if (!defined($filename)) {
-    print STDERR "no filename found in $routine\n";
-    return 0;
-  }
-
-  # Hack 2: assume that the largest line number from $filename is the
-  # end of the procedure.  This is typically safe since if P1 contains
-  # an inlined call to P2, then P2 usually occurs earlier in the
-  # source file.  If this does not work, we might have to compute a
-  # density profile or just print all regions we find.
-  my $lastline = 0;
-  for (my $i = 0; $i <= $#instructions; $i++) {
-    my $f = $instructions[$i]->[1];
-    my $l = $instructions[$i]->[2];
-    if (($f eq $filename) && ($l > $lastline)) {
-      $lastline = $l;
-    }
-  }
-
-  # Hack 3: assume the first source location from "filename" is the start of
-  # the source code.
-  my $firstline = 1;
-  for (my $i = 0; $i <= $#instructions; $i++) {
-    if ($instructions[$i]->[1] eq $filename) {
-      $firstline = $instructions[$i]->[2];
-      last;
-    }
-  }
-
-  # Hack 4: Extend last line forward until its indentation is less than
-  # the indentation we saw on $firstline
-  my $oldlastline = $lastline;
-  {
-    if (!open(FILE, "<$filename")) {
-      print STDERR "$filename: $!\n";
-      return 0;
-    }
-    my $l = 0;
-    my $first_indentation = -1;
-    while (<FILE>) {
-      s/\r//g;         # turn windows-looking lines into unix-looking lines
-      $l++;
-      my $indent = Indentation($_);
-      if ($l >= $firstline) {
-        if ($first_indentation < 0 && $indent >= 0) {
-          $first_indentation = $indent;
-          last if ($first_indentation == 0);
-        }
-      }
-      if ($l >= $lastline && $indent >= 0) {
-        if ($indent >= $first_indentation) {
-          $lastline = $l+1;
-        } else {
-          last;
-        }
-      }
-    }
-    close(FILE);
-  }
-
-  # Assign all samples to the range $firstline,$lastline,
-  # Hack 4: If an instruction does not occur in the range, its samples
-  # are moved to the next instruction that occurs in the range.
-  my $samples1 = {};        # Map from line number to flat count
-  my $samples2 = {};        # Map from line number to cumulative count
-  my $running1 = 0;         # Unassigned flat counts
-  my $running2 = 0;         # Unassigned cumulative counts
-  my $total1 = 0;           # Total flat counts
-  my $total2 = 0;           # Total cumulative counts
-  my %disasm = ();          # Map from line number to disassembly
-  my $running_disasm = "";  # Unassigned disassembly
-  my $skip_marker = "---\n";
-  if ($html) {
-    $skip_marker = "";
-    for (my $l = $firstline; $l <= $lastline; $l++) {
-      $disasm{$l} = "";
-    }
-  }
-  my $last_dis_filename = '';
-  my $last_dis_linenum = -1;
-  my $last_touched_line = -1;  # To detect gaps in disassembly for a line
-  foreach my $e (@instructions) {
-    # Add up counts for all address that fall inside this instruction
-    my $c1 = 0;
-    my $c2 = 0;
-    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
-      $c1 += GetEntry($flat, $a);
-      $c2 += GetEntry($cumulative, $a);
-    }
-
-    if ($html) {
-      my $dis = sprintf("      %6s %6s \t\t%8s: %s ",
-                        HtmlPrintNumber($c1),
-                        HtmlPrintNumber($c2),
-                        UnparseAddress($offset, $e->[0]),
-                        CleanDisassembly($e->[3]));
-
-      # Append the most specific source line associated with this instruction
-      if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) };
-      $dis = HtmlEscape($dis);
-      my $f = $e->[5];
-      my $l = $e->[6];
-      if ($f ne $last_dis_filename) {
-        $dis .= sprintf("<span class=disasmloc>%s:%d</span>",
-                        HtmlEscape(CleanFileName($f)), $l);
-      } elsif ($l ne $last_dis_linenum) {
-        # De-emphasize the unchanged file name portion
-        $dis .= sprintf("<span class=unimportant>%s</span>" .
-                        "<span class=disasmloc>:%d</span>",
-                        HtmlEscape(CleanFileName($f)), $l);
-      } else {
-        # De-emphasize the entire location
-        $dis .= sprintf("<span class=unimportant>%s:%d</span>",
-                        HtmlEscape(CleanFileName($f)), $l);
-      }
-      $last_dis_filename = $f;
-      $last_dis_linenum = $l;
-      $running_disasm .= $dis;
-      $running_disasm .= "\n";
-    }
-
-    $running1 += $c1;
-    $running2 += $c2;
-    $total1 += $c1;
-    $total2 += $c2;
-    my $file = $e->[1];
-    my $line = $e->[2];
-    if (($file eq $filename) &&
-        ($line >= $firstline) &&
-        ($line <= $lastline)) {
-      # Assign all accumulated samples to this line
-      AddEntry($samples1, $line, $running1);
-      AddEntry($samples2, $line, $running2);
-      $running1 = 0;
-      $running2 = 0;
-      if ($html) {
-        if ($line != $last_touched_line && $disasm{$line} ne '') {
-          $disasm{$line} .= "\n";
-        }
-        $disasm{$line} .= $running_disasm;
-        $running_disasm = '';
-        $last_touched_line = $line;
-      }
-    }
-  }
-
-  # Assign any leftover samples to $lastline
-  AddEntry($samples1, $lastline, $running1);
-  AddEntry($samples2, $lastline, $running2);
-  if ($html) {
-    if ($lastline != $last_touched_line && $disasm{$lastline} ne '') {
-      $disasm{$lastline} .= "\n";
-    }
-    $disasm{$lastline} .= $running_disasm;
-  }
-
-  if ($html) {
-    printf $output (
-      "<h1>%s</h1>%s\n<pre onClick=\"pprof_toggle_asm()\">\n" .
-      "Total:%6s %6s (flat / cumulative %s)\n",
-      HtmlEscape(ShortFunctionName($routine)),
-      HtmlEscape(CleanFileName($filename)),
-      Unparse($total1),
-      Unparse($total2),
-      Units());
-  } else {
-    printf $output (
-      "ROUTINE ====================== %s in %s\n" .
-      "%6s %6s Total %s (flat / cumulative)\n",
-      ShortFunctionName($routine),
-      CleanFileName($filename),
-      Unparse($total1),
-      Unparse($total2),
-      Units());
-  }
-  if (!open(FILE, "<$filename")) {
-    print STDERR "$filename: $!\n";
-    return 0;
-  }
-  my $l = 0;
-  while (<FILE>) {
-    s/\r//g;         # turn windows-looking lines into unix-looking lines
-    $l++;
-    if ($l >= $firstline - 5 &&
-        (($l <= $oldlastline + 5) || ($l <= $lastline))) {
-      chop;
-      my $text = $_;
-      if ($l == $firstline) { print $output $skip_marker; }
-      my $n1 = GetEntry($samples1, $l);
-      my $n2 = GetEntry($samples2, $l);
-      if ($html) {
-        # Emit a span that has one of the following classes:
-        #    livesrc -- has samples
-        #    deadsrc -- has disassembly, but with no samples
-        #    nop     -- has no matching disasembly
-        # Also emit an optional span containing disassembly.
-        my $dis = $disasm{$l};
-        my $asm = "";
-        if (defined($dis) && $dis ne '') {
-          $asm = "<span class=\"asm\">" . $dis . "</span>";
-        }
-        my $source_class = (($n1 + $n2 > 0)
-                            ? "livesrc"
-                            : (($asm ne "") ? "deadsrc" : "nop"));
-        printf $output (
-          "<span class=\"line\">%5d</span> " .
-          "<span class=\"%s\">%6s %6s %s</span>%s\n",
-          $l, $source_class,
-          HtmlPrintNumber($n1),
-          HtmlPrintNumber($n2),
-          HtmlEscape($text),
-          $asm);
-      } else {
-        printf $output(
-          "%6s %6s %4d: %s\n",
-          UnparseAlt($n1),
-          UnparseAlt($n2),
-          $l,
-          $text);
-      }
-      if ($l == $lastline)  { print $output $skip_marker; }
-    };
-  }
-  close(FILE);
-  if ($html) {
-    print $output "</pre>\n";
-  }
-  return 1;
-}
-
-# Return the source line for the specified file/linenumber.
-# Returns undef if not found.
-sub SourceLine {
-  my $file = shift;
-  my $line = shift;
-
-  # Look in cache
-  if (!defined($main::source_cache{$file})) {
-    if (100 < scalar keys(%main::source_cache)) {
-      # Clear the cache when it gets too big
-      $main::source_cache = ();
-    }
-
-    # Read all lines from the file
-    if (!open(FILE, "<$file")) {
-      print STDERR "$file: $!\n";
-      $main::source_cache{$file} = [];  # Cache the negative result
-      return undef;
-    }
-    my $lines = [];
-    push(@{$lines}, "");        # So we can use 1-based line numbers as indices
-    while (<FILE>) {
-      push(@{$lines}, $_);
-    }
-    close(FILE);
-
-    # Save the lines in the cache
-    $main::source_cache{$file} = $lines;
-  }
-
-  my $lines = $main::source_cache{$file};
-  if (($line < 0) || ($line > $#{$lines})) {
-    return undef;
-  } else {
-    return $lines->[$line];
-  }
-}
-
-# Print disassembly for one routine with interspersed source if available
-sub PrintDisassembledFunction {
-  my $prog = shift;
-  my $offset = shift;
-  my $routine = shift;
-  my $flat = shift;
-  my $cumulative = shift;
-  my $start_addr = shift;
-  my $end_addr = shift;
-  my $total = shift;
-
-  # Disassemble all instructions
-  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
-
-  # Make array of counts per instruction
-  my @flat_count = ();
-  my @cum_count = ();
-  my $flat_total = 0;
-  my $cum_total = 0;
-  foreach my $e (@instructions) {
-    # Add up counts for all address that fall inside this instruction
-    my $c1 = 0;
-    my $c2 = 0;
-    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
-      $c1 += GetEntry($flat, $a);
-      $c2 += GetEntry($cumulative, $a);
-    }
-    push(@flat_count, $c1);
-    push(@cum_count, $c2);
-    $flat_total += $c1;
-    $cum_total += $c2;
-  }
-
-  # Print header with total counts
-  printf("ROUTINE ====================== %s\n" .
-         "%6s %6s %s (flat, cumulative) %.1f%% of total\n",
-         ShortFunctionName($routine),
-         Unparse($flat_total),
-         Unparse($cum_total),
-         Units(),
-         ($cum_total * 100.0) / $total);
-
-  # Process instructions in order
-  my $current_file = "";
-  for (my $i = 0; $i <= $#instructions; ) {
-    my $e = $instructions[$i];
-
-    # Print the new file name whenever we switch files
-    if ($e->[1] ne $current_file) {
-      $current_file = $e->[1];
-      my $fname = $current_file;
-      $fname =~ s|^\./||;   # Trim leading "./"
-
-      # Shorten long file names
-      if (length($fname) >= 58) {
-        $fname = "..." . substr($fname, -55);
-      }
-      printf("-------------------- %s\n", $fname);
-    }
-
-    # TODO: Compute range of lines to print together to deal with
-    # small reorderings.
-    my $first_line = $e->[2];
-    my $last_line = $first_line;
-    my %flat_sum = ();
-    my %cum_sum = ();
-    for (my $l = $first_line; $l <= $last_line; $l++) {
-      $flat_sum{$l} = 0;
-      $cum_sum{$l} = 0;
-    }
-
-    # Find run of instructions for this range of source lines
-    my $first_inst = $i;
-    while (($i <= $#instructions) &&
-           ($instructions[$i]->[2] >= $first_line) &&
-           ($instructions[$i]->[2] <= $last_line)) {
-      $e = $instructions[$i];
-      $flat_sum{$e->[2]} += $flat_count[$i];
-      $cum_sum{$e->[2]} += $cum_count[$i];
-      $i++;
-    }
-    my $last_inst = $i - 1;
-
-    # Print source lines
-    for (my $l = $first_line; $l <= $last_line; $l++) {
-      my $line = SourceLine($current_file, $l);
-      if (!defined($line)) {
-        $line = "?\n";
-        next;
-      } else {
-        $line =~ s/^\s+//;
-      }
-      printf("%6s %6s %5d: %s",
-             UnparseAlt($flat_sum{$l}),
-             UnparseAlt($cum_sum{$l}),
-             $l,
-             $line);
-    }
-
-    # Print disassembly
-    for (my $x = $first_inst; $x <= $last_inst; $x++) {
-      my $e = $instructions[$x];
-      printf("%6s %6s    %8s: %6s\n",
-             UnparseAlt($flat_count[$x]),
-             UnparseAlt($cum_count[$x]),
-             UnparseAddress($offset, $e->[0]),
-             CleanDisassembly($e->[3]));
-    }
-  }
-}
-
-# Print DOT graph
-sub PrintDot {
-  my $prog = shift;
-  my $symbols = shift;
-  my $raw = shift;
-  my $flat = shift;
-  my $cumulative = shift;
-  my $overall_total = shift;
-
-  # Get total
-  my $local_total = TotalProfile($flat);
-  my $nodelimit = int($main::opt_nodefraction * $local_total);
-  my $edgelimit = int($main::opt_edgefraction * $local_total);
-  my $nodecount = $main::opt_nodecount;
-
-  # Find nodes to include
-  my @list = (sort { abs(GetEntry($cumulative, $b)) <=>
-                     abs(GetEntry($cumulative, $a))
-                     || $a cmp $b }
-              keys(%{$cumulative}));
-  my $last = $nodecount - 1;
-  if ($last > $#list) {
-    $last = $#list;
-  }
-  while (($last >= 0) &&
-         (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) {
-    $last--;
-  }
-  if ($last < 0) {
-    print STDERR "No nodes to print\n";
-    return 0;
-  }
-
-  if ($nodelimit > 0 || $edgelimit > 0) {
-    printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n",
-                   Unparse($nodelimit), Units(),
-                   Unparse($edgelimit), Units());
-  }
-
-  # Open DOT output file
-  my $output;
-  my $escaped_dot = ShellEscape(@DOT);
-  my $escaped_ps2pdf = ShellEscape(@PS2PDF);
-  if ($main::opt_gv) {
-    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "ps"));
-    $output = "| $escaped_dot -Tps2 >$escaped_outfile";
-  } elsif ($main::opt_evince) {
-    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "pdf"));
-    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - $escaped_outfile";
-  } elsif ($main::opt_ps) {
-    $output = "| $escaped_dot -Tps2";
-  } elsif ($main::opt_pdf) {
-    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - -";
-  } elsif ($main::opt_web || $main::opt_svg) {
-    # We need to post-process the SVG, so write to a temporary file always.
-    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "svg"));
-    $output = "| $escaped_dot -Tsvg >$escaped_outfile";
-  } elsif ($main::opt_gif) {
-    $output = "| $escaped_dot -Tgif";
-  } else {
-    $output = ">&STDOUT";
-  }
-  open(DOT, $output) || error("$output: $!\n");
-
-  # Title
-  printf DOT ("digraph \"%s; %s %s\" {\n",
-              $prog,
-              Unparse($overall_total),
-              Units());
-  if ($main::opt_pdf) {
-    # The output is more printable if we set the page size for dot.
-    printf DOT ("size=\"8,11\"\n");
-  }
-  printf DOT ("node [width=0.375,height=0.25];\n");
-
-  # Print legend
-  printf DOT ("Legend [shape=box,fontsize=24,shape=plaintext," .
-              "label=\"%s\\l%s\\l%s\\l%s\\l%s\\l\"];\n",
-              $prog,
-              sprintf("Total %s: %s", Units(), Unparse($overall_total)),
-              sprintf("Focusing on: %s", Unparse($local_total)),
-              sprintf("Dropped nodes with <= %s abs(%s)",
-                      Unparse($nodelimit), Units()),
-              sprintf("Dropped edges with <= %s %s",
-                      Unparse($edgelimit), Units())
-              );
-
-  # Print nodes
-  my %node = ();
-  my $nextnode = 1;
-  foreach my $a (@list[0..$last]) {
-    # Pick font size
-    my $f = GetEntry($flat, $a);
-    my $c = GetEntry($cumulative, $a);
-
-    my $fs = 8;
-    if ($local_total > 0) {
-      $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total)));
-    }
-
-    $node{$a} = $nextnode++;
-    my $sym = $a;
-    $sym =~ s/\s+/\\n/g;
-    $sym =~ s/::/\\n/g;
-
-    # Extra cumulative info to print for non-leaves
-    my $extra = "";
-    if ($f != $c) {
-      $extra = sprintf("\\rof %s (%s)",
-                       Unparse($c),
-                       Percent($c, $local_total));
-    }
-    my $style = "";
-    if ($main::opt_heapcheck) {
-      if ($f > 0) {
-        # make leak-causing nodes more visible (add a background)
-        $style = ",style=filled,fillcolor=gray"
-      } elsif ($f < 0) {
-        # make anti-leak-causing nodes (which almost never occur)
-        # stand out as well (triple border)
-        $style = ",peripheries=3"
-      }
-    }
-
-    printf DOT ("N%d [label=\"%s\\n%s (%s)%s\\r" .
-                "\",shape=box,fontsize=%.1f%s];\n",
-                $node{$a},
-                $sym,
-                Unparse($f),
-                Percent($f, $local_total),
-                $extra,
-                $fs,
-                $style,
-               );
-  }
-
-  # Get edges and counts per edge
-  my %edge = ();
-  my $n;
-  my $fullname_to_shortname_map = {};
-  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
-  foreach my $k (keys(%{$raw})) {
-    # TODO: omit low %age edges
-    $n = $raw->{$k};
-    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
-    for (my $i = 1; $i <= $#translated; $i++) {
-      my $src = $translated[$i];
-      my $dst = $translated[$i-1];
-      #next if ($src eq $dst);  # Avoid self-edges?
-      if (exists($node{$src}) && exists($node{$dst})) {
-        my $edge_label = "$src\001$dst";
-        if (!exists($edge{$edge_label})) {
-          $edge{$edge_label} = 0;
-        }
-        $edge{$edge_label} += $n;
-      }
-    }
-  }
-
-  # Print edges (process in order of decreasing counts)
-  my %indegree = ();   # Number of incoming edges added per node so far
-  my %outdegree = ();  # Number of outgoing edges added per node so far
-  foreach my $e (sort { $edge{$b} <=> $edge{$a} } keys(%edge)) {
-    my @x = split(/\001/, $e);
-    $n = $edge{$e};
-
-    # Initialize degree of kept incoming and outgoing edges if necessary
-    my $src = $x[0];
-    my $dst = $x[1];
-    if (!exists($outdegree{$src})) { $outdegree{$src} = 0; }
-    if (!exists($indegree{$dst})) { $indegree{$dst} = 0; }
-
-    my $keep;
-    if ($indegree{$dst} == 0) {
-      # Keep edge if needed for reachability
-      $keep = 1;
-    } elsif (abs($n) <= $edgelimit) {
-      # Drop if we are below --edgefraction
-      $keep = 0;
-    } elsif ($outdegree{$src} >= $main::opt_maxdegree ||
-             $indegree{$dst} >= $main::opt_maxdegree) {
-      # Keep limited number of in/out edges per node
-      $keep = 0;
-    } else {
-      $keep = 1;
-    }
-
-    if ($keep) {
-      $outdegree{$src}++;
-      $indegree{$dst}++;
-
-      # Compute line width based on edge count
-      my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0);
-      if ($fraction > 1) { $fraction = 1; }
-      my $w = $fraction * 2;
-      if ($w < 1 && ($main::opt_web || $main::opt_svg)) {
-        # SVG output treats line widths < 1 poorly.
-        $w = 1;
-      }
-
-      # Dot sometimes segfaults if given edge weights that are too large, so
-      # we cap the weights at a large value
-      my $edgeweight = abs($n) ** 0.7;
-      if ($edgeweight > 100000) { $edgeweight = 100000; }
-      $edgeweight = int($edgeweight);
-
-      my $style = sprintf("setlinewidth(%f)", $w);
-      if ($x[1] =~ m/\(inline\)/) {
-        $style .= ",dashed";
-      }
-
-      # Use a slightly squashed function of the edge count as the weight
-      printf DOT ("N%s -> N%s [label=%s, weight=%d, style=\"%s\"];\n",
-                  $node{$x[0]},
-                  $node{$x[1]},
-                  Unparse($n),
-                  $edgeweight,
-                  $style);
-    }
-  }
-
-  print DOT ("}\n");
-  close(DOT);
-
-  if ($main::opt_web || $main::opt_svg) {
-    # Rewrite SVG to be more usable inside web browser.
-    RewriteSvg(TempName($main::next_tmpfile, "svg"));
-  }
-
-  return 1;
-}
-
-sub RewriteSvg {
-  my $svgfile = shift;
-
-  open(SVG, $svgfile) || die "open temp svg: $!";
-  my @svg = <SVG>;
-  close(SVG);
-  unlink $svgfile;
-  my $svg = join('', @svg);
-
-  # Dot's SVG output is
-  #
-  #    <svg width="___" height="___"
-  #     viewBox="___" xmlns=...>
-  #    <g id="graph0" transform="...">
-  #    ...
-  #    </g>
-  #    </svg>
-  #
-  # Change it to
-  #
-  #    <svg width="100%" height="100%"
-  #     xmlns=...>
-  #    $svg_javascript
-  #    <g id="viewport" transform="translate(0,0)">
-  #    <g id="graph0" transform="...">
-  #    ...
-  #    </g>
-  #    </g>
-  #    </svg>
-
-  # Fix width, height; drop viewBox.
-  $svg =~ s/(?s)<svg width="[^"]+" height="[^"]+"(.*?)viewBox="[^"]+"/<svg width="100%" height="100%"$1/;
-
-  # Insert script, viewport <g> above first <g>
-  my $svg_javascript = SvgJavascript();
-  my $viewport = "<g id=\"viewport\" transform=\"translate(0,0)\">\n";
-  $svg =~ s/<g id="graph\d"/$svg_javascript$viewport$&/;
-
-  # Insert final </g> above </svg>.
-  $svg =~ s/(.*)(<\/svg>)/$1<\/g>$2/;
-  $svg =~ s/<g id="graph\d"(.*?)/<g id="viewport"$1/;
-
-  if ($main::opt_svg) {
-    # --svg: write to standard output.
-    print $svg;
-  } else {
-    # Write back to temporary file.
-    open(SVG, ">$svgfile") || die "open $svgfile: $!";
-    print SVG $svg;
-    close(SVG);
-  }
-}
-
-sub SvgJavascript {
-  return <<'EOF';
-<script type="text/ecmascript"><![CDATA[
-// SVGPan
-// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/
-// Local modification: if(true || ...) below to force panning, never moving.
-
-/**
- *  SVGPan library 1.2
- * ====================
- *
- * Given an unique existing element with id "viewport", including the
- * the library into any SVG adds the following capabilities:
- *
- *  - Mouse panning
- *  - Mouse zooming (using the wheel)
- *  - Object dargging
- *
- * Known issues:
- *
- *  - Zooming (while panning) on Safari has still some issues
- *
- * Releases:
- *
- * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
- *     Fixed a bug with browser mouse handler interaction
- *
- * 1.1, Wed Feb  3 17:39:33 GMT 2010, Zeng Xiaohui
- *     Updated the zoom code to support the mouse wheel on Safari/Chrome
- *
- * 1.0, Andrea Leofreddi
- *     First release
- *
- * This code is licensed under the following BSD license:
- *
- * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are
- * permitted provided that the following conditions are met:
- *
- *    1. Redistributions of source code must retain the above copyright notice, this list of
- *       conditions and the following disclaimer.
- *
- *    2. Redistributions in binary form must reproduce the above copyright notice, this list
- *       of conditions and the following disclaimer in the documentation and/or other materials
- *       provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and documentation are those of the
- * authors and should not be interpreted as representing official policies, either expressed
- * or implied, of Andrea Leofreddi.
- */
-
-var root = document.documentElement;
-
-var state = 'none', stateTarget, stateOrigin, stateTf;
-
-setupHandlers(root);
-
-/**
- * Register handlers
- */
-function setupHandlers(root){
-       setAttributes(root, {
-               "onmouseup" : "add(evt)",
-               "onmousedown" : "handleMouseDown(evt)",
-               "onmousemove" : "handleMouseMove(evt)",
-               "onmouseup" : "handleMouseUp(evt)",
-               //"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element
-       });
-
-       if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)
-               window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari
-       else
-               window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others
-
-       var g = svgDoc.getElementById("svg");
-       g.width = "100%";
-       g.height = "100%";
-}
-
-/**
- * Instance an SVGPoint object with given event coordinates.
- */
-function getEventPoint(evt) {
-       var p = root.createSVGPoint();
-
-       p.x = evt.clientX;
-       p.y = evt.clientY;
-
-       return p;
-}
-
-/**
- * Sets the current transform matrix of an element.
- */
-function setCTM(element, matrix) {
-       var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
-
-       element.setAttribute("transform", s);
-}
-
-/**
- * Dumps a matrix to a string (useful for debug).
- */
-function dumpMatrix(matrix) {
-       var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n  " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n  0, 0, 1 ]";
-
-       return s;
-}
-
-/**
- * Sets attributes of an element.
- */
-function setAttributes(element, attributes){
-       for (i in attributes)
-               element.setAttributeNS(null, i, attributes[i]);
-}
-
-/**
- * Handle mouse move event.
- */
-function handleMouseWheel(evt) {
-       if(evt.preventDefault)
-               evt.preventDefault();
-
-       evt.returnValue = false;
-
-       var svgDoc = evt.target.ownerDocument;
-
-       var delta;
-
-       if(evt.wheelDelta)
-               delta = evt.wheelDelta / 3600; // Chrome/Safari
-       else
-               delta = evt.detail / -90; // Mozilla
-
-       var z = 1 + delta; // Zoom factor: 0.9/1.1
-
-       var g = svgDoc.getElementById("viewport");
-
-       var p = getEventPoint(evt);
-
-       p = p.matrixTransform(g.getCTM().inverse());
-
-       // Compute new scale matrix in current mouse position
-       var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);
-
-        setCTM(g, g.getCTM().multiply(k));
-
-       stateTf = stateTf.multiply(k.inverse());
-}
-
-/**
- * Handle mouse move event.
- */
-function handleMouseMove(evt) {
-       if(evt.preventDefault)
-               evt.preventDefault();
-
-       evt.returnValue = false;
-
-       var svgDoc = evt.target.ownerDocument;
-
-       var g = svgDoc.getElementById("viewport");
-
-       if(state == 'pan') {
-               // Pan mode
-               var p = getEventPoint(evt).matrixTransform(stateTf);
-
-               setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));
-       } else if(state == 'move') {
-               // Move mode
-               var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());
-
-               setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));
-
-               stateOrigin = p;
-       }
-}
-
-/**
- * Handle click event.
- */
-function handleMouseDown(evt) {
-       if(evt.preventDefault)
-               evt.preventDefault();
-
-       evt.returnValue = false;
-
-       var svgDoc = evt.target.ownerDocument;
-
-       var g = svgDoc.getElementById("viewport");
-
-       if(true || evt.target.tagName == "svg") {
-               // Pan mode
-               state = 'pan';
-
-               stateTf = g.getCTM().inverse();
-
-               stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
-       } else {
-               // Move mode
-               state = 'move';
-
-               stateTarget = evt.target;
-
-               stateTf = g.getCTM().inverse();
-
-               stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
-       }
-}
-
-/**
- * Handle mouse button release event.
- */
-function handleMouseUp(evt) {
-       if(evt.preventDefault)
-               evt.preventDefault();
-
-       evt.returnValue = false;
-
-       var svgDoc = evt.target.ownerDocument;
-
-       if(state == 'pan' || state == 'move') {
-               // Quit pan mode
-               state = '';
-       }
-}
-
-]]></script>
-EOF
-}
-
-# Provides a map from fullname to shortname for cases where the
-# shortname is ambiguous.  The symlist has both the fullname and
-# shortname for all symbols, which is usually fine, but sometimes --
-# such as overloaded functions -- two different fullnames can map to
-# the same shortname.  In that case, we use the address of the
-# function to disambiguate the two.  This function fills in a map that
-# maps fullnames to modified shortnames in such cases.  If a fullname
-# is not present in the map, the 'normal' shortname provided by the
-# symlist is the appropriate one to use.
-sub FillFullnameToShortnameMap {
-  my $symbols = shift;
-  my $fullname_to_shortname_map = shift;
-  my $shortnames_seen_once = {};
-  my $shortnames_seen_more_than_once = {};
-
-  foreach my $symlist (values(%{$symbols})) {
-    # TODO(csilvers): deal with inlined symbols too.
-    my $shortname = $symlist->[0];
-    my $fullname = $symlist->[2];
-    if ($fullname !~ /<[0-9a-fA-F]+>$/) {  # fullname doesn't end in an address
-      next;       # the only collisions we care about are when addresses differ
-    }
-    if (defined($shortnames_seen_once->{$shortname}) &&
-        $shortnames_seen_once->{$shortname} ne $fullname) {
-      $shortnames_seen_more_than_once->{$shortname} = 1;
-    } else {
-      $shortnames_seen_once->{$shortname} = $fullname;
-    }
-  }
-
-  foreach my $symlist (values(%{$symbols})) {
-    my $shortname = $symlist->[0];
-    my $fullname = $symlist->[2];
-    # TODO(csilvers): take in a list of addresses we care about, and only
-    # store in the map if $symlist->[1] is in that list.  Saves space.
-    next if defined($fullname_to_shortname_map->{$fullname});
-    if (defined($shortnames_seen_more_than_once->{$shortname})) {
-      if ($fullname =~ /<0*([^>]*)>$/) {   # fullname has address at end of it
-        $fullname_to_shortname_map->{$fullname} = "$shortname\@$1";
-      }
-    }
-  }
-}
-
-# Return a small number that identifies the argument.
-# Multiple calls with the same argument will return the same number.
-# Calls with different arguments will return different numbers.
-sub ShortIdFor {
-  my $key = shift;
-  my $id = $main::uniqueid{$key};
-  if (!defined($id)) {
-    $id = keys(%main::uniqueid) + 1;
-    $main::uniqueid{$key} = $id;
-  }
-  return $id;
-}
-
-# Translate a stack of addresses into a stack of symbols
-sub TranslateStack {
-  my $symbols = shift;
-  my $fullname_to_shortname_map = shift;
-  my $k = shift;
-
-  my @addrs = split(/\n/, $k);
-  my @result = ();
-  for (my $i = 0; $i <= $#addrs; $i++) {
-    my $a = $addrs[$i];
-
-    # Skip large addresses since they sometimes show up as fake entries on RH9
-    if (length($a) > 8 && $a gt "7fffffffffffffff") {
-      next;
-    }
-
-    if ($main::opt_disasm || $main::opt_list) {
-      # We want just the address for the key
-      push(@result, $a);
-      next;
-    }
-
-    my $symlist = $symbols->{$a};
-    if (!defined($symlist)) {
-      $symlist = [$a, "", $a];
-    }
-
-    # We can have a sequence of symbols for a particular entry
-    # (more than one symbol in the case of inlining).  Callers
-    # come before callees in symlist, so walk backwards since
-    # the translated stack should contain callees before callers.
-    for (my $j = $#{$symlist}; $j >= 2; $j -= 3) {
-      my $func = $symlist->[$j-2];
-      my $fileline = $symlist->[$j-1];
-      my $fullfunc = $symlist->[$j];
-      if (defined($fullname_to_shortname_map->{$fullfunc})) {
-        $func = $fullname_to_shortname_map->{$fullfunc};
-      }
-      if ($j > 2) {
-        $func = "$func (inline)";
-      }
-
-      # Do not merge nodes corresponding to Callback::Run since that
-      # causes confusing cycles in dot display.  Instead, we synthesize
-      # a unique name for this frame per caller.
-      if ($func =~ m/Callback.*::Run$/) {
-        my $caller = ($i > 0) ? $addrs[$i-1] : 0;
-        $func = "Run#" . ShortIdFor($caller);
-      }
-
-      if ($main::opt_addresses) {
-        push(@result, "$a $func $fileline");
-      } elsif ($main::opt_lines) {
-        if ($func eq '??' && $fileline eq '??:0') {
-          push(@result, "$a");
-        } else {
-          push(@result, "$func $fileline");
-        }
-      } elsif ($main::opt_functions) {
-        if ($func eq '??') {
-          push(@result, "$a");
-        } else {
-          push(@result, $func);
-        }
-      } elsif ($main::opt_files) {
-        if ($fileline eq '??:0' || $fileline eq '') {
-          push(@result, "$a");
-        } else {
-          my $f = $fileline;
-          $f =~ s/:\d+$//;
-          push(@result, $f);
-        }
-      } else {
-        push(@result, $a);
-        last;  # Do not print inlined info
-      }
-    }
-  }
-
-  # print join(",", @addrs), " => ", join(",", @result), "\n";
-  return @result;
-}
-
-# Generate percent string for a number and a total
-sub Percent {
-  my $num = shift;
-  my $tot = shift;
-  if ($tot != 0) {
-    return sprintf("%.1f%%", $num * 100.0 / $tot);
-  } else {
-    return ($num == 0) ? "nan" : (($num > 0) ? "+inf" : "-inf");
-  }
-}
-
-# Generate pretty-printed form of number
-sub Unparse {
-  my $num = shift;
-  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
-    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
-      return sprintf("%d", $num);
-    } else {
-      if ($main::opt_show_bytes) {
-        return sprintf("%d", $num);
-      } else {
-        return sprintf("%.1f", $num / 1048576.0);
-      }
-    }
-  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
-    return sprintf("%.3f", $num / 1e9); # Convert nanoseconds to seconds
-  } else {
-    return sprintf("%d", $num);
-  }
-}
-
-# Alternate pretty-printed form: 0 maps to "."
-sub UnparseAlt {
-  my $num = shift;
-  if ($num == 0) {
-    return ".";
-  } else {
-    return Unparse($num);
-  }
-}
-
-# Alternate pretty-printed form: 0 maps to ""
-sub HtmlPrintNumber {
-  my $num = shift;
-  if ($num == 0) {
-    return "";
-  } else {
-    return Unparse($num);
-  }
-}
-
-# Return output units
-sub Units {
-  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
-    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
-      return "objects";
-    } else {
-      if ($main::opt_show_bytes) {
-        return "B";
-      } else {
-        return "MB";
-      }
-    }
-  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
-    return "seconds";
-  } else {
-    return "samples";
-  }
-}
-
-##### Profile manipulation code #####
-
-# Generate flattened profile:
-# If count is charged to stack [a,b,c,d], in generated profile,
-# it will be charged to [a]
-sub FlatProfile {
-  my $profile = shift;
-  my $result = {};
-  foreach my $k (keys(%{$profile})) {
-    my $count = $profile->{$k};
-    my @addrs = split(/\n/, $k);
-    if ($#addrs >= 0) {
-      AddEntry($result, $addrs[0], $count);
-    }
-  }
-  return $result;
-}
-
-# Generate cumulative profile:
-# If count is charged to stack [a,b,c,d], in generated profile,
-# it will be charged to [a], [b], [c], [d]
-sub CumulativeProfile {
-  my $profile = shift;
-  my $result = {};
-  foreach my $k (keys(%{$profile})) {
-    my $count = $profile->{$k};
-    my @addrs = split(/\n/, $k);
-    foreach my $a (@addrs) {
-      AddEntry($result, $a, $count);
-    }
-  }
-  return $result;
-}
-
-# If the second-youngest PC on the stack is always the same, returns
-# that pc.  Otherwise, returns undef.
-sub IsSecondPcAlwaysTheSame {
-  my $profile = shift;
-
-  my $second_pc = undef;
-  foreach my $k (keys(%{$profile})) {
-    my @addrs = split(/\n/, $k);
-    if ($#addrs < 1) {
-      return undef;
-    }
-    if (not defined $second_pc) {
-      $second_pc = $addrs[1];
-    } else {
-      if ($second_pc ne $addrs[1]) {
-        return undef;
-      }
-    }
-  }
-  return $second_pc;
-}
-
-sub ExtractSymbolLocation {
-  my $symbols = shift;
-  my $address = shift;
-  # 'addr2line' outputs "??:0" for unknown locations; we do the
-  # same to be consistent.
-  my $location = "??:0:unknown";
-  if (exists $symbols->{$address}) {
-    my $file = $symbols->{$address}->[1];
-    if ($file eq "?") {
-      $file = "??:0"
-    }
-    $location = $file . ":" . $symbols->{$address}->[0];
-  }
-  return $location;
-}
-
-# Extracts a graph of calls.
-sub ExtractCalls {
-  my $symbols = shift;
-  my $profile = shift;
-
-  my $calls = {};
-  while( my ($stack_trace, $count) = each %$profile ) {
-    my @address = split(/\n/, $stack_trace);
-    my $destination = ExtractSymbolLocation($symbols, $address[0]);
-    AddEntry($calls, $destination, $count);
-    for (my $i = 1; $i <= $#address; $i++) {
-      my $source = ExtractSymbolLocation($symbols, $address[$i]);
-      my $call = "$source -> $destination";
-      AddEntry($calls, $call, $count);
-      $destination = $source;
-    }
-  }
-
-  return $calls;
-}
-
-sub RemoveUninterestingFrames {
-  my $symbols = shift;
-  my $profile = shift;
-
-  # List of function names to skip
-  my %skip = ();
-  my $skip_regexp = 'NOMATCH';
-  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
-    foreach my $name ('calloc',
-                      'cfree',
-                      'malloc',
-                      'free',
-                      'memalign',
-                      'posix_memalign',
-                      'aligned_alloc',
-                      'pvalloc',
-                      'valloc',
-                      'realloc',
-                      'mallocx', # jemalloc
-                      'rallocx', # jemalloc
-                      'xallocx', # jemalloc
-                      'dallocx', # jemalloc
-                      'sdallocx', # jemalloc
-                      'tc_calloc',
-                      'tc_cfree',
-                      'tc_malloc',
-                      'tc_free',
-                      'tc_memalign',
-                      'tc_posix_memalign',
-                      'tc_pvalloc',
-                      'tc_valloc',
-                      'tc_realloc',
-                      'tc_new',
-                      'tc_delete',
-                      'tc_newarray',
-                      'tc_deletearray',
-                      'tc_new_nothrow',
-                      'tc_newarray_nothrow',
-                      'do_malloc',
-                      '::do_malloc',   # new name -- got moved to an unnamed ns
-                      '::do_malloc_or_cpp_alloc',
-                      'DoSampledAllocation',
-                      'simple_alloc::allocate',
-                      '__malloc_alloc_template::allocate',
-                      '__builtin_delete',
-                      '__builtin_new',
-                      '__builtin_vec_delete',
-                      '__builtin_vec_new',
-                      'operator new',
-                      'operator new[]',
-                      # The entry to our memory-allocation routines on OS X
-                      'malloc_zone_malloc',
-                      'malloc_zone_calloc',
-                      'malloc_zone_valloc',
-                      'malloc_zone_realloc',
-                      'malloc_zone_memalign',
-                      'malloc_zone_free',
-                      # These mark the beginning/end of our custom sections
-                      '__start_google_malloc',
-                      '__stop_google_malloc',
-                      '__start_malloc_hook',
-                      '__stop_malloc_hook') {
-      $skip{$name} = 1;
-      $skip{"_" . $name} = 1;   # Mach (OS X) adds a _ prefix to everything
-    }
-    # TODO: Remove TCMalloc once everything has been
-    # moved into the tcmalloc:: namespace and we have flushed
-    # old code out of the system.
-    $skip_regexp = "TCMalloc|^tcmalloc::";
-  } elsif ($main::profile_type eq 'contention') {
-    foreach my $vname ('base::RecordLockProfileData',
-                       'base::SubmitMutexProfileData',
-                       'base::SubmitSpinLockProfileData',
-                       'Mutex::Unlock',
-                       'Mutex::UnlockSlow',
-                       'Mutex::ReaderUnlock',
-                       'MutexLock::~MutexLock',
-                       'SpinLock::Unlock',
-                       'SpinLock::SlowUnlock',
-                       'SpinLockHolder::~SpinLockHolder') {
-      $skip{$vname} = 1;
-    }
-  } elsif ($main::profile_type eq 'cpu') {
-    # Drop signal handlers used for CPU profile collection
-    # TODO(dpeng): this should not be necessary; it's taken
-    # care of by the general 2nd-pc mechanism below.
-    foreach my $name ('ProfileData::Add',           # historical
-                      'ProfileData::prof_handler',  # historical
-                      'CpuProfiler::prof_handler',
-                      '__FRAME_END__',
-                      '__pthread_sighandler',
-                      '__restore') {
-      $skip{$name} = 1;
-    }
-  } else {
-    # Nothing skipped for unknown types
-  }
-
-  if ($main::profile_type eq 'cpu') {
-    # If all the second-youngest program counters are the same,
-    # this STRONGLY suggests that it is an artifact of measurement,
-    # i.e., stack frames pushed by the CPU profiler signal handler.
-    # Hence, we delete them.
-    # (The topmost PC is read from the signal structure, not from
-    # the stack, so it does not get involved.)
-    while (my $second_pc = IsSecondPcAlwaysTheSame($profile)) {
-      my $result = {};
-      my $func = '';
-      if (exists($symbols->{$second_pc})) {
-        $second_pc = $symbols->{$second_pc}->[0];
-      }
-      print STDERR "Removing $second_pc from all stack traces.\n";
-      foreach my $k (keys(%{$profile})) {
-        my $count = $profile->{$k};
-        my @addrs = split(/\n/, $k);
-        splice @addrs, 1, 1;
-        my $reduced_path = join("\n", @addrs);
-        AddEntry($result, $reduced_path, $count);
-      }
-      $profile = $result;
-    }
-  }
-
-  my $result = {};
-  foreach my $k (keys(%{$profile})) {
-    my $count = $profile->{$k};
-    my @addrs = split(/\n/, $k);
-    my @path = ();
-    foreach my $a (@addrs) {
-      if (exists($symbols->{$a})) {
-        my $func = $symbols->{$a}->[0];
-        if ($skip{$func} || ($func =~ m/$skip_regexp/)) {
-          # Throw away the portion of the backtrace seen so far, under the
-          # assumption that previous frames were for functions internal to the
-          # allocator.
-          @path = ();
-          next;
-        }
-      }
-      push(@path, $a);
-    }
-    my $reduced_path = join("\n", @path);
-    AddEntry($result, $reduced_path, $count);
-  }
-  return $result;
-}
-
-# Reduce profile to granularity given by user
-sub ReduceProfile {
-  my $symbols = shift;
-  my $profile = shift;
-  my $result = {};
-  my $fullname_to_shortname_map = {};
-  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
-  foreach my $k (keys(%{$profile})) {
-    my $count = $profile->{$k};
-    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
-    my @path = ();
-    my %seen = ();
-    $seen{''} = 1;      # So that empty keys are skipped
-    foreach my $e (@translated) {
-      # To avoid double-counting due to recursion, skip a stack-trace
-      # entry if it has already been seen
-      if (!$seen{$e}) {
-        $seen{$e} = 1;
-        push(@path, $e);
-      }
-    }
-    my $reduced_path = join("\n", @path);
-    AddEntry($result, $reduced_path, $count);
-  }
-  return $result;
-}
-
-# Does the specified symbol array match the regexp?
-sub SymbolMatches {
-  my $sym = shift;
-  my $re = shift;
-  if (defined($sym)) {
-    for (my $i = 0; $i < $#{$sym}; $i += 3) {
-      if ($sym->[$i] =~ m/$re/ || $sym->[$i+1] =~ m/$re/) {
-        return 1;
-      }
-    }
-  }
-  return 0;
-}
-
-# Focus only on paths involving specified regexps
-sub FocusProfile {
-  my $symbols = shift;
-  my $profile = shift;
-  my $focus = shift;
-  my $result = {};
-  foreach my $k (keys(%{$profile})) {
-    my $count = $profile->{$k};
-    my @addrs = split(/\n/, $k);
-    foreach my $a (@addrs) {
-      # Reply if it matches either the address/shortname/fileline
-      if (($a =~ m/$focus/) || SymbolMatches($symbols->{$a}, $focus)) {
-        AddEntry($result, $k, $count);
-        last;
-      }
-    }
-  }
-  return $result;
-}
-
-# Focus only on paths not involving specified regexps
-sub IgnoreProfile {
-  my $symbols = shift;
-  my $profile = shift;
-  my $ignore = shift;
-  my $result = {};
-  foreach my $k (keys(%{$profile})) {
-    my $count = $profile->{$k};
-    my @addrs = split(/\n/, $k);
-    my $matched = 0;
-    foreach my $a (@addrs) {
-      # Reply if it matches either the address/shortname/fileline
-      if (($a =~ m/$ignore/) || SymbolMatches($symbols->{$a}, $ignore)) {
-        $matched = 1;
-        last;
-      }
-    }
-    if (!$matched) {
-      AddEntry($result, $k, $count);
-    }
-  }
-  return $result;
-}
-
-# Get total count in profile
-sub TotalProfile {
-  my $profile = shift;
-  my $result = 0;
-  foreach my $k (keys(%{$profile})) {
-    $result += $profile->{$k};
-  }
-  return $result;
-}
-
-# Add A to B
-sub AddProfile {
-  my $A = shift;
-  my $B = shift;
-
-  my $R = {};
-  # add all keys in A
-  foreach my $k (keys(%{$A})) {
-    my $v = $A->{$k};
-    AddEntry($R, $k, $v);
-  }
-  # add all keys in B
-  foreach my $k (keys(%{$B})) {
-    my $v = $B->{$k};
-    AddEntry($R, $k, $v);
-  }
-  return $R;
-}
-
-# Merges symbol maps
-sub MergeSymbols {
-  my $A = shift;
-  my $B = shift;
-
-  my $R = {};
-  foreach my $k (keys(%{$A})) {
-    $R->{$k} = $A->{$k};
-  }
-  if (defined($B)) {
-    foreach my $k (keys(%{$B})) {
-      $R->{$k} = $B->{$k};
-    }
-  }
-  return $R;
-}
-
-
-# Add A to B
-sub AddPcs {
-  my $A = shift;
-  my $B = shift;
-
-  my $R = {};
-  # add all keys in A
-  foreach my $k (keys(%{$A})) {
-    $R->{$k} = 1
-  }
-  # add all keys in B
-  foreach my $k (keys(%{$B})) {
-    $R->{$k} = 1
-  }
-  return $R;
-}
-
-# Subtract B from A
-sub SubtractProfile {
-  my $A = shift;
-  my $B = shift;
-
-  my $R = {};
-  foreach my $k (keys(%{$A})) {
-    my $v = $A->{$k} - GetEntry($B, $k);
-    if ($v < 0 && $main::opt_drop_negative) {
-      $v = 0;
-    }
-    AddEntry($R, $k, $v);
-  }
-  if (!$main::opt_drop_negative) {
-    # Take care of when subtracted profile has more entries
-    foreach my $k (keys(%{$B})) {
-      if (!exists($A->{$k})) {
-        AddEntry($R, $k, 0 - $B->{$k});
-      }
-    }
-  }
-  return $R;
-}
-
-# Get entry from profile; zero if not present
-sub GetEntry {
-  my $profile = shift;
-  my $k = shift;
-  if (exists($profile->{$k})) {
-    return $profile->{$k};
-  } else {
-    return 0;
-  }
-}
-
-# Add entry to specified profile
-sub AddEntry {
-  my $profile = shift;
-  my $k = shift;
-  my $n = shift;
-  if (!exists($profile->{$k})) {
-    $profile->{$k} = 0;
-  }
-  $profile->{$k} += $n;
-}
-
-# Add a stack of entries to specified profile, and add them to the $pcs
-# list.
-sub AddEntries {
-  my $profile = shift;
-  my $pcs = shift;
-  my $stack = shift;
-  my $count = shift;
-  my @k = ();
-
-  foreach my $e (split(/\s+/, $stack)) {
-    my $pc = HexExtend($e);
-    $pcs->{$pc} = 1;
-    push @k, $pc;
-  }
-  AddEntry($profile, (join "\n", @k), $count);
-}
-
-##### Code to profile a server dynamically #####
-
-sub CheckSymbolPage {
-  my $url = SymbolPageURL();
-  my $command = ShellEscape(@URL_FETCHER, $url);
-  open(SYMBOL, "$command |") or error($command);
-  my $line = <SYMBOL>;
-  $line =~ s/\r//g;         # turn windows-looking lines into unix-looking lines
-  close(SYMBOL);
-  unless (defined($line)) {
-    error("$url doesn't exist\n");
-  }
-
-  if ($line =~ /^num_symbols:\s+(\d+)$/) {
-    if ($1 == 0) {
-      error("Stripped binary. No symbols available.\n");
-    }
-  } else {
-    error("Failed to get the number of symbols from $url\n");
-  }
-}
-
-sub IsProfileURL {
-  my $profile_name = shift;
-  if (-f $profile_name) {
-    printf STDERR "Using local file $profile_name.\n";
-    return 0;
-  }
-  return 1;
-}
-
-sub ParseProfileURL {
-  my $profile_name = shift;
-
-  if (!defined($profile_name) || $profile_name eq "") {
-    return ();
-  }
-
-  # Split profile URL - matches all non-empty strings, so no test.
-  $profile_name =~ m,^(https?://)?([^/]+)(.*?)(/|$PROFILES)?$,;
-
-  my $proto = $1 || "http://";
-  my $hostport = $2;
-  my $prefix = $3;
-  my $profile = $4 || "/";
-
-  my $host = $hostport;
-  $host =~ s/:.*//;
-
-  my $baseurl = "$proto$hostport$prefix";
-  return ($host, $baseurl, $profile);
-}
-
-# We fetch symbols from the first profile argument.
-sub SymbolPageURL {
-  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
-  return "$baseURL$SYMBOL_PAGE";
-}
-
-sub FetchProgramName() {
-  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
-  my $url = "$baseURL$PROGRAM_NAME_PAGE";
-  my $command_line = ShellEscape(@URL_FETCHER, $url);
-  open(CMDLINE, "$command_line |") or error($command_line);
-  my $cmdline = <CMDLINE>;
-  $cmdline =~ s/\r//g;   # turn windows-looking lines into unix-looking lines
-  close(CMDLINE);
-  error("Failed to get program name from $url\n") unless defined($cmdline);
-  $cmdline =~ s/\x00.+//;  # Remove argv[1] and latters.
-  $cmdline =~ s!\n!!g;  # Remove LFs.
-  return $cmdline;
-}
-
-# Gee, curl's -L (--location) option isn't reliable at least
-# with its 7.12.3 version.  Curl will forget to post data if
-# there is a redirection.  This function is a workaround for
-# curl.  Redirection happens on borg hosts.
-sub ResolveRedirectionForCurl {
-  my $url = shift;
-  my $command_line = ShellEscape(@URL_FETCHER, "--head", $url);
-  open(CMDLINE, "$command_line |") or error($command_line);
-  while (<CMDLINE>) {
-    s/\r//g;         # turn windows-looking lines into unix-looking lines
-    if (/^Location: (.*)/) {
-      $url = $1;
-    }
-  }
-  close(CMDLINE);
-  return $url;
-}
-
-# Add a timeout flat to URL_FETCHER.  Returns a new list.
-sub AddFetchTimeout {
-  my $timeout = shift;
-  my @fetcher = shift;
-  if (defined($timeout)) {
-    if (join(" ", @fetcher) =~ m/\bcurl -s/) {
-      push(@fetcher, "--max-time", sprintf("%d", $timeout));
-    } elsif (join(" ", @fetcher) =~ m/\brpcget\b/) {
-      push(@fetcher, sprintf("--deadline=%d", $timeout));
-    }
-  }
-  return @fetcher;
-}
-
-# Reads a symbol map from the file handle name given as $1, returning
-# the resulting symbol map.  Also processes variables relating to symbols.
-# Currently, the only variable processed is 'binary=<value>' which updates
-# $main::prog to have the correct program name.
-sub ReadSymbols {
-  my $in = shift;
-  my $map = {};
-  while (<$in>) {
-    s/\r//g;         # turn windows-looking lines into unix-looking lines
-    # Removes all the leading zeroes from the symbols, see comment below.
-    if (m/^0x0*([0-9a-f]+)\s+(.+)/) {
-      $map->{$1} = $2;
-    } elsif (m/^---/) {
-      last;
-    } elsif (m/^([a-z][^=]*)=(.*)$/ ) {
-      my ($variable, $value) = ($1, $2);
-      for ($variable, $value) {
-        s/^\s+//;
-        s/\s+$//;
-      }
-      if ($variable eq "binary") {
-        if ($main::prog ne $UNKNOWN_BINARY && $main::prog ne $value) {
-          printf STDERR ("Warning: Mismatched binary name '%s', using '%s'.\n",
-                         $main::prog, $value);
-        }
-        $main::prog = $value;
-      } else {
-        printf STDERR ("Ignoring unknown variable in symbols list: " .
-            "'%s' = '%s'\n", $variable, $value);
-      }
-    }
-  }
-  return $map;
-}
-
-# Fetches and processes symbols to prepare them for use in the profile output
-# code.  If the optional 'symbol_map' arg is not given, fetches symbols from
-# $SYMBOL_PAGE for all PC values found in profile.  Otherwise, the raw symbols
-# are assumed to have already been fetched into 'symbol_map' and are simply
-# extracted and processed.
-sub FetchSymbols {
-  my $pcset = shift;
-  my $symbol_map = shift;
-
-  my %seen = ();
-  my @pcs = grep { !$seen{$_}++ } keys(%$pcset);  # uniq
-
-  if (!defined($symbol_map)) {
-    my $post_data = join("+", sort((map {"0x" . "$_"} @pcs)));
-
-    open(POSTFILE, ">$main::tmpfile_sym");
-    print POSTFILE $post_data;
-    close(POSTFILE);
-
-    my $url = SymbolPageURL();
-
-    my $command_line;
-    if (join(" ", @URL_FETCHER) =~ m/\bcurl -s/) {
-      $url = ResolveRedirectionForCurl($url);
-      $command_line = ShellEscape(@URL_FETCHER, "-d", "\@$main::tmpfile_sym",
-                                  $url);
-    } else {
-      $command_line = (ShellEscape(@URL_FETCHER, "--post", $url)
-                       . " < " . ShellEscape($main::tmpfile_sym));
-    }
-    # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols.
-    my $escaped_cppfilt = ShellEscape($obj_tool_map{"c++filt"});
-    open(SYMBOL, "$command_line | $escaped_cppfilt |") or error($command_line);
-    $symbol_map = ReadSymbols(*SYMBOL{IO});
-    close(SYMBOL);
-  }
-
-  my $symbols = {};
-  foreach my $pc (@pcs) {
-    my $fullname;
-    # For 64 bits binaries, symbols are extracted with 8 leading zeroes.
-    # Then /symbol reads the long symbols in as uint64, and outputs
-    # the result with a "0x%08llx" format which get rid of the zeroes.
-    # By removing all the leading zeroes in both $pc and the symbols from
-    # /symbol, the symbols match and are retrievable from the map.
-    my $shortpc = $pc;
-    $shortpc =~ s/^0*//;
-    # Each line may have a list of names, which includes the function
-    # and also other functions it has inlined.  They are separated (in
-    # PrintSymbolizedProfile), by --, which is illegal in function names.
-    my $fullnames;
-    if (defined($symbol_map->{$shortpc})) {
-      $fullnames = $symbol_map->{$shortpc};
-    } else {
-      $fullnames = "0x" . $pc;  # Just use addresses
-    }
-    my $sym = [];
-    $symbols->{$pc} = $sym;
-    foreach my $fullname (split("--", $fullnames)) {
-      my $name = ShortFunctionName($fullname);
-      push(@{$sym}, $name, "?", $fullname);
-    }
-  }
-  return $symbols;
-}
-
-sub BaseName {
-  my $file_name = shift;
-  $file_name =~ s!^.*/!!;  # Remove directory name
-  return $file_name;
-}
-
-sub MakeProfileBaseName {
-  my ($binary_name, $profile_name) = @_;
-  my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
-  my $binary_shortname = BaseName($binary_name);
-  return sprintf("%s.%s.%s",
-                 $binary_shortname, $main::op_time, $host);
-}
-
-sub FetchDynamicProfile {
-  my $binary_name = shift;
-  my $profile_name = shift;
-  my $fetch_name_only = shift;
-  my $encourage_patience = shift;
-
-  if (!IsProfileURL($profile_name)) {
-    return $profile_name;
-  } else {
-    my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
-    if ($path eq "" || $path eq "/") {
-      # Missing type specifier defaults to cpu-profile
-      $path = $PROFILE_PAGE;
-    }
-
-    my $profile_file = MakeProfileBaseName($binary_name, $profile_name);
-
-    my $url = "$baseURL$path";
-    my $fetch_timeout = undef;
-    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE/) {
-      if ($path =~ m/[?]/) {
-        $url .= "&";
-      } else {
-        $url .= "?";
-      }
-      $url .= sprintf("seconds=%d", $main::opt_seconds);
-      $fetch_timeout = $main::opt_seconds * 1.01 + 60;
-    } else {
-      # For non-CPU profiles, we add a type-extension to
-      # the target profile file name.
-      my $suffix = $path;
-      $suffix =~ s,/,.,g;
-      $profile_file .= $suffix;
-    }
-
-    my $profile_dir = $ENV{"PPROF_TMPDIR"} || ($ENV{HOME} . "/pprof");
-    if (! -d $profile_dir) {
-      mkdir($profile_dir)
-          || die("Unable to create profile directory $profile_dir: $!\n");
-    }
-    my $tmp_profile = "$profile_dir/.tmp.$profile_file";
-    my $real_profile = "$profile_dir/$profile_file";
-
-    if ($fetch_name_only > 0) {
-      return $real_profile;
-    }
-
-    my @fetcher = AddFetchTimeout($fetch_timeout, @URL_FETCHER);
-    my $cmd = ShellEscape(@fetcher, $url) . " > " . ShellEscape($tmp_profile);
-    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE|$CENSUSPROFILE_PAGE/){
-      print STDERR "Gathering CPU profile from $url for $main::opt_seconds seconds to\n  ${real_profile}\n";
-      if ($encourage_patience) {
-        print STDERR "Be patient...\n";
-      }
-    } else {
-      print STDERR "Fetching $path profile from $url to\n  ${real_profile}\n";
-    }
-
-    (system($cmd) == 0) || error("Failed to get profile: $cmd: $!\n");
-    (system("mv", $tmp_profile, $real_profile) == 0) || error("Unable to rename profile\n");
-    print STDERR "Wrote profile to $real_profile\n";
-    $main::collected_profile = $real_profile;
-    return $main::collected_profile;
-  }
-}
-
-# Collect profiles in parallel
-sub FetchDynamicProfiles {
-  my $items = scalar(@main::pfile_args);
-  my $levels = log($items) / log(2);
-
-  if ($items == 1) {
-    $main::profile_files[0] = FetchDynamicProfile($main::prog, $main::pfile_args[0], 0, 1);
-  } else {
-    # math rounding issues
-    if ((2 ** $levels) < $items) {
-     $levels++;
-    }
-    my $count = scalar(@main::pfile_args);
-    for (my $i = 0; $i < $count; $i++) {
-      $main::profile_files[$i] = FetchDynamicProfile($main::prog, $main::pfile_args[$i], 1, 0);
-    }
-    print STDERR "Fetching $count profiles, Be patient...\n";
-    FetchDynamicProfilesRecurse($levels, 0, 0);
-    $main::collected_profile = join(" \\\n    ", @main::profile_files);
-  }
-}
-
-# Recursively fork a process to get enough processes
-# collecting profiles
-sub FetchDynamicProfilesRecurse {
-  my $maxlevel = shift;
-  my $level = shift;
-  my $position = shift;
-
-  if (my $pid = fork()) {
-    $position = 0 | ($position << 1);
-    TryCollectProfile($maxlevel, $level, $position);
-    wait;
-  } else {
-    $position = 1 | ($position << 1);
-    TryCollectProfile($maxlevel, $level, $position);
-    cleanup();
-    exit(0);
-  }
-}
-
-# Collect a single profile
-sub TryCollectProfile {
-  my $maxlevel = shift;
-  my $level = shift;
-  my $position = shift;
-
-  if ($level >= ($maxlevel - 1)) {
-    if ($position < scalar(@main::pfile_args)) {
-      FetchDynamicProfile($main::prog, $main::pfile_args[$position], 0, 0);
-    }
-  } else {
-    FetchDynamicProfilesRecurse($maxlevel, $level+1, $position);
-  }
-}
-
-##### Parsing code #####
-
-# Provide a small streaming-read module to handle very large
-# cpu-profile files.  Stream in chunks along a sliding window.
-# Provides an interface to get one 'slot', correctly handling
-# endian-ness differences.  A slot is one 32-bit or 64-bit word
-# (depending on the input profile).  We tell endianness and bit-size
-# for the profile by looking at the first 8 bytes: in cpu profiles,
-# the second slot is always 3 (we'll accept anything that's not 0).
-BEGIN {
-  package CpuProfileStream;
-
-  sub new {
-    my ($class, $file, $fname) = @_;
-    my $self = { file        => $file,
-                 base        => 0,
-                 stride      => 512 * 1024,   # must be a multiple of bitsize/8
-                 slots       => [],
-                 unpack_code => "",           # N for big-endian, V for little
-                 perl_is_64bit => 1,          # matters if profile is 64-bit
-    };
-    bless $self, $class;
-    # Let unittests adjust the stride
-    if ($main::opt_test_stride > 0) {
-      $self->{stride} = $main::opt_test_stride;
-    }
-    # Read the first two slots to figure out bitsize and endianness.
-    my $slots = $self->{slots};
-    my $str;
-    read($self->{file}, $str, 8);
-    # Set the global $address_length based on what we see here.
-    # 8 is 32-bit (8 hexadecimal chars); 16 is 64-bit (16 hexadecimal chars).
-    $address_length = ($str eq (chr(0)x8)) ? 16 : 8;
-    if ($address_length == 8) {
-      if (substr($str, 6, 2) eq chr(0)x2) {
-        $self->{unpack_code} = 'V';  # Little-endian.
-      } elsif (substr($str, 4, 2) eq chr(0)x2) {
-        $self->{unpack_code} = 'N';  # Big-endian
-      } else {
-        ::error("$fname: header size >= 2**16\n");
-      }
-      @$slots = unpack($self->{unpack_code} . "*", $str);
-    } else {
-      # If we're a 64-bit profile, check if we're a 64-bit-capable
-      # perl.  Otherwise, each slot will be represented as a float
-      # instead of an int64, losing precision and making all the
-      # 64-bit addresses wrong.  We won't complain yet, but will
-      # later if we ever see a value that doesn't fit in 32 bits.
-      my $has_q = 0;
-      eval { $has_q = pack("Q", "1") ? 1 : 1; };
-      if (!$has_q) {
-        $self->{perl_is_64bit} = 0;
-      }
-      read($self->{file}, $str, 8);
-      if (substr($str, 4, 4) eq chr(0)x4) {
-        # We'd love to use 'Q', but it's a) not universal, b) not endian-proof.
-        $self->{unpack_code} = 'V';  # Little-endian.
-      } elsif (substr($str, 0, 4) eq chr(0)x4) {
-        $self->{unpack_code} = 'N';  # Big-endian
-      } else {
-        ::error("$fname: header size >= 2**32\n");
-      }
-      my @pair = unpack($self->{unpack_code} . "*", $str);
-      # Since we know one of the pair is 0, it's fine to just add them.
-      @$slots = (0, $pair[0] + $pair[1]);
-    }
-    return $self;
-  }
-
-  # Load more data when we access slots->get(X) which is not yet in memory.
-  sub overflow {
-    my ($self) = @_;
-    my $slots = $self->{slots};
-    $self->{base} += $#$slots + 1;   # skip over data we're replacing
-    my $str;
-    read($self->{file}, $str, $self->{stride});
-    if ($address_length == 8) {      # the 32-bit case
-      # This is the easy case: unpack provides 32-bit unpacking primitives.
-      @$slots = unpack($self->{unpack_code} . "*", $str);
-    } else {
-      # We need to unpack 32 bits at a time and combine.
-      my @b32_values = unpack($self->{unpack_code} . "*", $str);
-      my @b64_values = ();
-      for (my $i = 0; $i < $#b32_values; $i += 2) {
-        # TODO(csilvers): if this is a 32-bit perl, the math below
-        #    could end up in a too-large int, which perl will promote
-        #    to a double, losing necessary precision.  Deal with that.
-        #    Right now, we just die.
-        my ($lo, $hi) = ($b32_values[$i], $b32_values[$i+1]);
-        if ($self->{unpack_code} eq 'N') {    # big-endian
-          ($lo, $hi) = ($hi, $lo);
-        }
-        my $value = $lo + $hi * (2**32);
-        if (!$self->{perl_is_64bit} &&   # check value is exactly represented
-            (($value % (2**32)) != $lo || int($value / (2**32)) != $hi)) {
-          ::error("Need a 64-bit perl to process this 64-bit profile.\n");
-        }
-        push(@b64_values, $value);
-      }
-      @$slots = @b64_values;
-    }
-  }
-
-  # Access the i-th long in the file (logically), or -1 at EOF.
-  sub get {
-    my ($self, $idx) = @_;
-    my $slots = $self->{slots};
-    while ($#$slots >= 0) {
-      if ($idx < $self->{base}) {
-        # The only time we expect a reference to $slots[$i - something]
-        # after referencing $slots[$i] is reading the very first header.
-        # Since $stride > |header|, that shouldn't cause any lookback
-        # errors.  And everything after the header is sequential.
-        print STDERR "Unexpected look-back reading CPU profile";
-        return -1;   # shrug, don't know what better to return
-      } elsif ($idx > $self->{base} + $#$slots) {
-        $self->overflow();
-      } else {
-        return $slots->[$idx - $self->{base}];
-      }
-    }
-    # If we get here, $slots is [], which means we've reached EOF
-    return -1;  # unique since slots is supposed to hold unsigned numbers
-  }
-}
-
-# Reads the top, 'header' section of a profile, and returns the last
-# line of the header, commonly called a 'header line'.  The header
-# section of a profile consists of zero or more 'command' lines that
-# are instructions to pprof, which pprof executes when reading the
-# header.  All 'command' lines start with a %.  After the command
-# lines is the 'header line', which is a profile-specific line that
-# indicates what type of profile it is, and perhaps other global
-# information about the profile.  For instance, here's a header line
-# for a heap profile:
-#   heap profile:     53:    38236 [  5525:  1284029] @ heapprofile
-# For historical reasons, the CPU profile does not contain a text-
-# readable header line.  If the profile looks like a CPU profile,
-# this function returns "".  If no header line could be found, this
-# function returns undef.
-#
-# The following commands are recognized:
-#   %warn -- emit the rest of this line to stderr, prefixed by 'WARNING:'
-#
-# The input file should be in binmode.
-sub ReadProfileHeader {
-  local *PROFILE = shift;
-  my $firstchar = "";
-  my $line = "";
-  read(PROFILE, $firstchar, 1);
-  seek(PROFILE, -1, 1);                    # unread the firstchar
-  if ($firstchar !~ /[[:print:]]/) {       # is not a text character
-    return "";
-  }
-  while (defined($line = <PROFILE>)) {
-    $line =~ s/\r//g;   # turn windows-looking lines into unix-looking lines
-    if ($line =~ /^%warn\s+(.*)/) {        # 'warn' command
-      # Note this matches both '%warn blah\n' and '%warn\n'.
-      print STDERR "WARNING: $1\n";        # print the rest of the line
-    } elsif ($line =~ /^%/) {
-      print STDERR "Ignoring unknown command from profile header: $line";
-    } else {
-      # End of commands, must be the header line.
-      return $line;
-    }
-  }
-  return undef;     # got to EOF without seeing a header line
-}
-
-sub IsSymbolizedProfileFile {
-  my $file_name = shift;
-  if (!(-e $file_name) || !(-r $file_name)) {
-    return 0;
-  }
-  # Check if the file contains a symbol-section marker.
-  open(TFILE, "<$file_name");
-  binmode TFILE;
-  my $firstline = ReadProfileHeader(*TFILE);
-  close(TFILE);
-  if (!$firstline) {
-    return 0;
-  }
-  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
-  my $symbol_marker = $&;
-  return $firstline =~ /^--- *$symbol_marker/;
-}
-
-# Parse profile generated by common/profiler.cc and return a reference
-# to a map:
-#      $result->{version}     Version number of profile file
-#      $result->{period}      Sampling period (in microseconds)
-#      $result->{profile}     Profile object
-#      $result->{threads}     Map of thread IDs to profile objects
-#      $result->{map}         Memory map info from profile
-#      $result->{pcs}         Hash of all PC values seen, key is hex address
-sub ReadProfile {
-  my $prog = shift;
-  my $fname = shift;
-  my $result;            # return value
-
-  $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
-  my $contention_marker = $&;
-  $GROWTH_PAGE  =~ m,[^/]+$,;    # matches everything after the last slash
-  my $growth_marker = $&;
-  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
-  my $symbol_marker = $&;
-  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
-  my $profile_marker = $&;
-
-  # Look at first line to see if it is a heap or a CPU profile.
-  # CPU profile may start with no header at all, and just binary data
-  # (starting with \0\0\0\0) -- in that case, don't try to read the
-  # whole firstline, since it may be gigabytes(!) of data.
-  open(PROFILE, "<$fname") || error("$fname: $!\n");
-  binmode PROFILE;      # New perls do UTF-8 processing
-  my $header = ReadProfileHeader(*PROFILE);
-  if (!defined($header)) {   # means "at EOF"
-    error("Profile is empty.\n");
-  }
-
-  my $symbols;
-  if ($header =~ m/^--- *$symbol_marker/o) {
-    # Verify that the user asked for a symbolized profile
-    if (!$main::use_symbolized_profile) {
-      # we have both a binary and symbolized profiles, abort
-      error("FATAL ERROR: Symbolized profile\n   $fname\ncannot be used with " .
-            "a binary arg. Try again without passing\n   $prog\n");
-    }
-    # Read the symbol section of the symbolized profile file.
-    $symbols = ReadSymbols(*PROFILE{IO});
-    # Read the next line to get the header for the remaining profile.
-    $header = ReadProfileHeader(*PROFILE) || "";
-  }
-
-  $main::profile_type = '';
-  if ($header =~ m/^heap profile:.*$growth_marker/o) {
-    $main::profile_type = 'growth';
-    $result =  ReadHeapProfile($prog, *PROFILE, $header);
-  } elsif ($header =~ m/^heap profile:/) {
-    $main::profile_type = 'heap';
-    $result =  ReadHeapProfile($prog, *PROFILE, $header);
-  } elsif ($header =~ m/^heap/) {
-    $main::profile_type = 'heap';
-    $result = ReadThreadedHeapProfile($prog, $fname, $header);
-  } elsif ($header =~ m/^--- *$contention_marker/o) {
-    $main::profile_type = 'contention';
-    $result = ReadSynchProfile($prog, *PROFILE);
-  } elsif ($header =~ m/^--- *Stacks:/) {
-    print STDERR
-      "Old format contention profile: mistakenly reports " .
-      "condition variable signals as lock contentions.\n";
-    $main::profile_type = 'contention';
-    $result = ReadSynchProfile($prog, *PROFILE);
-  } elsif ($header =~ m/^--- *$profile_marker/) {
-    # the binary cpu profile data starts immediately after this line
-    $main::profile_type = 'cpu';
-    $result = ReadCPUProfile($prog, $fname, *PROFILE);
-  } else {
-    if (defined($symbols)) {
-      # a symbolized profile contains a format we don't recognize, bail out
-      error("$fname: Cannot recognize profile section after symbols.\n");
-    }
-    # no ascii header present -- must be a CPU profile
-    $main::profile_type = 'cpu';
-    $result = ReadCPUProfile($prog, $fname, *PROFILE);
-  }
-
-  close(PROFILE);
-
-  # if we got symbols along with the profile, return those as well
-  if (defined($symbols)) {
-    $result->{symbols} = $symbols;
-  }
-
-  return $result;
-}
-
-# Subtract one from caller pc so we map back to call instr.
-# However, don't do this if we're reading a symbolized profile
-# file, in which case the subtract-one was done when the file
-# was written.
-#
-# We apply the same logic to all readers, though ReadCPUProfile uses an
-# independent implementation.
-sub FixCallerAddresses {
-  my $stack = shift;
-  if ($main::use_symbolized_profile) {
-    return $stack;
-  } else {
-    $stack =~ /(\s)/;
-    my $delimiter = $1;
-    my @addrs = split(' ', $stack);
-    my @fixedaddrs;
-    $#fixedaddrs = $#addrs;
-    if ($#addrs >= 0) {
-      $fixedaddrs[0] = $addrs[0];
-    }
-    for (my $i = 1; $i <= $#addrs; $i++) {
-      $fixedaddrs[$i] = AddressSub($addrs[$i], "0x1");
-    }
-    return join $delimiter, @fixedaddrs;
-  }
-}
-
-# CPU profile reader
-sub ReadCPUProfile {
-  my $prog = shift;
-  my $fname = shift;       # just used for logging
-  local *PROFILE = shift;
-  my $version;
-  my $period;
-  my $i;
-  my $profile = {};
-  my $pcs = {};
-
-  # Parse string into array of slots.
-  my $slots = CpuProfileStream->new(*PROFILE, $fname);
-
-  # Read header.  The current header version is a 5-element structure
-  # containing:
-  #   0: header count (always 0)
-  #   1: header "words" (after this one: 3)
-  #   2: format version (0)
-  #   3: sampling period (usec)
-  #   4: unused padding (always 0)
-  if ($slots->get(0) != 0 ) {
-    error("$fname: not a profile file, or old format profile file\n");
-  }
-  $i = 2 + $slots->get(1);
-  $version = $slots->get(2);
-  $period = $slots->get(3);
-  # Do some sanity checking on these header values.
-  if ($version > (2**32) || $period > (2**32) || $i > (2**32) || $i < 5) {
-    error("$fname: not a profile file, or corrupted profile file\n");
-  }
-
-  # Parse profile
-  while ($slots->get($i) != -1) {
-    my $n = $slots->get($i++);
-    my $d = $slots->get($i++);
-    if ($d > (2**16)) {  # TODO(csilvers): what's a reasonable max-stack-depth?
-      my $addr = sprintf("0%o", $i * ($address_length == 8 ? 4 : 8));
-      print STDERR "At index $i (address $addr):\n";
-      error("$fname: stack trace depth >= 2**32\n");
-    }
-    if ($slots->get($i) == 0) {
-      # End of profile data marker
-      $i += $d;
-      last;
-    }
-
-    # Make key out of the stack entries
-    my @k = ();
-    for (my $j = 0; $j < $d; $j++) {
-      my $pc = $slots->get($i+$j);
-      # Subtract one from caller pc so we map back to call instr.
-      # However, don't do this if we're reading a symbolized profile
-      # file, in which case the subtract-one was done when the file
-      # was written.
-      if ($j > 0 && !$main::use_symbolized_profile) {
-        $pc--;
-      }
-      $pc = sprintf("%0*x", $address_length, $pc);
-      $pcs->{$pc} = 1;
-      push @k, $pc;
-    }
-
-    AddEntry($profile, (join "\n", @k), $n);
-    $i += $d;
-  }
-
-  # Parse map
-  my $map = '';
-  seek(PROFILE, $i * 4, 0);
-  read(PROFILE, $map, (stat PROFILE)[7]);
-
-  my $r = {};
-  $r->{version} = $version;
-  $r->{period} = $period;
-  $r->{profile} = $profile;
-  $r->{libs} = ParseLibraries($prog, $map, $pcs);
-  $r->{pcs} = $pcs;
-
-  return $r;
-}
-
-sub HeapProfileIndex {
-  my $index = 1;
-  if ($main::opt_inuse_space) {
-    $index = 1;
-  } elsif ($main::opt_inuse_objects) {
-    $index = 0;
-  } elsif ($main::opt_alloc_space) {
-    $index = 3;
-  } elsif ($main::opt_alloc_objects) {
-    $index = 2;
-  }
-  return $index;
-}
-
-sub ReadMappedLibraries {
-  my $fh = shift;
-  my $map = "";
-  # Read the /proc/self/maps data
-  while (<$fh>) {
-    s/\r//g;         # turn windows-looking lines into unix-looking lines
-    $map .= $_;
-  }
-  return $map;
-}
-
-sub ReadMemoryMap {
-  my $fh = shift;
-  my $map = "";
-  # Read /proc/self/maps data as formatted by DumpAddressMap()
-  my $buildvar = "";
-  while (<PROFILE>) {
-    s/\r//g;         # turn windows-looking lines into unix-looking lines
-    # Parse "build=<dir>" specification if supplied
-    if (m/^\s*build=(.*)\n/) {
-      $buildvar = $1;
-    }
-
-    # Expand "$build" variable if available
-    $_ =~ s/\$build\b/$buildvar/g;
-
-    $map .= $_;
-  }
-  return $map;
-}
-
-sub AdjustSamples {
-  my ($sample_adjustment, $sampling_algorithm, $n1, $s1, $n2, $s2) = @_;
-  if ($sample_adjustment) {
-    if ($sampling_algorithm == 2) {
-      # Remote-heap version 2
-      # The sampling frequency is the rate of a Poisson process.
-      # This means that the probability of sampling an allocation of
-      # size X with sampling rate Y is 1 - exp(-X/Y)
-      if ($n1 != 0) {
-        my $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
-        my $scale_factor = 1/(1 - exp(-$ratio));
-        $n1 *= $scale_factor;
-        $s1 *= $scale_factor;
-      }
-      if ($n2 != 0) {
-        my $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
-        my $scale_factor = 1/(1 - exp(-$ratio));
-        $n2 *= $scale_factor;
-        $s2 *= $scale_factor;
-      }
-    } else {
-      # Remote-heap version 1
-      my $ratio;
-      $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
-      if ($ratio < 1) {
-        $n1 /= $ratio;
-        $s1 /= $ratio;
-      }
-      $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
-      if ($ratio < 1) {
-        $n2 /= $ratio;
-        $s2 /= $ratio;
-      }
-    }
-  }
-  return ($n1, $s1, $n2, $s2);
-}
-
-sub ReadHeapProfile {
-  my $prog = shift;
-  local *PROFILE = shift;
-  my $header = shift;
-
-  my $index = HeapProfileIndex();
-
-  # Find the type of this profile.  The header line looks like:
-  #    heap profile:   1246:  8800744 [  1246:  8800744] @ <heap-url>/266053
-  # There are two pairs <count: size>, the first inuse objects/space, and the
-  # second allocated objects/space.  This is followed optionally by a profile
-  # type, and if that is present, optionally by a sampling frequency.
-  # For remote heap profiles (v1):
-  # The interpretation of the sampling frequency is that the profiler, for
-  # each sample, calculates a uniformly distributed random integer less than
-  # the given value, and records the next sample after that many bytes have
-  # been allocated.  Therefore, the expected sample interval is half of the
-  # given frequency.  By default, if not specified, the expected sample
-  # interval is 128KB.  Only remote-heap-page profiles are adjusted for
-  # sample size.
-  # For remote heap profiles (v2):
-  # The sampling frequency is the rate of a Poisson process. This means that
-  # the probability of sampling an allocation of size X with sampling rate Y
-  # is 1 - exp(-X/Y)
-  # For version 2, a typical header line might look like this:
-  # heap profile:   1922: 127792360 [  1922: 127792360] @ <heap-url>_v2/524288
-  # the trailing number (524288) is the sampling rate. (Version 1 showed
-  # double the 'rate' here)
-  my $sampling_algorithm = 0;
-  my $sample_adjustment = 0;
-  chomp($header);
-  my $type = "unknown";
-  if ($header =~ m"^heap profile:\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\](\s*@\s*([^/]*)(/(\d+))?)?") {
-    if (defined($6) && ($6 ne '')) {
-      $type = $6;
-      my $sample_period = $8;
-      # $type is "heapprofile" for profiles generated by the
-      # heap-profiler, and either "heap" or "heap_v2" for profiles
-      # generated by sampling directly within tcmalloc.  It can also
-      # be "growth" for heap-growth profiles.  The first is typically
-      # found for profiles generated locally, and the others for
-      # remote profiles.
-      if (($type eq "heapprofile") || ($type !~ /heap/) ) {
-        # No need to adjust for the sampling rate with heap-profiler-derived data
-        $sampling_algorithm = 0;
-      } elsif ($type =~ /_v2/) {
-        $sampling_algorithm = 2;     # version 2 sampling
-        if (defined($sample_period) && ($sample_period ne '')) {
-          $sample_adjustment = int($sample_period);
-        }
-      } else {
-        $sampling_algorithm = 1;     # version 1 sampling
-        if (defined($sample_period) && ($sample_period ne '')) {
-          $sample_adjustment = int($sample_period)/2;
-        }
-      }
-    } else {
-      # We detect whether or not this is a remote-heap profile by checking
-      # that the total-allocated stats ($n2,$s2) are exactly the
-      # same as the in-use stats ($n1,$s1).  It is remotely conceivable
-      # that a non-remote-heap profile may pass this check, but it is hard
-      # to imagine how that could happen.
-      # In this case it's so old it's guaranteed to be remote-heap version 1.
-      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
-      if (($n1 == $n2) && ($s1 == $s2)) {
-        # This is likely to be a remote-heap based sample profile
-        $sampling_algorithm = 1;
-      }
-    }
-  }
-
-  if ($sampling_algorithm > 0) {
-    # For remote-heap generated profiles, adjust the counts and sizes to
-    # account for the sample rate (we sample once every 128KB by default).
-    if ($sample_adjustment == 0) {
-      # Turn on profile adjustment.
-      $sample_adjustment = 128*1024;
-      print STDERR "Adjusting heap profiles for 1-in-128KB sampling rate\n";
-    } else {
-      printf STDERR ("Adjusting heap profiles for 1-in-%d sampling rate\n",
-                     $sample_adjustment);
-    }
-    if ($sampling_algorithm > 1) {
-      # We don't bother printing anything for the original version (version 1)
-      printf STDERR "Heap version $sampling_algorithm\n";
-    }
-  }
-
-  my $profile = {};
-  my $pcs = {};
-  my $map = "";
-
-  while (<PROFILE>) {
-    s/\r//g;         # turn windows-looking lines into unix-looking lines
-    if (/^MAPPED_LIBRARIES:/) {
-      $map .= ReadMappedLibraries(*PROFILE);
-      last;
-    }
-
-    if (/^--- Memory map:/) {
-      $map .= ReadMemoryMap(*PROFILE);
-      last;
-    }
-
-    # Read entry of the form:
-    #  <count1>: <bytes1> [<count2>: <bytes2>] @ a1 a2 a3 ... an
-    s/^\s*//;
-    s/\s*$//;
-    if (m/^\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]\s+@\s+(.*)$/) {
-      my $stack = $5;
-      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
-      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,
-                                 $n1, $s1, $n2, $s2);
-      AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
-    }
-  }
-
-  my $r = {};
-  $r->{version} = "heap";
-  $r->{period} = 1;
-  $r->{profile} = $profile;
-  $r->{libs} = ParseLibraries($prog, $map, $pcs);
-  $r->{pcs} = $pcs;
-  return $r;
-}
-
-sub ReadThreadedHeapProfile {
-  my ($prog, $fname, $header) = @_;
-
-  my $index = HeapProfileIndex();
-  my $sampling_algorithm = 0;
-  my $sample_adjustment = 0;
-  chomp($header);
-  my $type = "unknown";
-  # Assuming a very specific type of header for now.
-  if ($header =~ m"^heap_v2/(\d+)") {
-    $type = "_v2";
-    $sampling_algorithm = 2;
-    $sample_adjustment = int($1);
-  }
-  if ($type ne "_v2" || !defined($sample_adjustment)) {
-    die "Threaded heap profiles require v2 sampling with a sample rate\n";
-  }
-
-  my $profile = {};
-  my $thread_profiles = {};
-  my $pcs = {};
-  my $map = "";
-  my $stack = "";
-
-  while (<PROFILE>) {
-    s/\r//g;
-    if (/^MAPPED_LIBRARIES:/) {
-      $map .= ReadMappedLibraries(*PROFILE);
-      last;
-    }
-
-    if (/^--- Memory map:/) {
-      $map .= ReadMemoryMap(*PROFILE);
-      last;
-    }
-
-    # Read entry of the form:
-    # @ a1 a2 ... an
-    #   t*: <count1>: <bytes1> [<count2>: <bytes2>]
-    #   t1: <count1>: <bytes1> [<count2>: <bytes2>]
-    #     ...
-    #   tn: <count1>: <bytes1> [<count2>: <bytes2>]
-    s/^\s*//;
-    s/\s*$//;
-    if (m/^@\s+(.*)$/) {
-      $stack = $1;
-    } elsif (m/^\s*(t(\*|\d+)):\s+(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]$/) {
-      if ($stack eq "") {
-        # Still in the header, so this is just a per-thread summary.
-        next;
-      }
-      my $thread = $2;
-      my ($n1, $s1, $n2, $s2) = ($3, $4, $5, $6);
-      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,
-                                 $n1, $s1, $n2, $s2);
-      if ($thread eq "*") {
-        AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
-      } else {
-        if (!exists($thread_profiles->{$thread})) {
-          $thread_profiles->{$thread} = {};
-        }
-        AddEntries($thread_profiles->{$thread}, $pcs,
-                   FixCallerAddresses($stack), $counts[$index]);
-      }
-    }
-  }
-
-  my $r = {};
-  $r->{version} = "heap";
-  $r->{period} = 1;
-  $r->{profile} = $profile;
-  $r->{threads} = $thread_profiles;
-  $r->{libs} = ParseLibraries($prog, $map, $pcs);
-  $r->{pcs} = $pcs;
-  return $r;
-}
-
-sub ReadSynchProfile {
-  my $prog = shift;
-  local *PROFILE = shift;
-  my $header = shift;
-
-  my $map = '';
-  my $profile = {};
-  my $pcs = {};
-  my $sampling_period = 1;
-  my $cyclespernanosec = 2.8;   # Default assumption for old binaries
-  my $seen_clockrate = 0;
-  my $line;
-
-  my $index = 0;
-  if ($main::opt_total_delay) {
-    $index = 0;
-  } elsif ($main::opt_contentions) {
-    $index = 1;
-  } elsif ($main::opt_mean_delay) {
-    $index = 2;
-  }
-
-  while ( $line = <PROFILE> ) {
-    $line =~ s/\r//g;      # turn windows-looking lines into unix-looking lines
-    if ( $line =~ /^\s*(\d+)\s+(\d+) \@\s*(.*?)\s*$/ ) {
-      my ($cycles, $count, $stack) = ($1, $2, $3);
-
-      # Convert cycles to nanoseconds
-      $cycles /= $cyclespernanosec;
-
-      # Adjust for sampling done by application
-      $cycles *= $sampling_period;
-      $count *= $sampling_period;
-
-      my @values = ($cycles, $count, $cycles / $count);
-      AddEntries($profile, $pcs, FixCallerAddresses($stack), $values[$index]);
-
-    } elsif ( $line =~ /^(slow release).*thread \d+  \@\s*(.*?)\s*$/ ||
-              $line =~ /^\s*(\d+) \@\s*(.*?)\s*$/ ) {
-      my ($cycles, $stack) = ($1, $2);
-      if ($cycles !~ /^\d+$/) {
-        next;
-      }
-
-      # Convert cycles to nanoseconds
-      $cycles /= $cyclespernanosec;
-
-      # Adjust for sampling done by application
-      $cycles *= $sampling_period;
-
-      AddEntries($profile, $pcs, FixCallerAddresses($stack), $cycles);
-
-    } elsif ( $line =~ m/^([a-z][^=]*)=(.*)$/ ) {
-      my ($variable, $value) = ($1,$2);
-      for ($variable, $value) {
-        s/^\s+//;
-        s/\s+$//;
-      }
-      if ($variable eq "cycles/second") {
-        $cyclespernanosec = $value / 1e9;
-        $seen_clockrate = 1;
-      } elsif ($variable eq "sampling period") {
-        $sampling_period = $value;
-      } elsif ($variable eq "ms since reset") {
-        # Currently nothing is done with this value in pprof
-        # So we just silently ignore it for now
-      } elsif ($variable eq "discarded samples") {
-        # Currently nothing is done with this value in pprof
-        # So we just silently ignore it for now
-      } else {
-        printf STDERR ("Ignoring unnknown variable in /contention output: " .
-                       "'%s' = '%s'\n",$variable,$value);
-      }
-    } else {
-      # Memory map entry
-      $map .= $line;
-    }
-  }
-
-  if (!$seen_clockrate) {
-    printf STDERR ("No cycles/second entry in profile; Guessing %.1f GHz\n",
-                   $cyclespernanosec);
-  }
-
-  my $r = {};
-  $r->{version} = 0;
-  $r->{period} = $sampling_period;
-  $r->{profile} = $profile;
-  $r->{libs} = ParseLibraries($prog, $map, $pcs);
-  $r->{pcs} = $pcs;
-  return $r;
-}
-
-# Given a hex value in the form "0x1abcd" or "1abcd", return either
-# "0001abcd" or "000000000001abcd", depending on the current (global)
-# address length.
-sub HexExtend {
-  my $addr = shift;
-
-  $addr =~ s/^(0x)?0*//;
-  my $zeros_needed = $address_length - length($addr);
-  if ($zeros_needed < 0) {
-    printf STDERR "Warning: address $addr is longer than address length $address_length\n";
-    return $addr;
-  }
-  return ("0" x $zeros_needed) . $addr;
-}
-
-##### Symbol extraction #####
-
-# Aggressively search the lib_prefix values for the given library
-# If all else fails, just return the name of the library unmodified.
-# If the lib_prefix is "/my/path,/other/path" and $file is "/lib/dir/mylib.so"
-# it will search the following locations in this order, until it finds a file:
-#   /my/path/lib/dir/mylib.so
-#   /other/path/lib/dir/mylib.so
-#   /my/path/dir/mylib.so
-#   /other/path/dir/mylib.so
-#   /my/path/mylib.so
-#   /other/path/mylib.so
-#   /lib/dir/mylib.so              (returned as last resort)
-sub FindLibrary {
-  my $file = shift;
-  my $suffix = $file;
-
-  # Search for the library as described above
-  do {
-    foreach my $prefix (@prefix_list) {
-      my $fullpath = $prefix . $suffix;
-      if (-e $fullpath) {
-        return $fullpath;
-      }
-    }
-  } while ($suffix =~ s|^/[^/]+/|/|);
-  return $file;
-}
-
-# Return path to library with debugging symbols.
-# For libc libraries, the copy in /usr/lib/debug contains debugging symbols
-sub DebuggingLibrary {
-  my $file = shift;
-  if ($file =~ m|^/|) {
-      if (-f "/usr/lib/debug$file") {
-        return "/usr/lib/debug$file";
-      } elsif (-f "/usr/lib/debug$file.debug") {
-        return "/usr/lib/debug$file.debug";
-      }
-  }
-  return undef;
-}
-
-# Parse text section header of a library using objdump
-sub ParseTextSectionHeaderFromObjdump {
-  my $lib = shift;
-
-  my $size = undef;
-  my $vma;
-  my $file_offset;
-  # Get objdump output from the library file to figure out how to
-  # map between mapped addresses and addresses in the library.
-  my $cmd = ShellEscape($obj_tool_map{"objdump"}, "-h", $lib);
-  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
-  while (<OBJDUMP>) {
-    s/\r//g;         # turn windows-looking lines into unix-looking lines
-    # Idx Name          Size      VMA       LMA       File off  Algn
-    #  10 .text         00104b2c  420156f0  420156f0  000156f0  2**4
-    # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file
-    # offset may still be 8.  But AddressSub below will still handle that.
-    my @x = split;
-    if (($#x >= 6) && ($x[1] eq '.text')) {
-      $size = $x[2];
-      $vma = $x[3];
-      $file_offset = $x[5];
-      last;
-    }
-  }
-  close(OBJDUMP);
-
-  if (!defined($size)) {
-    return undef;
-  }
-
-  my $r = {};
-  $r->{size} = $size;
-  $r->{vma} = $vma;
-  $r->{file_offset} = $file_offset;
-
-  return $r;
-}
-
-# Parse text section header of a library using otool (on OS X)
-sub ParseTextSectionHeaderFromOtool {
-  my $lib = shift;
-
-  my $size = undef;
-  my $vma = undef;
-  my $file_offset = undef;
-  # Get otool output from the library file to figure out how to
-  # map between mapped addresses and addresses in the library.
-  my $command = ShellEscape($obj_tool_map{"otool"}, "-l", $lib);
-  open(OTOOL, "$command |") || error("$command: $!\n");
-  my $cmd = "";
-  my $sectname = "";
-  my $segname = "";
-  foreach my $line (<OTOOL>) {
-    $line =~ s/\r//g;      # turn windows-looking lines into unix-looking lines
-    # Load command <#>
-    #       cmd LC_SEGMENT
-    # [...]
-    # Section
-    #   sectname __text
-    #    segname __TEXT
-    #       addr 0x000009f8
-    #       size 0x00018b9e
-    #     offset 2552
-    #      align 2^2 (4)
-    # We will need to strip off the leading 0x from the hex addresses,
-    # and convert the offset into hex.
-    if ($line =~ /Load command/) {
-      $cmd = "";
-      $sectname = "";
-      $segname = "";
-    } elsif ($line =~ /Section/) {
-      $sectname = "";
-      $segname = "";
-    } elsif ($line =~ /cmd (\w+)/) {
-      $cmd = $1;
-    } elsif ($line =~ /sectname (\w+)/) {
-      $sectname = $1;
-    } elsif ($line =~ /segname (\w+)/) {
-      $segname = $1;
-    } elsif (!(($cmd eq "LC_SEGMENT" || $cmd eq "LC_SEGMENT_64") &&
-               $sectname eq "__text" &&
-               $segname eq "__TEXT")) {
-      next;
-    } elsif ($line =~ /\baddr 0x([0-9a-fA-F]+)/) {
-      $vma = $1;
-    } elsif ($line =~ /\bsize 0x([0-9a-fA-F]+)/) {
-      $size = $1;
-    } elsif ($line =~ /\boffset ([0-9]+)/) {
-      $file_offset = sprintf("%016x", $1);
-    }
-    if (defined($vma) && defined($size) && defined($file_offset)) {
-      last;
-    }
-  }
-  close(OTOOL);
-
-  if (!defined($vma) || !defined($size) || !defined($file_offset)) {
-     return undef;
-  }
-
-  my $r = {};
-  $r->{size} = $size;
-  $r->{vma} = $vma;
-  $r->{file_offset} = $file_offset;
-
-  return $r;
-}
-
-sub ParseTextSectionHeader {
-  # obj_tool_map("otool") is only defined if we're in a Mach-O environment
-  if (defined($obj_tool_map{"otool"})) {
-    my $r = ParseTextSectionHeaderFromOtool(@_);
-    if (defined($r)){
-      return $r;
-    }
-  }
-  # If otool doesn't work, or we don't have it, fall back to objdump
-  return ParseTextSectionHeaderFromObjdump(@_);
-}
-
-# Split /proc/pid/maps dump into a list of libraries
-sub ParseLibraries {
-  return if $main::use_symbol_page;  # We don't need libraries info.
-  my $prog = shift;
-  my $map = shift;
-  my $pcs = shift;
-
-  my $result = [];
-  my $h = "[a-f0-9]+";
-  my $zero_offset = HexExtend("0");
-
-  my $buildvar = "";
-  foreach my $l (split("\n", $map)) {
-    if ($l =~ m/^\s*build=(.*)$/) {
-      $buildvar = $1;
-    }
-
-    my $start;
-    my $finish;
-    my $offset;
-    my $lib;
-    if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?)$/i) {
-      # Full line from /proc/self/maps.  Example:
-      #   40000000-40015000 r-xp 00000000 03:01 12845071   /lib/ld-2.3.2.so
-      $start = HexExtend($1);
-      $finish = HexExtend($2);
-      $offset = HexExtend($3);
-      $lib = $4;
-      $lib =~ s|\\|/|g;     # turn windows-style paths into unix-style paths
-    } elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) {
-      # Cooked line from DumpAddressMap.  Example:
-      #   40000000-40015000: /lib/ld-2.3.2.so
-      $start = HexExtend($1);
-      $finish = HexExtend($2);
-      $offset = $zero_offset;
-      $lib = $3;
-    }
-    # FreeBSD 10.0 virtual memory map /proc/curproc/map as defined in
-    # function procfs_doprocmap (sys/fs/procfs/procfs_map.c)
-    #
-    # Example:
-    # 0x800600000 0x80061a000 26 0 0xfffff800035a0000 r-x 75 33 0x1004 COW NC vnode /libexec/ld-elf.s
-    # o.1 NCH -1
-    elsif ($l =~ /^(0x$h)\s(0x$h)\s\d+\s\d+\s0x$h\sr-x\s\d+\s\d+\s0x\d+\s(COW|NCO)\s(NC|NNC)\svnode\s(\S+\.so(\.\d+)*)/) {
-      $start = HexExtend($1);
-      $finish = HexExtend($2);
-      $offset = $zero_offset;
-      $lib = FindLibrary($5);
-
-    } else {
-      next;
-    }
-
-    # Expand "$build" variable if available
-    $lib =~ s/\$build\b/$buildvar/g;
-
-    $lib = FindLibrary($lib);
-
-    # Check for pre-relocated libraries, which use pre-relocated symbol tables
-    # and thus require adjusting the offset that we'll use to translate
-    # VM addresses into symbol table addresses.
-    # Only do this if we're not going to fetch the symbol table from a
-    # debugging copy of the library.
-    if (!DebuggingLibrary($lib)) {
-      my $text = ParseTextSectionHeader($lib);
-      if (defined($text)) {
-         my $vma_offset = AddressSub($text->{vma}, $text->{file_offset});
-         $offset = AddressAdd($offset, $vma_offset);
-      }
-    }
-
-    if($main::opt_debug) { printf STDERR "$start:$finish ($offset) $lib\n"; }
-    push(@{$result}, [$lib, $start, $finish, $offset]);
-  }
-
-  # Append special entry for additional library (not relocated)
-  if ($main::opt_lib ne "") {
-    my $text = ParseTextSectionHeader($main::opt_lib);
-    if (defined($text)) {
-       my $start = $text->{vma};
-       my $finish = AddressAdd($start, $text->{size});
-
-       push(@{$result}, [$main::opt_lib, $start, $finish, $start]);
-    }
-  }
-
-  # Append special entry for the main program.  This covers
-  # 0..max_pc_value_seen, so that we assume pc values not found in one
-  # of the library ranges will be treated as coming from the main
-  # program binary.
-  my $min_pc = HexExtend("0");
-  my $max_pc = $min_pc;          # find the maximal PC value in any sample
-  foreach my $pc (keys(%{$pcs})) {
-    if (HexExtend($pc) gt $max_pc) { $max_pc = HexExtend($pc); }
-  }
-  push(@{$result}, [$prog, $min_pc, $max_pc, $zero_offset]);
-
-  return $result;
-}
-
-# Add two hex addresses of length $address_length.
-# Run pprof --test for unit test if this is changed.
-sub AddressAdd {
-  my $addr1 = shift;
-  my $addr2 = shift;
-  my $sum;
-
-  if ($address_length == 8) {
-    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
-    $sum = (hex($addr1)+hex($addr2)) % (0x10000000 * 16);
-    return sprintf("%08x", $sum);
-
-  } else {
-    # Do the addition in 7-nibble chunks to trivialize carry handling.
-
-    if ($main::opt_debug and $main::opt_test) {
-      print STDERR "AddressAdd $addr1 + $addr2 = ";
-    }
-
-    my $a1 = substr($addr1,-7);
-    $addr1 = substr($addr1,0,-7);
-    my $a2 = substr($addr2,-7);
-    $addr2 = substr($addr2,0,-7);
-    $sum = hex($a1) + hex($a2);
-    my $c = 0;
-    if ($sum > 0xfffffff) {
-      $c = 1;
-      $sum -= 0x10000000;
-    }
-    my $r = sprintf("%07x", $sum);
-
-    $a1 = substr($addr1,-7);
-    $addr1 = substr($addr1,0,-7);
-    $a2 = substr($addr2,-7);
-    $addr2 = substr($addr2,0,-7);
-    $sum = hex($a1) + hex($a2) + $c;
-    $c = 0;
-    if ($sum > 0xfffffff) {
-      $c = 1;
-      $sum -= 0x10000000;
-    }
-    $r = sprintf("%07x", $sum) . $r;
-
-    $sum = hex($addr1) + hex($addr2) + $c;
-    if ($sum > 0xff) { $sum -= 0x100; }
-    $r = sprintf("%02x", $sum) . $r;
-
-    if ($main::opt_debug and $main::opt_test) { print STDERR "$r\n"; }
-
-    return $r;
-  }
-}
-
-
-# Subtract two hex addresses of length $address_length.
-# Run pprof --test for unit test if this is changed.
-sub AddressSub {
-  my $addr1 = shift;
-  my $addr2 = shift;
-  my $diff;
-
-  if ($address_length == 8) {
-    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
-    $diff = (hex($addr1)-hex($addr2)) % (0x10000000 * 16);
-    return sprintf("%08x", $diff);
-
-  } else {
-    # Do the addition in 7-nibble chunks to trivialize borrow handling.
-    # if ($main::opt_debug) { print STDERR "AddressSub $addr1 - $addr2 = "; }
-
-    my $a1 = hex(substr($addr1,-7));
-    $addr1 = substr($addr1,0,-7);
-    my $a2 = hex(substr($addr2,-7));
-    $addr2 = substr($addr2,0,-7);
-    my $b = 0;
-    if ($a2 > $a1) {
-      $b = 1;
-      $a1 += 0x10000000;
-    }
-    $diff = $a1 - $a2;
-    my $r = sprintf("%07x", $diff);
-
-    $a1 = hex(substr($addr1,-7));
-    $addr1 = substr($addr1,0,-7);
-    $a2 = hex(substr($addr2,-7)) + $b;
-    $addr2 = substr($addr2,0,-7);
-    $b = 0;
-    if ($a2 > $a1) {
-      $b = 1;
-      $a1 += 0x10000000;
-    }
-    $diff = $a1 - $a2;
-    $r = sprintf("%07x", $diff) . $r;
-
-    $a1 = hex($addr1);
-    $a2 = hex($addr2) + $b;
-    if ($a2 > $a1) { $a1 += 0x100; }
-    $diff = $a1 - $a2;
-    $r = sprintf("%02x", $diff) . $r;
-
-    # if ($main::opt_debug) { print STDERR "$r\n"; }
-
-    return $r;
-  }
-}
-
-# Increment a hex addresses of length $address_length.
-# Run pprof --test for unit test if this is changed.
-sub AddressInc {
-  my $addr = shift;
-  my $sum;
-
-  if ($address_length == 8) {
-    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
-    $sum = (hex($addr)+1) % (0x10000000 * 16);
-    return sprintf("%08x", $sum);
-
-  } else {
-    # Do the addition in 7-nibble chunks to trivialize carry handling.
-    # We are always doing this to step through the addresses in a function,
-    # and will almost never overflow the first chunk, so we check for this
-    # case and exit early.
-
-    # if ($main::opt_debug) { print STDERR "AddressInc $addr1 = "; }
-
-    my $a1 = substr($addr,-7);
-    $addr = substr($addr,0,-7);
-    $sum = hex($a1) + 1;
-    my $r = sprintf("%07x", $sum);
-    if ($sum <= 0xfffffff) {
-      $r = $addr . $r;
-      # if ($main::opt_debug) { print STDERR "$r\n"; }
-      return HexExtend($r);
-    } else {
-      $r = "0000000";
-    }
-
-    $a1 = substr($addr,-7);
-    $addr = substr($addr,0,-7);
-    $sum = hex($a1) + 1;
-    $r = sprintf("%07x", $sum) . $r;
-    if ($sum <= 0xfffffff) {
-      $r = $addr . $r;
-      # if ($main::opt_debug) { print STDERR "$r\n"; }
-      return HexExtend($r);
-    } else {
-      $r = "00000000000000";
-    }
-
-    $sum = hex($addr) + 1;
-    if ($sum > 0xff) { $sum -= 0x100; }
-    $r = sprintf("%02x", $sum) . $r;
-
-    # if ($main::opt_debug) { print STDERR "$r\n"; }
-    return $r;
-  }
-}
-
-# Extract symbols for all PC values found in profile
-sub ExtractSymbols {
-  my $libs = shift;
-  my $pcset = shift;
-
-  my $symbols = {};
-
-  # Map each PC value to the containing library.  To make this faster,
-  # we sort libraries by their starting pc value (highest first), and
-  # advance through the libraries as we advance the pc.  Sometimes the
-  # addresses of libraries may overlap with the addresses of the main
-  # binary, so to make sure the libraries 'win', we iterate over the
-  # libraries in reverse order (which assumes the binary doesn't start
-  # in the middle of a library, which seems a fair assumption).
-  my @pcs = (sort { $a cmp $b } keys(%{$pcset}));  # pcset is 0-extended strings
-  foreach my $lib (sort {$b->[1] cmp $a->[1]} @{$libs}) {
-    my $libname = $lib->[0];
-    my $start = $lib->[1];
-    my $finish = $lib->[2];
-    my $offset = $lib->[3];
-
-    # Use debug library if it exists
-    my $debug_libname = DebuggingLibrary($libname);
-    if ($debug_libname) {
-        $libname = $debug_libname;
-    }
-
-    # Get list of pcs that belong in this library.
-    my $contained = [];
-    my ($start_pc_index, $finish_pc_index);
-    # Find smallest finish_pc_index such that $finish < $pc[$finish_pc_index].
-    for ($finish_pc_index = $#pcs + 1; $finish_pc_index > 0;
-         $finish_pc_index--) {
-      last if $pcs[$finish_pc_index - 1] le $finish;
-    }
-    # Find smallest start_pc_index such that $start <= $pc[$start_pc_index].
-    for ($start_pc_index = $finish_pc_index; $start_pc_index > 0;
-         $start_pc_index--) {
-      last if $pcs[$start_pc_index - 1] lt $start;
-    }
-    # This keeps PC values higher than $pc[$finish_pc_index] in @pcs,
-    # in case there are overlaps in libraries and the main binary.
-    @{$contained} = splice(@pcs, $start_pc_index,
-                           $finish_pc_index - $start_pc_index);
-    # Map to symbols
-    MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols);
-  }
-
-  return $symbols;
-}
-
-# Map list of PC values to symbols for a given image
-sub MapToSymbols {
-  my $image = shift;
-  my $offset = shift;
-  my $pclist = shift;
-  my $symbols = shift;
-
-  my $debug = 0;
-
-  # Ignore empty binaries
-  if ($#{$pclist} < 0) { return; }
-
-  # Figure out the addr2line command to use
-  my $addr2line = $obj_tool_map{"addr2line"};
-  my $cmd = ShellEscape($addr2line, "-f", "-C", "-e", $image);
-  if (exists $obj_tool_map{"addr2line_pdb"}) {
-    $addr2line = $obj_tool_map{"addr2line_pdb"};
-    $cmd = ShellEscape($addr2line, "--demangle", "-f", "-C", "-e", $image);
-  }
-
-  # If "addr2line" isn't installed on the system at all, just use
-  # nm to get what info we can (function names, but not line numbers).
-  if (system(ShellEscape($addr2line, "--help") . " >$dev_null 2>&1") != 0) {
-    MapSymbolsWithNM($image, $offset, $pclist, $symbols);
-    return;
-  }
-
-  # "addr2line -i" can produce a variable number of lines per input
-  # address, with no separator that allows us to tell when data for
-  # the next address starts.  So we find the address for a special
-  # symbol (_fini) and interleave this address between all real
-  # addresses passed to addr2line.  The name of this special symbol
-  # can then be used as a separator.
-  $sep_address = undef;  # May be filled in by MapSymbolsWithNM()
-  my $nm_symbols = {};
-  MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols);
-  if (defined($sep_address)) {
-    # Only add " -i" to addr2line if the binary supports it.
-    # addr2line --help returns 0, but not if it sees an unknown flag first.
-    if (system("$cmd -i --help >$dev_null 2>&1") == 0) {
-      $cmd .= " -i";
-    } else {
-      $sep_address = undef;   # no need for sep_address if we don't support -i
-    }
-  }
-
-  # Make file with all PC values with intervening 'sep_address' so
-  # that we can reliably detect the end of inlined function list
-  open(ADDRESSES, ">$main::tmpfile_sym") || error("$main::tmpfile_sym: $!\n");
-  if ($debug) { print("---- $image ---\n"); }
-  for (my $i = 0; $i <= $#{$pclist}; $i++) {
-    # addr2line always reads hex addresses, and does not need '0x' prefix.
-    if ($debug) { printf STDERR ("%s\n", $pclist->[$i]); }
-    printf ADDRESSES ("%s\n", AddressSub($pclist->[$i], $offset));
-    if (defined($sep_address)) {
-      printf ADDRESSES ("%s\n", $sep_address);
-    }
-  }
-  close(ADDRESSES);
-  if ($debug) {
-    print("----\n");
-    system("cat", $main::tmpfile_sym);
-    print("----\n");
-    system("$cmd < " . ShellEscape($main::tmpfile_sym));
-    print("----\n");
-  }
-
-  open(SYMBOLS, "$cmd <" . ShellEscape($main::tmpfile_sym) . " |")
-      || error("$cmd: $!\n");
-  my $count = 0;   # Index in pclist
-  while (<SYMBOLS>) {
-    # Read fullfunction and filelineinfo from next pair of lines
-    s/\r?\n$//g;
-    my $fullfunction = $_;
-    $_ = <SYMBOLS>;
-    s/\r?\n$//g;
-    my $filelinenum = $_;
-
-    if (defined($sep_address) && $fullfunction eq $sep_symbol) {
-      # Terminating marker for data for this address
-      $count++;
-      next;
-    }
-
-    $filelinenum =~ s|\\|/|g; # turn windows-style paths into unix-style paths
-
-    my $pcstr = $pclist->[$count];
-    my $function = ShortFunctionName($fullfunction);
-    my $nms = $nm_symbols->{$pcstr};
-    if (defined($nms)) {
-      if ($fullfunction eq '??') {
-        # nm found a symbol for us.
-        $function = $nms->[0];
-        $fullfunction = $nms->[2];
-      } else {
-       # MapSymbolsWithNM tags each routine with its starting address,
-       # useful in case the image has multiple occurrences of this
-       # routine.  (It uses a syntax that resembles template paramters,
-       # that are automatically stripped out by ShortFunctionName().)
-       # addr2line does not provide the same information.  So we check
-       # if nm disambiguated our symbol, and if so take the annotated
-       # (nm) version of the routine-name.  TODO(csilvers): this won't
-       # catch overloaded, inlined symbols, which nm doesn't see.
-       # Better would be to do a check similar to nm's, in this fn.
-       if ($nms->[2] =~ m/^\Q$function\E/) {  # sanity check it's the right fn
-         $function = $nms->[0];
-         $fullfunction = $nms->[2];
-       }
-      }
-    }
-
-    # Prepend to accumulated symbols for pcstr
-    # (so that caller comes before callee)
-    my $sym = $symbols->{$pcstr};
-    if (!defined($sym)) {
-      $sym = [];
-      $symbols->{$pcstr} = $sym;
-    }
-    unshift(@{$sym}, $function, $filelinenum, $fullfunction);
-    if ($debug) { printf STDERR ("%s => [%s]\n", $pcstr, join(" ", @{$sym})); }
-    if (!defined($sep_address)) {
-      # Inlining is off, so this entry ends immediately
-      $count++;
-    }
-  }
-  close(SYMBOLS);
-}
-
-# Use nm to map the list of referenced PCs to symbols.  Return true iff we
-# are able to read procedure information via nm.
-sub MapSymbolsWithNM {
-  my $image = shift;
-  my $offset = shift;
-  my $pclist = shift;
-  my $symbols = shift;
-
-  # Get nm output sorted by increasing address
-  my $symbol_table = GetProcedureBoundaries($image, ".");
-  if (!%{$symbol_table}) {
-    return 0;
-  }
-  # Start addresses are already the right length (8 or 16 hex digits).
-  my @names = sort { $symbol_table->{$a}->[0] cmp $symbol_table->{$b}->[0] }
-    keys(%{$symbol_table});
-
-  if ($#names < 0) {
-    # No symbols: just use addresses
-    foreach my $pc (@{$pclist}) {
-      my $pcstr = "0x" . $pc;
-      $symbols->{$pc} = [$pcstr, "?", $pcstr];
-    }
-    return 0;
-  }
-
-  # Sort addresses so we can do a join against nm output
-  my $index = 0;
-  my $fullname = $names[0];
-  my $name = ShortFunctionName($fullname);
-  foreach my $pc (sort { $a cmp $b } @{$pclist}) {
-    # Adjust for mapped offset
-    my $mpc = AddressSub($pc, $offset);
-    while (($index < $#names) && ($mpc ge $symbol_table->{$fullname}->[1])){
-      $index++;
-      $fullname = $names[$index];
-      $name = ShortFunctionName($fullname);
-    }
-    if ($mpc lt $symbol_table->{$fullname}->[1]) {
-      $symbols->{$pc} = [$name, "?", $fullname];
-    } else {
-      my $pcstr = "0x" . $pc;
-      $symbols->{$pc} = [$pcstr, "?", $pcstr];
-    }
-  }
-  return 1;
-}
-
-sub ShortFunctionName {
-  my $function = shift;
-  while ($function =~ s/\([^()]*\)(\s*const)?//g) { }   # Argument types
-  while ($function =~ s/<[^<>]*>//g)  { }    # Remove template arguments
-  $function =~ s/^.*\s+(\w+::)/$1/;          # Remove leading type
-  return $function;
-}
-
-# Trim overly long symbols found in disassembler output
-sub CleanDisassembly {
-  my $d = shift;
-  while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
-  while ($d =~ s/(\w+)<[^<>]*>/$1/g)  { }       # Remove template arguments
-  return $d;
-}
-
-# Clean file name for display
-sub CleanFileName {
-  my ($f) = @_;
-  $f =~ s|^/proc/self/cwd/||;
-  $f =~ s|^\./||;
-  return $f;
-}
-
-# Make address relative to section and clean up for display
-sub UnparseAddress {
-  my ($offset, $address) = @_;
-  $address = AddressSub($address, $offset);
-  $address =~ s/^0x//;
-  $address =~ s/^0*//;
-  return $address;
-}
-
-##### Miscellaneous #####
-
-# Find the right versions of the above object tools to use.  The
-# argument is the program file being analyzed, and should be an ELF
-# 32-bit or ELF 64-bit executable file.  The location of the tools
-# is determined by considering the following options in this order:
-#   1) --tools option, if set
-#   2) PPROF_TOOLS environment variable, if set
-#   3) the environment
-sub ConfigureObjTools {
-  my $prog_file = shift;
-
-  # Check for the existence of $prog_file because /usr/bin/file does not
-  # predictably return error status in prod.
-  (-e $prog_file)  || error("$prog_file does not exist.\n");
-
-  my $file_type = undef;
-  if (-e "/usr/bin/file") {
-    # Follow symlinks (at least for systems where "file" supports that).
-    my $escaped_prog_file = ShellEscape($prog_file);
-    $file_type = `/usr/bin/file -L $escaped_prog_file 2>$dev_null ||
-                  /usr/bin/file $escaped_prog_file`;
-  } elsif ($^O == "MSWin32") {
-    $file_type = "MS Windows";
-  } else {
-    print STDERR "WARNING: Can't determine the file type of $prog_file";
-  }
-
-  if ($file_type =~ /64-bit/) {
-    # Change $address_length to 16 if the program file is ELF 64-bit.
-    # We can't detect this from many (most?) heap or lock contention
-    # profiles, since the actual addresses referenced are generally in low
-    # memory even for 64-bit programs.
-    $address_length = 16;
-  }
-
-  if ($file_type =~ /MS Windows/) {
-    # For windows, we provide a version of nm and addr2line as part of
-    # the opensource release, which is capable of parsing
-    # Windows-style PDB executables.  It should live in the path, or
-    # in the same directory as pprof.
-    $obj_tool_map{"nm_pdb"} = "nm-pdb";
-    $obj_tool_map{"addr2line_pdb"} = "addr2line-pdb";
-  }
-
-  if ($file_type =~ /Mach-O/) {
-    # OS X uses otool to examine Mach-O files, rather than objdump.
-    $obj_tool_map{"otool"} = "otool";
-    $obj_tool_map{"addr2line"} = "false";  # no addr2line
-    $obj_tool_map{"objdump"} = "false";  # no objdump
-  }
-
-  # Go fill in %obj_tool_map with the pathnames to use:
-  foreach my $tool (keys %obj_tool_map) {
-    $obj_tool_map{$tool} = ConfigureTool($obj_tool_map{$tool});
-  }
-}
-
-# Returns the path of a caller-specified object tool.  If --tools or
-# PPROF_TOOLS are specified, then returns the full path to the tool
-# with that prefix.  Otherwise, returns the path unmodified (which
-# means we will look for it on PATH).
-sub ConfigureTool {
-  my $tool = shift;
-  my $path;
-
-  # --tools (or $PPROF_TOOLS) is a comma separated list, where each
-  # item is either a) a pathname prefix, or b) a map of the form
-  # <tool>:<path>.  First we look for an entry of type (b) for our
-  # tool.  If one is found, we use it.  Otherwise, we consider all the
-  # pathname prefixes in turn, until one yields an existing file.  If
-  # none does, we use a default path.
-  my $tools = $main::opt_tools || $ENV{"PPROF_TOOLS"} || "";
-  if ($tools =~ m/(,|^)\Q$tool\E:([^,]*)/) {
-    $path = $2;
-    # TODO(csilvers): sanity-check that $path exists?  Hard if it's relative.
-  } elsif ($tools ne '') {
-    foreach my $prefix (split(',', $tools)) {
-      next if ($prefix =~ /:/);    # ignore "tool:fullpath" entries in the list
-      if (-x $prefix . $tool) {
-        $path = $prefix . $tool;
-        last;
-      }
-    }
-    if (!$path) {
-      error("No '$tool' found with prefix specified by " .
-            "--tools (or \$PPROF_TOOLS) '$tools'\n");
-    }
-  } else {
-    # ... otherwise use the version that exists in the same directory as
-    # pprof.  If there's nothing there, use $PATH.
-    $0 =~ m,[^/]*$,;     # this is everything after the last slash
-    my $dirname = $`;    # this is everything up to and including the last slash
-    if (-x "$dirname$tool") {
-      $path = "$dirname$tool";
-    } else {
-      $path = $tool;
-    }
-  }
-  if ($main::opt_debug) { print STDERR "Using '$path' for '$tool'.\n"; }
-  return $path;
-}
-
-sub ShellEscape {
-  my @escaped_words = ();
-  foreach my $word (@_) {
-    my $escaped_word = $word;
-    if ($word =~ m![^a-zA-Z0-9/.,_=-]!) {  # check for anything not in whitelist
-      $escaped_word =~ s/'/'\\''/;
-      $escaped_word = "'$escaped_word'";
-    }
-    push(@escaped_words, $escaped_word);
-  }
-  return join(" ", @escaped_words);
-}
-
-sub cleanup {
-  unlink($main::tmpfile_sym);
-  unlink(keys %main::tempnames);
-
-  # We leave any collected profiles in $HOME/pprof in case the user wants
-  # to look at them later.  We print a message informing them of this.
-  if ((scalar(@main::profile_files) > 0) &&
-      defined($main::collected_profile)) {
-    if (scalar(@main::profile_files) == 1) {
-      print STDERR "Dynamically gathered profile is in $main::collected_profile\n";
-    }
-    print STDERR "If you want to investigate this profile further, you can do:\n";
-    print STDERR "\n";
-    print STDERR "  pprof \\\n";
-    print STDERR "    $main::prog \\\n";
-    print STDERR "    $main::collected_profile\n";
-    print STDERR "\n";
-  }
-}
-
-sub sighandler {
-  cleanup();
-  exit(1);
-}
-
-sub error {
-  my $msg = shift;
-  print STDERR $msg;
-  cleanup();
-  exit(1);
-}
-
-
-# Run $nm_command and get all the resulting procedure boundaries whose
-# names match "$regexp" and returns them in a hashtable mapping from
-# procedure name to a two-element vector of [start address, end address]
-sub GetProcedureBoundariesViaNm {
-  my $escaped_nm_command = shift;    # shell-escaped
-  my $regexp = shift;
-
-  my $symbol_table = {};
-  open(NM, "$escaped_nm_command |") || error("$escaped_nm_command: $!\n");
-  my $last_start = "0";
-  my $routine = "";
-  while (<NM>) {
-    s/\r//g;         # turn windows-looking lines into unix-looking lines
-    if (m/^\s*([0-9a-f]+) (.) (..*)/) {
-      my $start_val = $1;
-      my $type = $2;
-      my $this_routine = $3;
-
-      # It's possible for two symbols to share the same address, if
-      # one is a zero-length variable (like __start_google_malloc) or
-      # one symbol is a weak alias to another (like __libc_malloc).
-      # In such cases, we want to ignore all values except for the
-      # actual symbol, which in nm-speak has type "T".  The logic
-      # below does this, though it's a bit tricky: what happens when
-      # we have a series of lines with the same address, is the first
-      # one gets queued up to be processed.  However, it won't
-      # *actually* be processed until later, when we read a line with
-      # a different address.  That means that as long as we're reading
-      # lines with the same address, we have a chance to replace that
-      # item in the queue, which we do whenever we see a 'T' entry --
-      # that is, a line with type 'T'.  If we never see a 'T' entry,
-      # we'll just go ahead and process the first entry (which never
-      # got touched in the queue), and ignore the others.
-      if ($start_val eq $last_start && $type =~ /t/i) {
-        # We are the 'T' symbol at this address, replace previous symbol.
-        $routine = $this_routine;
-        next;
-      } elsif ($start_val eq $last_start) {
-        # We're not the 'T' symbol at this address, so ignore us.
-        next;
-      }
-
-      if ($this_routine eq $sep_symbol) {
-        $sep_address = HexExtend($start_val);
-      }
-
-      # Tag this routine with the starting address in case the image
-      # has multiple occurrences of this routine.  We use a syntax
-      # that resembles template parameters that are automatically
-      # stripped out by ShortFunctionName()
-      $this_routine .= "<$start_val>";
-
-      if (defined($routine) && $routine =~ m/$regexp/) {
-        $symbol_table->{$routine} = [HexExtend($last_start),
-                                     HexExtend($start_val)];
-      }
-      $last_start = $start_val;
-      $routine = $this_routine;
-    } elsif (m/^Loaded image name: (.+)/) {
-      # The win32 nm workalike emits information about the binary it is using.
-      if ($main::opt_debug) { print STDERR "Using Image $1\n"; }
-    } elsif (m/^PDB file name: (.+)/) {
-      # The win32 nm workalike emits information about the pdb it is using.
-      if ($main::opt_debug) { print STDERR "Using PDB $1\n"; }
-    }
-  }
-  close(NM);
-  # Handle the last line in the nm output.  Unfortunately, we don't know
-  # how big this last symbol is, because we don't know how big the file
-  # is.  For now, we just give it a size of 0.
-  # TODO(csilvers): do better here.
-  if (defined($routine) && $routine =~ m/$regexp/) {
-    $symbol_table->{$routine} = [HexExtend($last_start),
-                                 HexExtend($last_start)];
-  }
-  return $symbol_table;
-}
-
-# Gets the procedure boundaries for all routines in "$image" whose names
-# match "$regexp" and returns them in a hashtable mapping from procedure
-# name to a two-element vector of [start address, end address].
-# Will return an empty map if nm is not installed or not working properly.
-sub GetProcedureBoundaries {
-  my $image = shift;
-  my $regexp = shift;
-
-  # If $image doesn't start with /, then put ./ in front of it.  This works
-  # around an obnoxious bug in our probing of nm -f behavior.
-  # "nm -f $image" is supposed to fail on GNU nm, but if:
-  #
-  # a. $image starts with [BbSsPp] (for example, bin/foo/bar), AND
-  # b. you have a.out in your current directory (a not uncommon occurence)
-  #
-  # then "nm -f $image" succeeds because -f only looks at the first letter of
-  # the argument, which looks valid because it's [BbSsPp], and then since
-  # there's no image provided, it looks for a.out and finds it.
-  #
-  # This regex makes sure that $image starts with . or /, forcing the -f
-  # parsing to fail since . and / are not valid formats.
-  $image =~ s#^[^/]#./$&#;
-
-  # For libc libraries, the copy in /usr/lib/debug contains debugging symbols
-  my $debugging = DebuggingLibrary($image);
-  if ($debugging) {
-    $image = $debugging;
-  }
-
-  my $nm = $obj_tool_map{"nm"};
-  my $cppfilt = $obj_tool_map{"c++filt"};
-
-  # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm
-  # binary doesn't support --demangle.  In addition, for OS X we need
-  # to use the -f flag to get 'flat' nm output (otherwise we don't sort
-  # properly and get incorrect results).  Unfortunately, GNU nm uses -f
-  # in an incompatible way.  So first we test whether our nm supports
-  # --demangle and -f.
-  my $demangle_flag = "";
-  my $cppfilt_flag = "";
-  my $to_devnull = ">$dev_null 2>&1";
-  if (system(ShellEscape($nm, "--demangle", "image") . $to_devnull) == 0) {
-    # In this mode, we do "nm --demangle <foo>"
-    $demangle_flag = "--demangle";
-    $cppfilt_flag = "";
-  } elsif (system(ShellEscape($cppfilt, $image) . $to_devnull) == 0) {
-    # In this mode, we do "nm <foo> | c++filt"
-    $cppfilt_flag = " | " . ShellEscape($cppfilt);
-  };
-  my $flatten_flag = "";
-  if (system(ShellEscape($nm, "-f", $image) . $to_devnull) == 0) {
-    $flatten_flag = "-f";
-  }
-
-  # Finally, in the case $imagie isn't a debug library, we try again with
-  # -D to at least get *exported* symbols.  If we can't use --demangle,
-  # we use c++filt instead, if it exists on this system.
-  my @nm_commands = (ShellEscape($nm, "-n", $flatten_flag, $demangle_flag,
-                                 $image) . " 2>$dev_null $cppfilt_flag",
-                     ShellEscape($nm, "-D", "-n", $flatten_flag, $demangle_flag,
-                                 $image) . " 2>$dev_null $cppfilt_flag",
-                     # 6nm is for Go binaries
-                     ShellEscape("6nm", "$image") . " 2>$dev_null | sort",
-                     );
-
-  # If the executable is an MS Windows PDB-format executable, we'll
-  # have set up obj_tool_map("nm_pdb").  In this case, we actually
-  # want to use both unix nm and windows-specific nm_pdb, since
-  # PDB-format executables can apparently include dwarf .o files.
-  if (exists $obj_tool_map{"nm_pdb"}) {
-    push(@nm_commands,
-         ShellEscape($obj_tool_map{"nm_pdb"}, "--demangle", $image)
-         . " 2>$dev_null");
-  }
-
-  foreach my $nm_command (@nm_commands) {
-    my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp);
-    return $symbol_table if (%{$symbol_table});
-  }
-  my $symbol_table = {};
-  return $symbol_table;
-}
-
-
-# The test vectors for AddressAdd/Sub/Inc are 8-16-nibble hex strings.
-# To make them more readable, we add underscores at interesting places.
-# This routine removes the underscores, producing the canonical representation
-# used by pprof to represent addresses, particularly in the tested routines.
-sub CanonicalHex {
-  my $arg = shift;
-  return join '', (split '_',$arg);
-}
-
-
-# Unit test for AddressAdd:
-sub AddressAddUnitTest {
-  my $test_data_8 = shift;
-  my $test_data_16 = shift;
-  my $error_count = 0;
-  my $fail_count = 0;
-  my $pass_count = 0;
-  # print STDERR "AddressAddUnitTest: ", 1+$#{$test_data_8}, " tests\n";
-
-  # First a few 8-nibble addresses.  Note that this implementation uses
-  # plain old arithmetic, so a quick sanity check along with verifying what
-  # happens to overflow (we want it to wrap):
-  $address_length = 8;
-  foreach my $row (@{$test_data_8}) {
-    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
-    my $sum = AddressAdd ($row->[0], $row->[1]);
-    if ($sum ne $row->[2]) {
-      printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
-             $row->[0], $row->[1], $row->[2];
-      ++$fail_count;
-    } else {
-      ++$pass_count;
-    }
-  }
-  printf STDERR "AddressAdd 32-bit tests: %d passes, %d failures\n",
-         $pass_count, $fail_count;
-  $error_count = $fail_count;
-  $fail_count = 0;
-  $pass_count = 0;
-
-  # Now 16-nibble addresses.
-  $address_length = 16;
-  foreach my $row (@{$test_data_16}) {
-    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
-    my $sum = AddressAdd (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
-    my $expected = join '', (split '_',$row->[2]);
-    if ($sum ne CanonicalHex($row->[2])) {
-      printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
-             $row->[0], $row->[1], $row->[2];
-      ++$fail_count;
-    } else {
-      ++$pass_count;
-    }
-  }
-  printf STDERR "AddressAdd 64-bit tests: %d passes, %d failures\n",
-         $pass_count, $fail_count;
-  $error_count += $fail_count;
-
-  return $error_count;
-}
-
-
-# Unit test for AddressSub:
-sub AddressSubUnitTest {
-  my $test_data_8 = shift;
-  my $test_data_16 = shift;
-  my $error_count = 0;
-  my $fail_count = 0;
-  my $pass_count = 0;
-  # print STDERR "AddressSubUnitTest: ", 1+$#{$test_data_8}, " tests\n";
-
-  # First a few 8-nibble addresses.  Note that this implementation uses
-  # plain old arithmetic, so a quick sanity check along with verifying what
-  # happens to overflow (we want it to wrap):
-  $address_length = 8;
-  foreach my $row (@{$test_data_8}) {
-    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
-    my $sum = AddressSub ($row->[0], $row->[1]);
-    if ($sum ne $row->[3]) {
-      printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
-             $row->[0], $row->[1], $row->[3];
-      ++$fail_count;
-    } else {
-      ++$pass_count;
-    }
-  }
-  printf STDERR "AddressSub 32-bit tests: %d passes, %d failures\n",
-         $pass_count, $fail_count;
-  $error_count = $fail_count;
-  $fail_count = 0;
-  $pass_count = 0;
-
-  # Now 16-nibble addresses.
-  $address_length = 16;
-  foreach my $row (@{$test_data_16}) {
-    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
-    my $sum = AddressSub (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
-    if ($sum ne CanonicalHex($row->[3])) {
-      printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
-             $row->[0], $row->[1], $row->[3];
-      ++$fail_count;
-    } else {
-      ++$pass_count;
-    }
-  }
-  printf STDERR "AddressSub 64-bit tests: %d passes, %d failures\n",
-         $pass_count, $fail_count;
-  $error_count += $fail_count;
-
-  return $error_count;
-}
-
-
-# Unit test for AddressInc:
-sub AddressIncUnitTest {
-  my $test_data_8 = shift;
-  my $test_data_16 = shift;
-  my $error_count = 0;
-  my $fail_count = 0;
-  my $pass_count = 0;
-  # print STDERR "AddressIncUnitTest: ", 1+$#{$test_data_8}, " tests\n";
-
-  # First a few 8-nibble addresses.  Note that this implementation uses
-  # plain old arithmetic, so a quick sanity check along with verifying what
-  # happens to overflow (we want it to wrap):
-  $address_length = 8;
-  foreach my $row (@{$test_data_8}) {
-    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
-    my $sum = AddressInc ($row->[0]);
-    if ($sum ne $row->[4]) {
-      printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
-             $row->[0], $row->[4];
-      ++$fail_count;
-    } else {
-      ++$pass_count;
-    }
-  }
-  printf STDERR "AddressInc 32-bit tests: %d passes, %d failures\n",
-         $pass_count, $fail_count;
-  $error_count = $fail_count;
-  $fail_count = 0;
-  $pass_count = 0;
-
-  # Now 16-nibble addresses.
-  $address_length = 16;
-  foreach my $row (@{$test_data_16}) {
-    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
-    my $sum = AddressInc (CanonicalHex($row->[0]));
-    if ($sum ne CanonicalHex($row->[4])) {
-      printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
-             $row->[0], $row->[4];
-      ++$fail_count;
-    } else {
-      ++$pass_count;
-    }
-  }
-  printf STDERR "AddressInc 64-bit tests: %d passes, %d failures\n",
-         $pass_count, $fail_count;
-  $error_count += $fail_count;
-
-  return $error_count;
-}
-
-
-# Driver for unit tests.
-# Currently just the address add/subtract/increment routines for 64-bit.
-sub RunUnitTests {
-  my $error_count = 0;
-
-  # This is a list of tuples [a, b, a+b, a-b, a+1]
-  my $unit_test_data_8 = [
-    [qw(aaaaaaaa 50505050 fafafafa 5a5a5a5a aaaaaaab)],
-    [qw(50505050 aaaaaaaa fafafafa a5a5a5a6 50505051)],
-    [qw(ffffffff aaaaaaaa aaaaaaa9 55555555 00000000)],
-    [qw(00000001 ffffffff 00000000 00000002 00000002)],
-    [qw(00000001 fffffff0 fffffff1 00000011 00000002)],
-  ];
-  my $unit_test_data_16 = [
-    # The implementation handles data in 7-nibble chunks, so those are the
-    # interesting boundaries.
-    [qw(aaaaaaaa 50505050
-        00_000000f_afafafa 00_0000005_a5a5a5a 00_000000a_aaaaaab)],
-    [qw(50505050 aaaaaaaa
-        00_000000f_afafafa ff_ffffffa_5a5a5a6 00_0000005_0505051)],
-    [qw(ffffffff aaaaaaaa
-        00_000001a_aaaaaa9 00_0000005_5555555 00_0000010_0000000)],
-    [qw(00000001 ffffffff
-        00_0000010_0000000 ff_ffffff0_0000002 00_0000000_0000002)],
-    [qw(00000001 fffffff0
-        00_000000f_ffffff1 ff_ffffff0_0000011 00_0000000_0000002)],
-
-    [qw(00_a00000a_aaaaaaa 50505050
-        00_a00000f_afafafa 00_a000005_a5a5a5a 00_a00000a_aaaaaab)],
-    [qw(0f_fff0005_0505050 aaaaaaaa
-        0f_fff000f_afafafa 0f_ffefffa_5a5a5a6 0f_fff0005_0505051)],
-    [qw(00_000000f_fffffff 01_800000a_aaaaaaa
-        01_800001a_aaaaaa9 fe_8000005_5555555 00_0000010_0000000)],
-    [qw(00_0000000_0000001 ff_fffffff_fffffff
-        00_0000000_0000000 00_0000000_0000002 00_0000000_0000002)],
-    [qw(00_0000000_0000001 ff_fffffff_ffffff0
-        ff_fffffff_ffffff1 00_0000000_0000011 00_0000000_0000002)],
-  ];
-
-  $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16);
-  $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16);
-  $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16);
-  if ($error_count > 0) {
-    print STDERR $error_count, " errors: FAILED\n";
-  } else {
-    print STDERR "PASS\n";
-  }
-  exit ($error_count);
-}
index 5a2b1c91754da3fb4f33a15fe6fcb657e0a4463a..4feae0b4b26b2e484456ced370a2c5357eae96c0 100755 (executable)
@@ -628,12 +628,14 @@ cfghdrs_in
 enable_zone_allocator
 enable_tls
 enable_lazy_lock
+TESTLIBS
 jemalloc_version_gid
 jemalloc_version_nrev
 jemalloc_version_bugfix
 jemalloc_version_minor
 jemalloc_version_major
 jemalloc_version
+enable_cache_oblivious
 enable_xmalloc
 enable_valgrind
 enable_utrace
@@ -646,6 +648,7 @@ enable_debug
 je_
 install_suffix
 private_namespace
+JEMALLOC_CPREFIX
 enable_code_coverage
 AUTOCONF
 LD
@@ -706,6 +709,7 @@ objroot
 abs_srcroot
 srcroot
 rev
+CONFIG
 target_alias
 host_alias
 build_alias
@@ -771,6 +775,12 @@ enable_fill
 enable_utrace
 enable_valgrind
 enable_xmalloc
+enable_cache_oblivious
+with_lg_tiny_min
+with_lg_quantum
+with_lg_page
+with_lg_page_sizes
+with_lg_size_class_group
 enable_lazy_lock
 enable_tls
 enable_zone_allocator
@@ -1412,6 +1422,9 @@ Optional Features:
   --enable-utrace         Enable utrace(2)-based tracing
   --disable-valgrind      Disable support for Valgrind
   --enable-xmalloc        Support xmalloc option
+  --disable-cache-oblivious
+                          Disable support for cache-oblivious allocation
+                          alignment
   --enable-lazy-lock      Enable lazy locking (only lock when multi-threaded)
   --disable-tls           Disable thread-local storage (__thread keyword)
   --disable-zone-allocator
@@ -1433,6 +1446,16 @@ Optional Packages:
   --with-static-libunwind=<libunwind.a>
                           Path to static libunwind library; use rather than
                           dynamically linking
+  --with-lg-tiny-min=<lg-tiny-min>
+                          Base 2 log of minimum tiny size class to support
+  --with-lg-quantum=<lg-quantum>
+                          Base 2 log of minimum allocation alignment
+  --with-lg-page=<lg-page>
+                          Base 2 log of system page size
+  --with-lg-page-sizes=<lg-page-sizes>
+                          Base 2 logs of system page sizes to support
+  --with-lg-size-class-group=<lg-size-class-group>
+                          Base 2 log of size classes per doubling
 
 Some influential environment variables:
   CC          C compiler command
@@ -2467,6 +2490,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
 
+CONFIG=`echo ${ac_configure_args} | sed -e 's#'"'"'\([^ ]*\)'"'"'#\1#g'`
+
+
 rev=2
 
 
@@ -3479,6 +3505,42 @@ fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror=declaration-after-statement" >&5
+$as_echo_n "checking whether compiler supports -Werror=declaration-after-statement... " >&6; }
+TCFLAGS="${CFLAGS}"
+if test "x${CFLAGS}" = "x" ; then
+  CFLAGS="-Werror=declaration-after-statement"
+else
+  CFLAGS="${CFLAGS} -Werror=declaration-after-statement"
+fi
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+
+    return 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  je_cv_cflags_appended=-Werror=declaration-after-statement
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  je_cv_cflags_appended=
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+              CFLAGS="${TCFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -pipe" >&5
 $as_echo_n "checking whether compiler supports -pipe... " >&6; }
 TCFLAGS="${CFLAGS}"
@@ -4653,9 +4715,10 @@ case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
 
 CPU_SPINWAIT=""
 case "${host_cpu}" in
-  i[345]86)
-       ;;
   i686|x86_64)
+       if ${je_cv_pause+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pause instruction is compilable" >&5
 $as_echo_n "checking whether pause instruction is compilable... " >&6; }
@@ -4684,44 +4747,10 @@ fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_pause" >&5
 $as_echo "$je_cv_pause" >&6; }
 
-       if test "x${je_cv_pause}" = "xyes" ; then
-           CPU_SPINWAIT='__asm__ volatile("pause")'
-       fi
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether SSE2 intrinsics is compilable" >&5
-$as_echo_n "checking whether SSE2 intrinsics is compilable... " >&6; }
-if ${je_cv_sse2+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-#include <emmintrin.h>
-
-int
-main ()
-{
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  je_cv_sse2=yes
-else
-  je_cv_sse2=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_sse2" >&5
-$as_echo "$je_cv_sse2" >&6; }
-
-       if test "x${je_cv_sse2}" = "xyes" ; then
-         cat >>confdefs.h <<_ACEOF
-#define HAVE_SSE2
-_ACEOF
 
+       if test "x${je_cv_pause}" = "xyes" ; then
+           CPU_SPINWAIT='__asm__ volatile("pause")'
        fi
        ;;
   powerpc)
@@ -4853,6 +4882,7 @@ fi
 
 
 default_munmap="1"
+maps_coalesce="1"
 case "${host}" in
   *-*-darwin* | *-*-ios*)
        CFLAGS="$CFLAGS"
@@ -4864,7 +4894,7 @@ case "${host}" in
        so="dylib"
        importlib="${so}"
        force_tls="0"
-       DSO_LDFLAGS='-shared -Wl,-dylib_install_name,$(@F)'
+       DSO_LDFLAGS='-shared -Wl,-install_name,$(LIBDIR)/$(@F)'
        SOREV="${rev}.${so}"
        sbrk_deprecated="1"
        ;;
@@ -4881,7 +4911,14 @@ case "${host}" in
        $as_echo "#define JEMALLOC_PURGE_MADVISE_FREE  " >>confdefs.h
 
        ;;
-  *-*-openbsd*|*-*-bitrig*)
+  *-*-openbsd*)
+       CFLAGS="$CFLAGS"
+       abi="elf"
+       $as_echo "#define JEMALLOC_PURGE_MADVISE_FREE  " >>confdefs.h
+
+       force_tls="0"
+       ;;
+  *-*-bitrig*)
        CFLAGS="$CFLAGS"
        abi="elf"
        $as_echo "#define JEMALLOC_PURGE_MADVISE_FREE  " >>confdefs.h
@@ -4897,6 +4934,8 @@ case "${host}" in
 
        $as_echo "#define JEMALLOC_THREADED_INIT  " >>confdefs.h
 
+       $as_echo "#define JEMALLOC_USE_CXX_THROW  " >>confdefs.h
+
        default_munmap="0"
        ;;
   *-*-netbsd*)
@@ -4949,6 +4988,8 @@ $as_echo "$abi" >&6; }
   *-*-mingw* | *-*-cygwin*)
        abi="pecoff"
        force_tls="0"
+       force_lazy_lock="1"
+       maps_coalesce="0"
        RPATH=""
        so="dll"
        if test "x$je_cv_msvc" = "xyes" ; then
@@ -5189,6 +5230,216 @@ else
   $as_echo "#define JEMALLOC_TLS_MODEL  " >>confdefs.h
 
 fi
+SAVED_CFLAGS="${CFLAGS}"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
+$as_echo_n "checking whether compiler supports -Werror... " >&6; }
+TCFLAGS="${CFLAGS}"
+if test "x${CFLAGS}" = "x" ; then
+  CFLAGS="-Werror"
+else
+  CFLAGS="${CFLAGS} -Werror"
+fi
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+
+    return 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  je_cv_cflags_appended=-Werror
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  je_cv_cflags_appended=
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+              CFLAGS="${TCFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether alloc_size attribute is compilable" >&5
+$as_echo_n "checking whether alloc_size attribute is compilable... " >&6; }
+if ${je_cv_alloc_size+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+int
+main ()
+{
+void *foo(size_t size) __attribute__((alloc_size(1)));
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  je_cv_alloc_size=yes
+else
+  je_cv_alloc_size=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_alloc_size" >&5
+$as_echo "$je_cv_alloc_size" >&6; }
+
+CFLAGS="${SAVED_CFLAGS}"
+if test "x${je_cv_alloc_size}" = "xyes" ; then
+  $as_echo "#define JEMALLOC_HAVE_ATTR_ALLOC_SIZE  " >>confdefs.h
+
+fi
+SAVED_CFLAGS="${CFLAGS}"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
+$as_echo_n "checking whether compiler supports -Werror... " >&6; }
+TCFLAGS="${CFLAGS}"
+if test "x${CFLAGS}" = "x" ; then
+  CFLAGS="-Werror"
+else
+  CFLAGS="${CFLAGS} -Werror"
+fi
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+
+    return 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  je_cv_cflags_appended=-Werror
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  je_cv_cflags_appended=
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+              CFLAGS="${TCFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether format(gnu_printf, ...) attribute is compilable" >&5
+$as_echo_n "checking whether format(gnu_printf, ...) attribute is compilable... " >&6; }
+if ${je_cv_format_gnu_printf+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+int
+main ()
+{
+void *foo(const char *format, ...) __attribute__((format(gnu_printf, 1, 2)));
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  je_cv_format_gnu_printf=yes
+else
+  je_cv_format_gnu_printf=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_format_gnu_printf" >&5
+$as_echo "$je_cv_format_gnu_printf" >&6; }
+
+CFLAGS="${SAVED_CFLAGS}"
+if test "x${je_cv_format_gnu_printf}" = "xyes" ; then
+  $as_echo "#define JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF  " >>confdefs.h
+
+fi
+SAVED_CFLAGS="${CFLAGS}"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
+$as_echo_n "checking whether compiler supports -Werror... " >&6; }
+TCFLAGS="${CFLAGS}"
+if test "x${CFLAGS}" = "x" ; then
+  CFLAGS="-Werror"
+else
+  CFLAGS="${CFLAGS} -Werror"
+fi
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+
+    return 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  je_cv_cflags_appended=-Werror
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  je_cv_cflags_appended=
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+              CFLAGS="${TCFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether format(printf, ...) attribute is compilable" >&5
+$as_echo_n "checking whether format(printf, ...) attribute is compilable... " >&6; }
+if ${je_cv_format_printf+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+int
+main ()
+{
+void *foo(const char *format, ...) __attribute__((format(printf, 1, 2)));
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  je_cv_format_printf=yes
+else
+  je_cv_format_printf=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_format_printf" >&5
+$as_echo "$je_cv_format_printf" >&6; }
+
+CFLAGS="${SAVED_CFLAGS}"
+if test "x${je_cv_format_printf}" = "xyes" ; then
+  $as_echo "#define JEMALLOC_HAVE_ATTR_FORMAT_PRINTF  " >>confdefs.h
+
+fi
 
 
 # Check whether --with-rpath was given.
@@ -5637,6 +5888,7 @@ _ACEOF
 fi
 
 
+
 # Check whether --with-export was given.
 if test "${with_export+set}" = set; then :
   withval=$with_export; if test "x$with_export" = "xno"; then
@@ -5777,6 +6029,10 @@ else
 
 fi
 
+if test "x$enable_debug" = "x1" ; then
+  $as_echo "#define JEMALLOC_DEBUG  " >>confdefs.h
+
+fi
 if test "x$enable_debug" = "x1" ; then
   $as_echo "#define JEMALLOC_DEBUG  " >>confdefs.h
 
@@ -6267,6 +6523,11 @@ if test "x$enable_tcache" = "x1" ; then
 fi
 
 
+if test "x${maps_coalesce}" = "x1" ; then
+  $as_echo "#define JEMALLOC_MAPS_COALESCE  " >>confdefs.h
+
+fi
+
 # Check whether --enable-munmap was given.
 if test "${enable_munmap+set}" = set; then :
   enableval=$enable_munmap; if test "x$enable_munmap" = "xno" ; then
@@ -6464,6 +6725,25 @@ if test "x$enable_xmalloc" = "x1" ; then
 fi
 
 
+# Check whether --enable-cache-oblivious was given.
+if test "${enable_cache_oblivious+set}" = set; then :
+  enableval=$enable_cache_oblivious; if test "x$enable_cache_oblivious" = "xno" ; then
+  enable_cache_oblivious="0"
+else
+  enable_cache_oblivious="1"
+fi
+
+else
+  enable_cache_oblivious="1"
+
+fi
+
+if test "x$enable_cache_oblivious" = "x1" ; then
+  $as_echo "#define JEMALLOC_CACHE_OBLIVIOUS  " >>confdefs.h
+
+fi
+
+
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program using __builtin_ffsl is compilable" >&5
 $as_echo_n "checking whether a program using __builtin_ffsl is compilable... " >&6; }
@@ -6554,13 +6834,50 @@ $as_echo "$je_cv_function_ffsl" >&6; }
   fi
 fi
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking STATIC_PAGE_SHIFT" >&5
-$as_echo_n "checking STATIC_PAGE_SHIFT... " >&6; }
-if ${je_cv_static_page_shift+:} false; then :
+
+# Check whether --with-lg_tiny_min was given.
+if test "${with_lg_tiny_min+set}" = set; then :
+  withval=$with_lg_tiny_min; LG_TINY_MIN="$with_lg_tiny_min"
+else
+  LG_TINY_MIN="3"
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define LG_TINY_MIN $LG_TINY_MIN
+_ACEOF
+
+
+
+# Check whether --with-lg_quantum was given.
+if test "${with_lg_quantum+set}" = set; then :
+  withval=$with_lg_quantum; LG_QUANTA="$with_lg_quantum"
+else
+  LG_QUANTA="3 4"
+fi
+
+if test "x$with_lg_quantum" != "x" ; then
+  cat >>confdefs.h <<_ACEOF
+#define LG_QUANTUM $with_lg_quantum
+_ACEOF
+
+fi
+
+
+# Check whether --with-lg_page was given.
+if test "${with_lg_page+set}" = set; then :
+  withval=$with_lg_page; LG_PAGE="$with_lg_page"
+else
+  LG_PAGE="detect"
+fi
+
+if test "x$LG_PAGE" = "xdetect"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking LG_PAGE" >&5
+$as_echo_n "checking LG_PAGE... " >&6; }
+if ${je_cv_lg_page+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   if test "$cross_compiling" = yes; then :
-  je_cv_static_page_shift=12
+  je_cv_lg_page=12
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
@@ -6606,51 +6923,76 @@ main ()
 }
 _ACEOF
 if ac_fn_c_try_run "$LINENO"; then :
-  je_cv_static_page_shift=`cat conftest.out`
+  je_cv_lg_page=`cat conftest.out`
 else
-  je_cv_static_page_shift=undefined
+  je_cv_lg_page=undefined
 fi
 rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
   conftest.$ac_objext conftest.beam conftest.$ac_ext
 fi
 
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_static_page_shift" >&5
-$as_echo "$je_cv_static_page_shift" >&6; }
-
-if test "x$je_cv_static_page_shift" != "xundefined"; then
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_lg_page" >&5
+$as_echo "$je_cv_lg_page" >&6; }
+fi
+if test "x${je_cv_lg_page}" != "x" ; then
+  LG_PAGE="${je_cv_lg_page}"
+fi
+if test "x${LG_PAGE}" != "xundefined" ; then
    cat >>confdefs.h <<_ACEOF
-#define STATIC_PAGE_SHIFT $je_cv_static_page_shift
+#define LG_PAGE $LG_PAGE
 _ACEOF
 
 else
-   as_fn_error $? "cannot determine value for STATIC_PAGE_SHIFT" "$LINENO" 5
+   as_fn_error $? "cannot determine value for LG_PAGE" "$LINENO" 5
+fi
+
+
+# Check whether --with-lg_page_sizes was given.
+if test "${with_lg_page_sizes+set}" = set; then :
+  withval=$with_lg_page_sizes; LG_PAGE_SIZES="$with_lg_page_sizes"
+else
+  LG_PAGE_SIZES="$LG_PAGE"
+fi
+
+
+
+# Check whether --with-lg_size_class_group was given.
+if test "${with_lg_size_class_group+set}" = set; then :
+  withval=$with_lg_size_class_group; LG_SIZE_CLASS_GROUP="$with_lg_size_class_group"
+else
+  LG_SIZE_CLASS_GROUP="2"
 fi
 
 
-if test "x`git rev-parse --is-inside-work-tree 2>/dev/null`" = "xtrue" ; then
-        rm -f "${srcroot}VERSION"
+
+if test "x`test ! \"${srcroot}\" && cd \"${srcroot}\"; git rev-parse --is-inside-work-tree 2>/dev/null`" = "xtrue" ; then
+        rm -f "${objroot}VERSION"
   for pattern in '[0-9].[0-9].[0-9]' '[0-9].[0-9].[0-9][0-9]' \
                  '[0-9].[0-9][0-9].[0-9]' '[0-9].[0-9][0-9].[0-9][0-9]' \
                  '[0-9][0-9].[0-9].[0-9]' '[0-9][0-9].[0-9].[0-9][0-9]' \
                  '[0-9][0-9].[0-9][0-9].[0-9]' \
                  '[0-9][0-9].[0-9][0-9].[0-9][0-9]'; do
-    if test ! -e "${srcroot}VERSION" ; then
-      git describe --long --abbrev=40 --match="${pattern}" > "${srcroot}VERSION.tmp" 2>/dev/null
+    if test ! -e "${objroot}VERSION" ; then
+      (test ! "${srcroot}" && cd "${srcroot}"; git describe --long --abbrev=40 --match="${pattern}") > "${objroot}VERSION.tmp" 2>/dev/null
       if test $? -eq 0 ; then
-        mv "${srcroot}VERSION.tmp" "${srcroot}VERSION"
+        mv "${objroot}VERSION.tmp" "${objroot}VERSION"
         break
       fi
     fi
   done
 fi
-rm -f "${srcroot}VERSION.tmp"
-if test ! -e "${srcroot}VERSION" ; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: Missing VERSION file, and unable to generate it; creating bogus VERSION" >&5
+rm -f "${objroot}VERSION.tmp"
+if test ! -e "${objroot}VERSION" ; then
+  if test ! -e "${srcroot}VERSION" ; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: Missing VERSION file, and unable to generate it; creating bogus VERSION" >&5
 $as_echo "Missing VERSION file, and unable to generate it; creating bogus VERSION" >&6; }
-  echo "0.0.0-0-g0000000000000000000000000000000000000000" > "${srcroot}VERSION"
+    echo "0.0.0-0-g0000000000000000000000000000000000000000" > "${objroot}VERSION"
+  else
+    cp ${srcroot}VERSION ${objroot}VERSION
+  fi
 fi
-jemalloc_version=`cat "${srcroot}VERSION"`
+jemalloc_version=`cat "${objroot}VERSION"`
 jemalloc_version_major=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print $1}'`
 jemalloc_version_minor=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print $2}'`
 jemalloc_version_bugfix=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print $3}'`
@@ -6782,6 +7124,93 @@ fi
 
 CPPFLAGS="$CPPFLAGS -D_REENTRANT"
 
+SAVED_LIBS="${LIBS}"
+LIBS=
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
+$as_echo_n "checking for library containing clock_gettime... " >&6; }
+if ${ac_cv_search_clock_gettime+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' rt; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_clock_gettime=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_clock_gettime+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_clock_gettime+:} false; then :
+
+else
+  ac_cv_search_clock_gettime=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
+$as_echo "$ac_cv_search_clock_gettime" >&6; }
+ac_res=$ac_cv_search_clock_gettime
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+  TESTLIBS="${LIBS}"
+fi
+
+
+LIBS="${SAVED_LIBS}"
+
+ac_fn_c_check_func "$LINENO" "secure_getenv" "ac_cv_func_secure_getenv"
+if test "x$ac_cv_func_secure_getenv" = xyes; then :
+  have_secure_getenv="1"
+else
+  have_secure_getenv="0"
+
+fi
+
+if test "x$have_secure_getenv" = "x1" ; then
+  $as_echo "#define JEMALLOC_HAVE_SECURE_GETENV  " >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "issetugid" "ac_cv_func_issetugid"
+if test "x$ac_cv_func_issetugid" = xyes; then :
+  have_issetugid="1"
+else
+  have_issetugid="0"
+
+fi
+
+if test "x$have_issetugid" = "x1" ; then
+  $as_echo "#define JEMALLOC_HAVE_ISSETUGID  " >>confdefs.h
+
+fi
+
 ac_fn_c_check_func "$LINENO" "_malloc_thread_cleanup" "ac_cv_func__malloc_thread_cleanup"
 if test "x$ac_cv_func__malloc_thread_cleanup" = xyes; then :
   have__malloc_thread_cleanup="1"
@@ -6818,11 +7247,11 @@ else
 fi
 
 else
-  enable_lazy_lock="0"
+  enable_lazy_lock=""
 
 fi
 
-if test "x$enable_lazy_lock" = "x0" -a "x${force_lazy_lock}" = "x1" ; then
+if test "x$enable_lazy_lock" = "x" -a "x${force_lazy_lock}" = "x1" ; then
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: Forcing lazy-lock to avoid allocator/threading bootstrap issues" >&5
 $as_echo "Forcing lazy-lock to avoid allocator/threading bootstrap issues" >&6; }
   enable_lazy_lock="1"
@@ -6895,6 +7324,8 @@ fi
   fi
   $as_echo "#define JEMALLOC_LAZY_LOCK  " >>confdefs.h
 
+else
+  enable_lazy_lock="0"
 fi
 
 
@@ -6907,19 +7338,22 @@ else
 fi
 
 else
-  enable_tls="1"
+  enable_tls=""
 
 fi
 
-if test "x${enable_tls}" = "x0" -a "x${force_tls}" = "x1" ; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: Forcing TLS to avoid allocator/threading bootstrap issues" >&5
+if test "x${enable_tls}" = "x" ; then
+  if test "x${force_tls}" = "x1" ; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: Forcing TLS to avoid allocator/threading bootstrap issues" >&5
 $as_echo "Forcing TLS to avoid allocator/threading bootstrap issues" >&6; }
-  enable_tls="1"
-fi
-if test "x${enable_tls}" = "x1" -a "x${force_tls}" = "x0" ; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: Forcing no TLS to avoid allocator/threading bootstrap issues" >&5
+    enable_tls="1"
+  elif test "x${force_tls}" = "x0" ; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: Forcing no TLS to avoid allocator/threading bootstrap issues" >&5
 $as_echo "Forcing no TLS to avoid allocator/threading bootstrap issues" >&6; }
-  enable_tls="0"
+    enable_tls="0"
+  else
+    enable_tls="1"
+  fi
 fi
 if test "x${enable_tls}" = "x1" ; then
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for TLS" >&5
@@ -6950,15 +7384,69 @@ $as_echo "no" >&6; }
               enable_tls="0"
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  enable_tls="0"
 fi
 
 if test "x${enable_tls}" = "x1" ; then
+  if test "x${force_tls}" = "x0" ; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: TLS enabled despite being marked unusable on this platform" >&5
+$as_echo "$as_me: WARNING: TLS enabled despite being marked unusable on this platform" >&2;}
+  fi
   cat >>confdefs.h <<_ACEOF
 #define JEMALLOC_TLS
 _ACEOF
 
 elif test "x${force_tls}" = "x1" ; then
-  as_fn_error $? "Failed to configure TLS, which is mandatory for correct function" "$LINENO" 5
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: TLS disabled despite being marked critical on this platform" >&5
+$as_echo "$as_me: WARNING: TLS disabled despite being marked critical on this platform" >&2;}
+fi
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C11 atomics is compilable" >&5
+$as_echo_n "checking whether C11 atomics is compilable... " >&6; }
+if ${je_cv_c11atomics+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <stdint.h>
+#if (__STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_ATOMICS__)
+#include <stdatomic.h>
+#else
+#error Atomics not available
+#endif
+
+int
+main ()
+{
+
+    uint64_t *p = (uint64_t *)0;
+    uint64_t x = 1;
+    volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p;
+    uint64_t r = atomic_fetch_add(a, x) + x;
+    return (r == 0);
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  je_cv_c11atomics=yes
+else
+  je_cv_c11atomics=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_c11atomics" >&5
+$as_echo "$je_cv_c11atomics" >&6; }
+
+if test "x${je_cv_c11atomics}" = "xyes" ; then
+  $as_echo "#define JEMALLOC_C11ATOMICS 1" >>confdefs.h
+
 fi
 
 
@@ -7300,8 +7788,6 @@ if test "x${enable_zone_allocator}" = "x1" ; then
   if test "x${abi}" != "xmacho"; then
     as_fn_error $? "--enable-zone-allocator is only supported on Darwin" "$LINENO" 5
   fi
-  $as_echo "#define JEMALLOC_IVSALLOC  " >>confdefs.h
-
   $as_echo "#define JEMALLOC_ZONE  " >>confdefs.h
 
 
@@ -7315,7 +7801,7 @@ $as_echo_n "checking malloc zone version... " >&6; }
 int
 main ()
 {
-static foo[sizeof(malloc_zone_t) == sizeof(void *) * 14 ? 1 : -1]
+static int foo[sizeof(malloc_zone_t) == sizeof(void *) * 14 ? 1 : -1]
 
   ;
   return 0;
@@ -7331,7 +7817,7 @@ else
 int
 main ()
 {
-static foo[sizeof(malloc_zone_t) == sizeof(void *) * 15 ? 1 : -1]
+static int foo[sizeof(malloc_zone_t) == sizeof(void *) * 15 ? 1 : -1]
 
   ;
   return 0;
@@ -7347,7 +7833,7 @@ else
 int
 main ()
 {
-static foo[sizeof(malloc_zone_t) == sizeof(void *) * 16 ? 1 : -1]
+static int foo[sizeof(malloc_zone_t) == sizeof(void *) * 16 ? 1 : -1]
 
   ;
   return 0;
@@ -7361,7 +7847,7 @@ if ac_fn_c_try_compile "$LINENO"; then :
 int
 main ()
 {
-static foo[sizeof(malloc_introspection_t) == sizeof(void *) * 9 ? 1 : -1]
+static int foo[sizeof(malloc_introspection_t) == sizeof(void *) * 9 ? 1 : -1]
 
   ;
   return 0;
@@ -7377,7 +7863,7 @@ else
 int
 main ()
 {
-static foo[sizeof(malloc_introspection_t) == sizeof(void *) * 13 ? 1 : -1]
+static int foo[sizeof(malloc_introspection_t) == sizeof(void *) * 13 ? 1 : -1]
 
   ;
   return 0;
@@ -7400,7 +7886,7 @@ else
 int
 main ()
 {
-static foo[sizeof(malloc_zone_t) == sizeof(void *) * 17 ? 1 : -1]
+static int foo[sizeof(malloc_zone_t) == sizeof(void *) * 17 ? 1 : -1]
 
   ;
   return 0;
@@ -7416,7 +7902,7 @@ else
 int
 main ()
 {
-static foo[sizeof(malloc_zone_t) > sizeof(void *) * 17 ? 1 : -1]
+static int foo[sizeof(malloc_zone_t) > sizeof(void *) * 17 ? 1 : -1]
 
   ;
   return 0;
@@ -7705,7 +8191,7 @@ ac_config_headers="$ac_config_headers $cfghdrs_tup"
 
 
 
-ac_config_files="$ac_config_files $cfgoutputs_tup config.stamp bin/jemalloc.sh"
+ac_config_files="$ac_config_files $cfgoutputs_tup config.stamp bin/jemalloc-config bin/jemalloc.sh bin/jeprof"
 
 
 
@@ -8423,8 +8909,13 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
   objroot="${objroot}"
 
 
+  SHELL="${SHELL}"
   srcdir="${srcdir}"
   objroot="${objroot}"
+  LG_QUANTA="${LG_QUANTA}"
+  LG_TINY_MIN=${LG_TINY_MIN}
+  LG_PAGE_SIZES="${LG_PAGE_SIZES}"
+  LG_SIZE_CLASS_GROUP=${LG_SIZE_CLASS_GROUP}
 
 
   srcdir="${srcdir}"
@@ -8470,7 +8961,9 @@ do
     "$cfghdrs_tup") CONFIG_HEADERS="$CONFIG_HEADERS $cfghdrs_tup" ;;
     "$cfgoutputs_tup") CONFIG_FILES="$CONFIG_FILES $cfgoutputs_tup" ;;
     "config.stamp") CONFIG_FILES="$CONFIG_FILES config.stamp" ;;
+    "bin/jemalloc-config") CONFIG_FILES="$CONFIG_FILES bin/jemalloc-config" ;;
     "bin/jemalloc.sh") CONFIG_FILES="$CONFIG_FILES bin/jemalloc.sh" ;;
+    "bin/jeprof") CONFIG_FILES="$CONFIG_FILES bin/jeprof" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
   esac
@@ -9060,7 +9553,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
  ;;
     "include/jemalloc/internal/size_classes.h":C)
   mkdir -p "${objroot}include/jemalloc/internal"
-  "${srcdir}/include/jemalloc/internal/size_classes.sh" > "${objroot}include/jemalloc/internal/size_classes.h"
+  "${SHELL}" "${srcdir}/include/jemalloc/internal/size_classes.sh" "${LG_QUANTA}" ${LG_TINY_MIN} "${LG_PAGE_SIZES}" ${LG_SIZE_CLASS_GROUP} > "${objroot}include/jemalloc/internal/size_classes.h"
  ;;
     "include/jemalloc/jemalloc_protos_jet.h":C)
   mkdir -p "${objroot}include/jemalloc"
@@ -9129,18 +9622,22 @@ $as_echo "jemalloc version   : ${jemalloc_version}" >&6; }
 $as_echo "library revision   : ${rev}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5
 $as_echo "" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: CONFIG             : ${CONFIG}" >&5
+$as_echo "CONFIG             : ${CONFIG}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: CC                 : ${CC}" >&5
 $as_echo "CC                 : ${CC}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: CPPFLAGS           : ${CPPFLAGS}" >&5
-$as_echo "CPPFLAGS           : ${CPPFLAGS}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: CFLAGS             : ${CFLAGS}" >&5
 $as_echo "CFLAGS             : ${CFLAGS}" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: CPPFLAGS           : ${CPPFLAGS}" >&5
+$as_echo "CPPFLAGS           : ${CPPFLAGS}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: LDFLAGS            : ${LDFLAGS}" >&5
 $as_echo "LDFLAGS            : ${LDFLAGS}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: EXTRA_LDFLAGS      : ${EXTRA_LDFLAGS}" >&5
 $as_echo "EXTRA_LDFLAGS      : ${EXTRA_LDFLAGS}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: LIBS               : ${LIBS}" >&5
 $as_echo "LIBS               : ${LIBS}" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: TESTLIBS           : ${TESTLIBS}" >&5
+$as_echo "TESTLIBS           : ${TESTLIBS}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: RPATH_EXTRA        : ${RPATH_EXTRA}" >&5
 $as_echo "RPATH_EXTRA        : ${RPATH_EXTRA}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5
@@ -9155,12 +9652,12 @@ $as_echo "" >&6; }
 $as_echo "PREFIX             : ${PREFIX}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: BINDIR             : ${BINDIR}" >&5
 $as_echo "BINDIR             : ${BINDIR}" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: DATADIR            : ${DATADIR}" >&5
+$as_echo "DATADIR            : ${DATADIR}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: INCLUDEDIR         : ${INCLUDEDIR}" >&5
 $as_echo "INCLUDEDIR         : ${INCLUDEDIR}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: LIBDIR             : ${LIBDIR}" >&5
 $as_echo "LIBDIR             : ${LIBDIR}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: DATADIR            : ${DATADIR}" >&5
-$as_echo "DATADIR            : ${DATADIR}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: MANDIR             : ${MANDIR}" >&5
 $as_echo "MANDIR             : ${MANDIR}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5
@@ -9217,5 +9714,7 @@ $as_echo "munmap             : ${enable_munmap}" >&6; }
 $as_echo "lazy_lock          : ${enable_lazy_lock}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: tls                : ${enable_tls}" >&5
 $as_echo "tls                : ${enable_tls}" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: cache-oblivious    : ${enable_cache_oblivious}" >&5
+$as_echo "cache-oblivious    : ${enable_cache_oblivious}" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: ===============================================================================" >&5
 $as_echo "===============================================================================" >&6; }
index fd7554d668e94d52e61ab6034dc8066e1be03cea..7a1290e0db917f7d52b5d4db02953093c78412cc 100644 (file)
@@ -43,6 +43,9 @@ AC_CACHE_CHECK([whether $1 is compilable],
 
 dnl ============================================================================
 
+CONFIG=`echo ${ac_configure_args} | sed -e 's#'"'"'\([^ ]*\)'"'"'#\1#g'`
+AC_SUBST([CONFIG])
+
 dnl Library revision.
 rev=2
 AC_SUBST([rev])
@@ -134,6 +137,7 @@ if test "x$CFLAGS" = "x" ; then
       AC_DEFINE_UNQUOTED([JEMALLOC_HAS_RESTRICT])
     fi
     JE_CFLAGS_APPEND([-Wall])
+    JE_CFLAGS_APPEND([-Werror=declaration-after-statement])
     JE_CFLAGS_APPEND([-pipe])
     JE_CFLAGS_APPEND([-g3])
   elif test "x$je_cv_msvc" = "xyes" ; then
@@ -206,23 +210,14 @@ AC_CANONICAL_HOST
 dnl CPU-specific settings.
 CPU_SPINWAIT=""
 case "${host_cpu}" in
-  i[[345]]86)
-       ;;
   i686|x86_64)
-       JE_COMPILABLE([pause instruction], [],
-                     [[__asm__ volatile("pause"); return 0;]],
-                     [je_cv_pause])
+       AC_CACHE_VAL([je_cv_pause],
+         [JE_COMPILABLE([pause instruction], [],
+                       [[__asm__ volatile("pause"); return 0;]],
+                       [je_cv_pause])])
        if test "x${je_cv_pause}" = "xyes" ; then
            CPU_SPINWAIT='__asm__ volatile("pause")'
        fi
-       dnl emmintrin.h fails to compile unless MMX, SSE, and SSE2 are
-       dnl supported.
-       JE_COMPILABLE([SSE2 intrinsics], [
-#include <emmintrin.h>
-], [], [je_cv_sse2])
-       if test "x${je_cv_sse2}" = "xyes" ; then
-         AC_DEFINE_UNQUOTED([HAVE_SSE2], [ ])
-       fi
        ;;
   powerpc)
        AC_DEFINE_UNQUOTED([HAVE_ALTIVEC], [ ])
@@ -263,6 +258,7 @@ dnl Define cpp macros in CPPFLAGS, rather than doing AC_DEFINE(macro), since the
 dnl definitions need to be seen before any headers are included, which is a pain
 dnl to make happen otherwise.
 default_munmap="1"
+maps_coalesce="1"
 case "${host}" in
   *-*-darwin* | *-*-ios*)
        CFLAGS="$CFLAGS"
@@ -273,7 +269,7 @@ case "${host}" in
        so="dylib"
        importlib="${so}"
        force_tls="0"
-       DSO_LDFLAGS='-shared -Wl,-dylib_install_name,$(@F)'
+       DSO_LDFLAGS='-shared -Wl,-install_name,$(LIBDIR)/$(@F)'
        SOREV="${rev}.${so}"
        sbrk_deprecated="1"
        ;;
@@ -288,7 +284,13 @@ case "${host}" in
        abi="elf"
        AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])
        ;;
-  *-*-openbsd*|*-*-bitrig*)
+  *-*-openbsd*)
+       CFLAGS="$CFLAGS"
+       abi="elf"
+       AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])
+       force_tls="0"
+       ;;
+  *-*-bitrig*)
        CFLAGS="$CFLAGS"
        abi="elf"
        AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ])
@@ -300,6 +302,7 @@ case "${host}" in
        AC_DEFINE([JEMALLOC_HAS_ALLOCA_H])
        AC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED], [ ])
        AC_DEFINE([JEMALLOC_THREADED_INIT], [ ])
+       AC_DEFINE([JEMALLOC_USE_CXX_THROW], [ ])
        default_munmap="0"
        ;;
   *-*-netbsd*)
@@ -338,6 +341,8 @@ case "${host}" in
   *-*-mingw* | *-*-cygwin*)
        abi="pecoff"
        force_tls="0"
+       force_lazy_lock="1"
+       maps_coalesce="0"
        RPATH=""
        so="dll"
        if test "x$je_cv_msvc" = "xyes" ; then
@@ -426,6 +431,36 @@ if test "x${je_cv_tls_model}" = "xyes" ; then
 else
   AC_DEFINE([JEMALLOC_TLS_MODEL], [ ])
 fi
+dnl Check for alloc_size attribute support.
+SAVED_CFLAGS="${CFLAGS}"
+JE_CFLAGS_APPEND([-Werror])
+JE_COMPILABLE([alloc_size attribute], [#include <stdlib.h>],
+              [void *foo(size_t size) __attribute__((alloc_size(1)));],
+              [je_cv_alloc_size])
+CFLAGS="${SAVED_CFLAGS}"
+if test "x${je_cv_alloc_size}" = "xyes" ; then
+  AC_DEFINE([JEMALLOC_HAVE_ATTR_ALLOC_SIZE], [ ])
+fi
+dnl Check for format(gnu_printf, ...) attribute support.
+SAVED_CFLAGS="${CFLAGS}"
+JE_CFLAGS_APPEND([-Werror])
+JE_COMPILABLE([format(gnu_printf, ...) attribute], [#include <stdlib.h>],
+              [void *foo(const char *format, ...) __attribute__((format(gnu_printf, 1, 2)));],
+              [je_cv_format_gnu_printf])
+CFLAGS="${SAVED_CFLAGS}"
+if test "x${je_cv_format_gnu_printf}" = "xyes" ; then
+  AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF], [ ])
+fi
+dnl Check for format(printf, ...) attribute support.
+SAVED_CFLAGS="${CFLAGS}"
+JE_CFLAGS_APPEND([-Werror])
+JE_COMPILABLE([format(printf, ...) attribute], [#include <stdlib.h>],
+              [void *foo(const char *format, ...) __attribute__((format(printf, 1, 2)));],
+              [je_cv_format_printf])
+CFLAGS="${SAVED_CFLAGS}"
+if test "x${je_cv_format_printf}" = "xyes" ; then
+  AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_PRINTF], [ ])
+fi
 
 dnl Support optional additions to rpath.
 AC_ARG_WITH([rpath],
@@ -512,6 +547,7 @@ if test "x$JEMALLOC_PREFIX" != "x" ; then
   AC_DEFINE_UNQUOTED([JEMALLOC_PREFIX], ["$JEMALLOC_PREFIX"])
   AC_DEFINE_UNQUOTED([JEMALLOC_CPREFIX], ["$JEMALLOC_CPREFIX"])
 fi
+AC_SUBST([JEMALLOC_CPREFIX])
 
 AC_ARG_WITH([export],
   [AS_HELP_STRING([--without-export], [disable exporting jemalloc public APIs])],
@@ -630,7 +666,8 @@ fi
 
 dnl Do not compile with debugging by default.
 AC_ARG_ENABLE([debug],
-  [AS_HELP_STRING([--enable-debug], [Build debugging code (implies --enable-ivsalloc)])],
+  [AS_HELP_STRING([--enable-debug],
+                  [Build debugging code (implies --enable-ivsalloc)])],
 [if test "x$enable_debug" = "xno" ; then
   enable_debug="0"
 else
@@ -639,6 +676,9 @@ fi
 ],
 [enable_debug="0"]
 )
+if test "x$enable_debug" = "x1" ; then
+  AC_DEFINE([JEMALLOC_DEBUG], [ ])
+fi
 if test "x$enable_debug" = "x1" ; then
   AC_DEFINE([JEMALLOC_DEBUG], [ ])
   enable_ivsalloc="1"
@@ -647,7 +687,8 @@ AC_SUBST([enable_debug])
 
 dnl Do not validate pointers by default.
 AC_ARG_ENABLE([ivsalloc],
-  [AS_HELP_STRING([--enable-ivsalloc], [Validate pointers passed through the public API])],
+  [AS_HELP_STRING([--enable-ivsalloc],
+                  [Validate pointers passed through the public API])],
 [if test "x$enable_ivsalloc" = "xno" ; then
   enable_ivsalloc="0"
 else
@@ -823,6 +864,12 @@ if test "x$enable_tcache" = "x1" ; then
 fi
 AC_SUBST([enable_tcache])
 
+dnl Indicate whether adjacent virtual memory mappings automatically coalesce
+dnl (and fragment on demand).
+if test "x${maps_coalesce}" = "x1" ; then
+  AC_DEFINE([JEMALLOC_MAPS_COALESCE], [ ])
+fi
+
 dnl Enable VM deallocation via munmap() by default.
 AC_ARG_ENABLE([munmap],
   [AS_HELP_STRING([--disable-munmap], [Disable VM deallocation via munmap(2)])],
@@ -946,6 +993,23 @@ if test "x$enable_xmalloc" = "x1" ; then
 fi
 AC_SUBST([enable_xmalloc])
 
+dnl Support cache-oblivious allocation alignment by default.
+AC_ARG_ENABLE([cache-oblivious],
+  [AS_HELP_STRING([--disable-cache-oblivious],
+                  [Disable support for cache-oblivious allocation alignment])],
+[if test "x$enable_cache_oblivious" = "xno" ; then
+  enable_cache_oblivious="0"
+else
+  enable_cache_oblivious="1"
+fi
+],
+[enable_cache_oblivious="1"]
+)
+if test "x$enable_cache_oblivious" = "x1" ; then
+  AC_DEFINE([JEMALLOC_CACHE_OBLIVIOUS], [ ])
+fi
+AC_SUBST([enable_cache_oblivious])
+
 dnl ============================================================================
 dnl Check for  __builtin_ffsl(), then ffsl(3), and fail if neither are found.
 dnl One of those two functions should (theoretically) exist on all platforms
@@ -984,8 +1048,28 @@ else
   fi
 fi
 
-AC_CACHE_CHECK([STATIC_PAGE_SHIFT],
-               [je_cv_static_page_shift],
+AC_ARG_WITH([lg_tiny_min],
+  [AS_HELP_STRING([--with-lg-tiny-min=<lg-tiny-min>],
+   [Base 2 log of minimum tiny size class to support])],
+  [LG_TINY_MIN="$with_lg_tiny_min"],
+  [LG_TINY_MIN="3"])
+AC_DEFINE_UNQUOTED([LG_TINY_MIN], [$LG_TINY_MIN])
+
+AC_ARG_WITH([lg_quantum],
+  [AS_HELP_STRING([--with-lg-quantum=<lg-quantum>],
+   [Base 2 log of minimum allocation alignment])],
+  [LG_QUANTA="$with_lg_quantum"],
+  [LG_QUANTA="3 4"])
+if test "x$with_lg_quantum" != "x" ; then
+  AC_DEFINE_UNQUOTED([LG_QUANTUM], [$with_lg_quantum])
+fi
+
+AC_ARG_WITH([lg_page],
+  [AS_HELP_STRING([--with-lg-page=<lg-page>], [Base 2 log of system page size])],
+  [LG_PAGE="$with_lg_page"], [LG_PAGE="detect"])
+if test "x$LG_PAGE" = "xdetect"; then
+  AC_CACHE_CHECK([LG_PAGE],
+               [je_cv_lg_page],
                AC_RUN_IFELSE([AC_LANG_PROGRAM(
 [[
 #include <strings.h>
@@ -1021,47 +1105,65 @@ AC_CACHE_CHECK([STATIC_PAGE_SHIFT],
 
     return 0;
 ]])],
-                             [je_cv_static_page_shift=`cat conftest.out`],
-                             [je_cv_static_page_shift=undefined],
-                             [je_cv_static_page_shift=12]))
-
-if test "x$je_cv_static_page_shift" != "xundefined"; then
-   AC_DEFINE_UNQUOTED([STATIC_PAGE_SHIFT], [$je_cv_static_page_shift])
+                             [je_cv_lg_page=`cat conftest.out`],
+                             [je_cv_lg_page=undefined],
+                             [je_cv_lg_page=12]))
+fi
+if test "x${je_cv_lg_page}" != "x" ; then
+  LG_PAGE="${je_cv_lg_page}"
+fi
+if test "x${LG_PAGE}" != "xundefined" ; then
+   AC_DEFINE_UNQUOTED([LG_PAGE], [$LG_PAGE])
 else
-   AC_MSG_ERROR([cannot determine value for STATIC_PAGE_SHIFT])
+   AC_MSG_ERROR([cannot determine value for LG_PAGE])
 fi
 
+AC_ARG_WITH([lg_page_sizes],
+  [AS_HELP_STRING([--with-lg-page-sizes=<lg-page-sizes>],
+   [Base 2 logs of system page sizes to support])],
+  [LG_PAGE_SIZES="$with_lg_page_sizes"], [LG_PAGE_SIZES="$LG_PAGE"])
+
+AC_ARG_WITH([lg_size_class_group],
+  [AS_HELP_STRING([--with-lg-size-class-group=<lg-size-class-group>],
+   [Base 2 log of size classes per doubling])],
+  [LG_SIZE_CLASS_GROUP="$with_lg_size_class_group"],
+  [LG_SIZE_CLASS_GROUP="2"])
+
 dnl ============================================================================
 dnl jemalloc configuration.
 dnl 
 
 dnl Set VERSION if source directory is inside a git repository.
-if test "x`git rev-parse --is-inside-work-tree 2>/dev/null`" = "xtrue" ; then
+if test "x`test ! \"${srcroot}\" && cd \"${srcroot}\"; git rev-parse --is-inside-work-tree 2>/dev/null`" = "xtrue" ; then
   dnl Pattern globs aren't powerful enough to match both single- and
   dnl double-digit version numbers, so iterate over patterns to support up to
   dnl version 99.99.99 without any accidental matches.
-  rm -f "${srcroot}VERSION"
+  rm -f "${objroot}VERSION"
   for pattern in ['[0-9].[0-9].[0-9]' '[0-9].[0-9].[0-9][0-9]' \
                  '[0-9].[0-9][0-9].[0-9]' '[0-9].[0-9][0-9].[0-9][0-9]' \
                  '[0-9][0-9].[0-9].[0-9]' '[0-9][0-9].[0-9].[0-9][0-9]' \
                  '[0-9][0-9].[0-9][0-9].[0-9]' \
                  '[0-9][0-9].[0-9][0-9].[0-9][0-9]']; do
-    if test ! -e "${srcroot}VERSION" ; then
-      git describe --long --abbrev=40 --match="${pattern}" > "${srcroot}VERSION.tmp" 2>/dev/null
+    if test ! -e "${objroot}VERSION" ; then
+      (test ! "${srcroot}" && cd "${srcroot}"; git describe --long --abbrev=40 --match="${pattern}") > "${objroot}VERSION.tmp" 2>/dev/null
       if test $? -eq 0 ; then
-        mv "${srcroot}VERSION.tmp" "${srcroot}VERSION"
+        mv "${objroot}VERSION.tmp" "${objroot}VERSION"
         break
       fi
     fi
   done
 fi
-rm -f "${srcroot}VERSION.tmp"
-if test ! -e "${srcroot}VERSION" ; then
-  AC_MSG_RESULT(
-    [Missing VERSION file, and unable to generate it; creating bogus VERSION])
-  echo "0.0.0-0-g0000000000000000000000000000000000000000" > "${srcroot}VERSION"
+rm -f "${objroot}VERSION.tmp"
+if test ! -e "${objroot}VERSION" ; then
+  if test ! -e "${srcroot}VERSION" ; then
+    AC_MSG_RESULT(
+      [Missing VERSION file, and unable to generate it; creating bogus VERSION])
+    echo "0.0.0-0-g0000000000000000000000000000000000000000" > "${objroot}VERSION"
+  else
+    cp ${srcroot}VERSION ${objroot}VERSION
+  fi
 fi
-jemalloc_version=`cat "${srcroot}VERSION"`
+jemalloc_version=`cat "${objroot}VERSION"`
 jemalloc_version_major=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print [$]1}'`
 jemalloc_version_minor=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print [$]2}'`
 jemalloc_version_bugfix=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print [$]3}'`
@@ -1088,6 +1190,32 @@ fi
 
 CPPFLAGS="$CPPFLAGS -D_REENTRANT"
 
+dnl Check whether clock_gettime(2) is in libc or librt.  This function is only
+dnl used in test code, so save the result to TESTLIBS to avoid poluting LIBS.
+SAVED_LIBS="${LIBS}"
+LIBS=
+AC_SEARCH_LIBS([clock_gettime], [rt], [TESTLIBS="${LIBS}"])
+AC_SUBST([TESTLIBS])
+LIBS="${SAVED_LIBS}"
+
+dnl Check if the GNU-specific secure_getenv function exists.
+AC_CHECK_FUNC([secure_getenv],
+              [have_secure_getenv="1"],
+              [have_secure_getenv="0"]
+             )
+if test "x$have_secure_getenv" = "x1" ; then
+  AC_DEFINE([JEMALLOC_HAVE_SECURE_GETENV], [ ])
+fi
+
+dnl Check if the Solaris/BSD issetugid function exists.
+AC_CHECK_FUNC([issetugid],
+              [have_issetugid="1"],
+              [have_issetugid="0"]
+             )
+if test "x$have_issetugid" = "x1" ; then
+  AC_DEFINE([JEMALLOC_HAVE_ISSETUGID], [ ])
+fi
+
 dnl Check whether the BSD-specific _malloc_thread_cleanup() exists.  If so, use
 dnl it rather than pthreads TSD cleanup functions to support cleanup during
 dnl thread exit, in order to avoid pthreads library recursion during
@@ -1122,9 +1250,9 @@ else
   enable_lazy_lock="1"
 fi
 ],
-[enable_lazy_lock="0"]
+[enable_lazy_lock=""]
 )
-if test "x$enable_lazy_lock" = "x0" -a "x${force_lazy_lock}" = "x1" ; then
+if test "x$enable_lazy_lock" = "x" -a "x${force_lazy_lock}" = "x1" ; then
   AC_MSG_RESULT([Forcing lazy-lock to avoid allocator/threading bootstrap issues])
   enable_lazy_lock="1"
 fi
@@ -1137,6 +1265,8 @@ if test "x$enable_lazy_lock" = "x1" ; then
       ])
   fi
   AC_DEFINE([JEMALLOC_LAZY_LOCK], [ ])
+else
+  enable_lazy_lock="0"
 fi
 AC_SUBST([enable_lazy_lock])
 
@@ -1148,15 +1278,18 @@ else
   enable_tls="1"
 fi
 ,
-enable_tls="1"
+enable_tls=""
 )
-if test "x${enable_tls}" = "x0" -a "x${force_tls}" = "x1" ; then
-  AC_MSG_RESULT([Forcing TLS to avoid allocator/threading bootstrap issues])
-  enable_tls="1"
-fi
-if test "x${enable_tls}" = "x1" -a "x${force_tls}" = "x0" ; then
-  AC_MSG_RESULT([Forcing no TLS to avoid allocator/threading bootstrap issues])
-  enable_tls="0"
+if test "x${enable_tls}" = "x" ; then
+  if test "x${force_tls}" = "x1" ; then
+    AC_MSG_RESULT([Forcing TLS to avoid allocator/threading bootstrap issues])
+    enable_tls="1"
+  elif test "x${force_tls}" = "x0" ; then
+    AC_MSG_RESULT([Forcing no TLS to avoid allocator/threading bootstrap issues])
+    enable_tls="0"
+  else
+    enable_tls="1"
+  fi
 fi
 if test "x${enable_tls}" = "x1" ; then
 AC_MSG_CHECKING([for TLS])
@@ -1171,12 +1304,38 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
               AC_MSG_RESULT([yes]),
               AC_MSG_RESULT([no])
               enable_tls="0")
+else
+  enable_tls="0"
 fi
 AC_SUBST([enable_tls])
 if test "x${enable_tls}" = "x1" ; then
+  if test "x${force_tls}" = "x0" ; then
+    AC_MSG_WARN([TLS enabled despite being marked unusable on this platform])
+  fi
   AC_DEFINE_UNQUOTED([JEMALLOC_TLS], [ ])
 elif test "x${force_tls}" = "x1" ; then
-  AC_MSG_ERROR([Failed to configure TLS, which is mandatory for correct function])
+  AC_MSG_WARN([TLS disabled despite being marked critical on this platform])
+fi
+
+dnl ============================================================================
+dnl Check for C11 atomics.
+
+JE_COMPILABLE([C11 atomics], [
+#include <stdint.h>
+#if (__STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_ATOMICS__)
+#include <stdatomic.h>
+#else
+#error Atomics not available
+#endif
+], [
+    uint64_t *p = (uint64_t *)0;
+    uint64_t x = 1;
+    volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p;
+    uint64_t r = atomic_fetch_add(a, x) + x;
+    return (r == 0);
+], [je_cv_c11atomics])
+if test "x${je_cv_c11atomics}" = "xyes" ; then
+  AC_DEFINE([JEMALLOC_C11ATOMICS])
 fi
 
 dnl ============================================================================
@@ -1333,7 +1492,6 @@ if test "x${enable_zone_allocator}" = "x1" ; then
   if test "x${abi}" != "xmacho"; then
     AC_MSG_ERROR([--enable-zone-allocator is only supported on Darwin])
   fi
-  AC_DEFINE([JEMALLOC_IVSALLOC], [ ])
   AC_DEFINE([JEMALLOC_ZONE], [ ])
 
   dnl The szone version jumped from 3 to 6 between the OS X 10.5.x and 10.6
@@ -1343,7 +1501,7 @@ if test "x${enable_zone_allocator}" = "x1" ; then
   AC_DEFUN([JE_ZONE_PROGRAM],
     [AC_LANG_PROGRAM(
       [#include <malloc/malloc.h>],
-      [static foo[[sizeof($1) $2 sizeof(void *) * $3 ? 1 : -1]]]
+      [static int foo[[sizeof($1) $2 sizeof(void *) * $3 ? 1 : -1]]]
     )])
 
   AC_COMPILE_IFELSE([JE_ZONE_PROGRAM(malloc_zone_t,==,14)],[JEMALLOC_ZONE_VERSION=3],[
@@ -1471,10 +1629,15 @@ AC_CONFIG_COMMANDS([include/jemalloc/internal/public_unnamespace.h], [
 ])
 AC_CONFIG_COMMANDS([include/jemalloc/internal/size_classes.h], [
   mkdir -p "${objroot}include/jemalloc/internal"
-  "${srcdir}/include/jemalloc/internal/size_classes.sh" > "${objroot}include/jemalloc/internal/size_classes.h"
+  "${SHELL}" "${srcdir}/include/jemalloc/internal/size_classes.sh" "${LG_QUANTA}" ${LG_TINY_MIN} "${LG_PAGE_SIZES}" ${LG_SIZE_CLASS_GROUP} > "${objroot}include/jemalloc/internal/size_classes.h"
 ], [
+  SHELL="${SHELL}"
   srcdir="${srcdir}"
   objroot="${objroot}"
+  LG_QUANTA="${LG_QUANTA}"
+  LG_TINY_MIN=${LG_TINY_MIN}
+  LG_PAGE_SIZES="${LG_PAGE_SIZES}"
+  LG_SIZE_CLASS_GROUP=${LG_SIZE_CLASS_GROUP}
 ])
 AC_CONFIG_COMMANDS([include/jemalloc/jemalloc_protos_jet.h], [
   mkdir -p "${objroot}include/jemalloc"
@@ -1521,7 +1684,7 @@ AC_CONFIG_HEADERS([$cfghdrs_tup])
 dnl ============================================================================
 dnl Generate outputs.
 
-AC_CONFIG_FILES([$cfgoutputs_tup config.stamp bin/jemalloc.sh])
+AC_CONFIG_FILES([$cfgoutputs_tup config.stamp bin/jemalloc-config bin/jemalloc.sh bin/jeprof])
 AC_SUBST([cfgoutputs_in])
 AC_SUBST([cfgoutputs_out])
 AC_OUTPUT
@@ -1532,12 +1695,14 @@ AC_MSG_RESULT([=================================================================
 AC_MSG_RESULT([jemalloc version   : ${jemalloc_version}])
 AC_MSG_RESULT([library revision   : ${rev}])
 AC_MSG_RESULT([])
+AC_MSG_RESULT([CONFIG             : ${CONFIG}])
 AC_MSG_RESULT([CC                 : ${CC}])
-AC_MSG_RESULT([CPPFLAGS           : ${CPPFLAGS}])
 AC_MSG_RESULT([CFLAGS             : ${CFLAGS}])
+AC_MSG_RESULT([CPPFLAGS           : ${CPPFLAGS}])
 AC_MSG_RESULT([LDFLAGS            : ${LDFLAGS}])
 AC_MSG_RESULT([EXTRA_LDFLAGS      : ${EXTRA_LDFLAGS}])
 AC_MSG_RESULT([LIBS               : ${LIBS}])
+AC_MSG_RESULT([TESTLIBS           : ${TESTLIBS}])
 AC_MSG_RESULT([RPATH_EXTRA        : ${RPATH_EXTRA}])
 AC_MSG_RESULT([])
 AC_MSG_RESULT([XSLTPROC           : ${XSLTPROC}])
@@ -1545,9 +1710,9 @@ AC_MSG_RESULT([XSLROOT            : ${XSLROOT}])
 AC_MSG_RESULT([])
 AC_MSG_RESULT([PREFIX             : ${PREFIX}])
 AC_MSG_RESULT([BINDIR             : ${BINDIR}])
+AC_MSG_RESULT([DATADIR            : ${DATADIR}])
 AC_MSG_RESULT([INCLUDEDIR         : ${INCLUDEDIR}])
 AC_MSG_RESULT([LIBDIR             : ${LIBDIR}])
-AC_MSG_RESULT([DATADIR            : ${DATADIR}])
 AC_MSG_RESULT([MANDIR             : ${MANDIR}])
 AC_MSG_RESULT([])
 AC_MSG_RESULT([srcroot            : ${srcroot}])
@@ -1576,4 +1741,5 @@ AC_MSG_RESULT([xmalloc            : ${enable_xmalloc}])
 AC_MSG_RESULT([munmap             : ${enable_munmap}])
 AC_MSG_RESULT([lazy_lock          : ${enable_lazy_lock}])
 AC_MSG_RESULT([tls                : ${enable_tls}])
+AC_MSG_RESULT([cache-oblivious    : ${enable_cache_oblivious}])
 AC_MSG_RESULT([===============================================================================])
index 1f692f78b923a53f7924bb2a481109011c669fca..26a5e1429b0515b89b39eee70addfec30df5cc05 100644 (file)
       relevant.  Use bitwise or (<code language="C">|</code>) operations to
       specify one or more of the following:
         <variablelist>
-          <varlistentry>
+          <varlistentry id="MALLOCX_LG_ALIGN">
             <term><constant>MALLOCX_LG_ALIGN(<parameter>la</parameter>)
             </constant></term>
 
             that <parameter>la</parameter> is within the valid
             range.</para></listitem>
           </varlistentry>
-          <varlistentry>
+          <varlistentry id="MALLOCX_ALIGN">
             <term><constant>MALLOCX_ALIGN(<parameter>a</parameter>)
             </constant></term>
 
             validate that <parameter>a</parameter> is a power of 2.
             </para></listitem>
           </varlistentry>
-          <varlistentry>
+          <varlistentry id="MALLOCX_ZERO">
             <term><constant>MALLOCX_ZERO</constant></term>
 
             <listitem><para>Initialize newly allocated memory to contain zero
             that are initialized to contain zero bytes.  If this macro is
             absent, newly allocated memory is uninitialized.</para></listitem>
           </varlistentry>
-          <varlistentry>
+          <varlistentry id="MALLOCX_TCACHE">
+            <term><constant>MALLOCX_TCACHE(<parameter>tc</parameter>)
+            </constant></term>
+
+            <listitem><para>Use the thread-specific cache (tcache) specified by
+            the identifier <parameter>tc</parameter>, which must have been
+            acquired via the <link
+            linkend="tcache.create"><mallctl>tcache.create</mallctl></link>
+            mallctl.  This macro does not validate that
+            <parameter>tc</parameter> specifies a valid
+            identifier.</para></listitem>
+          </varlistentry>
+          <varlistentry id="MALLOC_TCACHE_NONE">
+            <term><constant>MALLOCX_TCACHE_NONE</constant></term>
+
+            <listitem><para>Do not use a thread-specific cache (tcache).  Unless
+            <constant>MALLOCX_TCACHE(<parameter>tc</parameter>)</constant> or
+            <constant>MALLOCX_TCACHE_NONE</constant> is specified, an
+            automatically managed tcache will be used under many circumstances.
+            This macro cannot be used in the same <parameter>flags</parameter>
+            argument as
+            <constant>MALLOCX_TCACHE(<parameter>tc</parameter>)</constant>.</para></listitem>
+          </varlistentry>
+          <varlistentry id="MALLOCX_ARENA">
             <term><constant>MALLOCX_ARENA(<parameter>a</parameter>)
             </constant></term>
 
             <listitem><para>Use the arena specified by the index
-            <parameter>a</parameter> (and by necessity bypass the thread
-            cache).  This macro has no effect for regions that were allocated
-            via an arena other than the one specified.  This macro does not
-            validate that <parameter>a</parameter> specifies an arena index in
-            the valid range.</para></listitem>
+            <parameter>a</parameter>.  This macro has no effect for regions that
+            were allocated via an arena other than the one specified.  This
+            macro does not validate that <parameter>a</parameter> specifies an
+            arena index in the valid range.</para></listitem>
           </varlistentry>
         </variablelist>
       </para>
@@ -406,11 +428,12 @@ for (i = 0; i < nbins; i++) {
       functions simultaneously.  If <option>--enable-stats</option> is
       specified during configuration, &ldquo;m&rdquo; and &ldquo;a&rdquo; can
       be specified to omit merged arena and per arena statistics, respectively;
-      &ldquo;b&rdquo; and &ldquo;l&rdquo; can be specified to omit per size
-      class statistics for bins and large objects, respectively.  Unrecognized
-      characters are silently ignored.  Note that thread caching may prevent
-      some statistics from being completely up to date, since extra locking
-      would be required to merge counters that track thread cache operations.
+      &ldquo;b&rdquo;, &ldquo;l&rdquo;, and &ldquo;h&rdquo; can be specified to
+      omit per size class statistics for bins, large objects, and huge objects,
+      respectively.  Unrecognized characters are silently ignored.  Note that
+      thread caching may prevent some statistics from being completely up to
+      date, since extra locking would be required to merge counters that track
+      thread cache operations.
       </para>
 
       <para>The <function>malloc_usable_size<parameter/></function> function
@@ -500,13 +523,11 @@ for (i = 0; i < nbins; i++) {
     possible to find metadata for user objects very quickly.</para>
 
     <para>User objects are broken into three categories according to size:
-    small, large, and huge.  Small objects are smaller than one page.  Large
-    objects are smaller than the chunk size.  Huge objects are a multiple of
-    the chunk size.  Small and large objects are managed entirely by arenas;
-    huge objects are additionally aggregated in a single data structure that is
-    shared by all threads.  Huge objects are typically used by applications
-    infrequently enough that this single data structure is not a scalability
-    issue.</para>
+    small, large, and huge.  Small and large objects are managed entirely by
+    arenas; huge objects are additionally aggregated in a single data structure
+    that is shared by all threads.  Huge objects are typically used by
+    applications infrequently enough that this single data structure is not a
+    scalability issue.</para>
 
     <para>Each chunk that is managed by an arena tracks its contents as runs of
     contiguous pages (unused, backing a set of small objects, or backing one
@@ -515,18 +536,18 @@ for (i = 0; i < nbins; i++) {
     allocations in constant time.</para>
 
     <para>Small objects are managed in groups by page runs.  Each run maintains
-    a frontier and free list to track which regions are in use.  Allocation
-    requests that are no more than half the quantum (8 or 16, depending on
-    architecture) are rounded up to the nearest power of two that is at least
-    <code language="C">sizeof(<type>double</type>)</code>.  All other small
-    object size classes are multiples of the quantum, spaced such that internal
-    fragmentation is limited to approximately 25% for all but the smallest size
-    classes.  Allocation requests that are larger than the maximum small size
-    class, but small enough to fit in an arena-managed chunk (see the <link
-    linkend="opt.lg_chunk"><mallctl>opt.lg_chunk</mallctl></link> option), are
-    rounded up to the nearest run size.  Allocation requests that are too large
-    to fit in an arena-managed chunk are rounded up to the nearest multiple of
-    the chunk size.</para>
+    a bitmap to track which regions are in use.  Allocation requests that are no
+    more than half the quantum (8 or 16, depending on architecture) are rounded
+    up to the nearest power of two that is at least <code
+    language="C">sizeof(<type>double</type>)</code>.  All other object size
+    classes are multiples of the quantum, spaced such that there are four size
+    classes for each doubling in size, which limits internal fragmentation to
+    approximately 20% for all but the smallest size classes.  Small size classes
+    are smaller than four times the page size, large size classes are smaller
+    than the chunk size (see the <link
+    linkend="opt.lg_chunk"><mallctl>opt.lg_chunk</mallctl></link> option), and
+    huge size classes extend from the chunk size up to one size class less than
+    the full address space size.</para>
 
     <para>Allocations are packed tightly together, which can be an issue for
     multi-threaded applications.  If you need to assure that allocations do not
@@ -534,8 +555,29 @@ for (i = 0; i < nbins; i++) {
     nearest multiple of the cacheline size, or specify cacheline alignment when
     allocating.</para>
 
-    <para>Assuming 4 MiB chunks, 4 KiB pages, and a 16-byte quantum on a 64-bit
-    system, the size classes in each category are as shown in <xref
+    <para>The <function>realloc<parameter/></function>,
+    <function>rallocx<parameter/></function>, and
+    <function>xallocx<parameter/></function> functions may resize allocations
+    without moving them under limited circumstances.  Unlike the
+    <function>*allocx<parameter/></function> API, the standard API does not
+    officially round up the usable size of an allocation to the nearest size
+    class, so technically it is necessary to call
+    <function>realloc<parameter/></function> to grow e.g. a 9-byte allocation to
+    16 bytes, or shrink a 16-byte allocation to 9 bytes.  Growth and shrinkage
+    trivially succeeds in place as long as the pre-size and post-size both round
+    up to the same size class.  No other API guarantees are made regarding
+    in-place resizing, but the current implementation also tries to resize large
+    and huge allocations in place, as long as the pre-size and post-size are
+    both large or both huge.  In such cases shrinkage always succeeds for large
+    size classes, but for huge size classes the chunk allocator must support
+    splitting (see <link
+    linkend="arena.i.chunk_hooks"><mallctl>arena.&lt;i&gt;.chunk_hooks</mallctl></link>).
+    Growth only succeeds if the trailing memory is currently available, and
+    additionally for huge size classes the chunk allocator must support
+    merging.</para>
+
+    <para>Assuming 2 MiB chunks, 4 KiB pages, and a 16-byte quantum on a
+    64-bit system, the size classes in each category are as shown in <xref
     linkend="size_classes" xrefstyle="template:Table %n"/>.</para>
 
     <table xml:id="size_classes" frame="all">
@@ -553,13 +595,13 @@ for (i = 0; i < nbins; i++) {
       </thead>
       <tbody>
         <row>
-          <entry morerows="6">Small</entry>
+          <entry morerows="8">Small</entry>
           <entry>lg</entry>
           <entry>[8]</entry>
         </row>
         <row>
           <entry>16</entry>
-          <entry>[16, 32, 48, ..., 128]</entry>
+          <entry>[16, 32, 48, 64, 80, 96, 112, 128]</entry>
         </row>
         <row>
           <entry>32</entry>
@@ -579,17 +621,77 @@ for (i = 0; i < nbins; i++) {
         </row>
         <row>
           <entry>512</entry>
-          <entry>[2560, 3072, 3584]</entry>
+          <entry>[2560, 3072, 3584, 4096]</entry>
+        </row>
+        <row>
+          <entry>1 KiB</entry>
+          <entry>[5 KiB, 6 KiB, 7 KiB, 8 KiB]</entry>
+        </row>
+        <row>
+          <entry>2 KiB</entry>
+          <entry>[10 KiB, 12 KiB, 14 KiB]</entry>
+        </row>
+        <row>
+          <entry morerows="7">Large</entry>
+          <entry>2 KiB</entry>
+          <entry>[16 KiB]</entry>
         </row>
         <row>
-          <entry>Large</entry>
           <entry>4 KiB</entry>
-          <entry>[4 KiB, 8 KiB, 12 KiB, ..., 4072 KiB]</entry>
+          <entry>[20 KiB, 24 KiB, 28 KiB, 32 KiB]</entry>
+        </row>
+        <row>
+          <entry>8 KiB</entry>
+          <entry>[40 KiB, 48 KiB, 54 KiB, 64 KiB]</entry>
+        </row>
+        <row>
+          <entry>16 KiB</entry>
+          <entry>[80 KiB, 96 KiB, 112 KiB, 128 KiB]</entry>
+        </row>
+        <row>
+          <entry>32 KiB</entry>
+          <entry>[160 KiB, 192 KiB, 224 KiB, 256 KiB]</entry>
+        </row>
+        <row>
+          <entry>64 KiB</entry>
+          <entry>[320 KiB, 384 KiB, 448 KiB, 512 KiB]</entry>
+        </row>
+        <row>
+          <entry>128 KiB</entry>
+          <entry>[640 KiB, 768 KiB, 896 KiB, 1 MiB]</entry>
+        </row>
+        <row>
+          <entry>256 KiB</entry>
+          <entry>[1280 KiB, 1536 KiB, 1792 KiB]</entry>
+        </row>
+        <row>
+          <entry morerows="6">Huge</entry>
+          <entry>256 KiB</entry>
+          <entry>[2 MiB]</entry>
+        </row>
+        <row>
+          <entry>512 KiB</entry>
+          <entry>[2560 KiB, 3 MiB, 3584 KiB, 4 MiB]</entry>
+        </row>
+        <row>
+          <entry>1 MiB</entry>
+          <entry>[5 MiB, 6 MiB, 7 MiB, 8 MiB]</entry>
+        </row>
+        <row>
+          <entry>2 MiB</entry>
+          <entry>[10 MiB, 12 MiB, 14 MiB, 16 MiB]</entry>
         </row>
         <row>
-          <entry>Huge</entry>
           <entry>4 MiB</entry>
-          <entry>[4 MiB, 8 MiB, 12 MiB, ...]</entry>
+          <entry>[20 MiB, 24 MiB, 28 MiB, 32 MiB]</entry>
+        </row>
+        <row>
+          <entry>8 MiB</entry>
+          <entry>[40 MiB, 48 MiB, 56 MiB, 64 MiB]</entry>
+        </row>
+        <row>
+          <entry>...</entry>
+          <entry>...</entry>
         </row>
       </tbody>
       </tgroup>
@@ -634,6 +736,16 @@ for (i = 0; i < nbins; i++) {
         detecting whether another thread caused a refresh.</para></listitem>
       </varlistentry>
 
+      <varlistentry id="config.cache_oblivious">
+        <term>
+          <mallctl>config.cache_oblivious</mallctl>
+          (<type>bool</type>)
+          <literal>r-</literal>
+        </term>
+        <listitem><para><option>--enable-cache-oblivious</option> was specified
+        during build configuration.</para></listitem>
+      </varlistentry>
+
       <varlistentry id="config.debug">
         <term>
           <mallctl>config.debug</mallctl>
@@ -810,7 +922,7 @@ for (i = 0; i < nbins; i++) {
         <listitem><para>Virtual memory chunk size (log base 2).  If a chunk
         size outside the supported size range is specified, the size is
         silently clipped to the minimum/maximum supported size.  The default
-        chunk size is 4 MiB (2^22).
+        chunk size is 2 MiB (2^21).
         </para></listitem>
       </varlistentry>
 
@@ -840,7 +952,11 @@ for (i = 0; i < nbins; i++) {
         provides the kernel with sufficient information to recycle dirty pages
         if physical memory becomes scarce and the pages remain unused.  The
         default minimum ratio is 8:1 (2^3:1); an option value of -1 will
-        disable dirty page purging.</para></listitem>
+        disable dirty page purging.  See <link
+        linkend="arenas.lg_dirty_mult"><mallctl>arenas.lg_dirty_mult</mallctl></link>
+        and <link
+        linkend="arena.i.lg_dirty_mult"><mallctl>arena.&lt;i&gt;.lg_dirty_mult</mallctl></link>
+        for related dynamic control options.</para></listitem>
       </varlistentry>
 
       <varlistentry id="opt.stats_print">
@@ -857,26 +973,34 @@ for (i = 0; i < nbins; i++) {
         <option>--enable-stats</option> is specified during configuration, this
         has the potential to cause deadlock for a multi-threaded process that
         exits while one or more threads are executing in the memory allocation
-        functions.  Therefore, this option should only be used with care; it is
-        primarily intended as a performance tuning aid during application
+        functions.  Furthermore, <function>atexit<parameter/></function> may
+        allocate memory during application initialization and then deadlock
+        internally when jemalloc in turn calls
+        <function>atexit<parameter/></function>, so this option is not
+        univerally usable (though the application can register its own
+        <function>atexit<parameter/></function> function with equivalent
+        functionality).  Therefore, this option should only be used with care;
+        it is primarily intended as a performance tuning aid during application
         development.  This option is disabled by default.</para></listitem>
       </varlistentry>
 
       <varlistentry id="opt.junk">
         <term>
           <mallctl>opt.junk</mallctl>
-          (<type>bool</type>)
+          (<type>const char *</type>)
           <literal>r-</literal>
           [<option>--enable-fill</option>]
         </term>
-        <listitem><para>Junk filling enabled/disabled.  If enabled, each byte
-        of uninitialized allocated memory will be initialized to
-        <literal>0xa5</literal>.  All deallocated memory will be initialized to
-        <literal>0x5a</literal>.  This is intended for debugging and will
-        impact performance negatively.  This option is disabled by default
-        unless <option>--enable-debug</option> is specified during
-        configuration, in which case it is enabled by default unless running
-        inside <ulink
+        <listitem><para>Junk filling.  If set to "alloc", each byte of
+        uninitialized allocated memory will be initialized to
+        <literal>0xa5</literal>.  If set to "free", all deallocated memory will
+        be initialized to <literal>0x5a</literal>.  If set to "true", both
+        allocated and deallocated memory will be initialized, and if set to
+        "false", junk filling be disabled entirely.  This is intended for
+        debugging and will impact performance negatively.  This option is
+        "false" by default unless <option>--enable-debug</option> is specified
+        during configuration, in which case it is "true" by default unless
+        running inside <ulink
         url="http://valgrind.org/">Valgrind</ulink>.</para></listitem>
       </varlistentry>
 
@@ -977,12 +1101,11 @@ malloc_conf = "xmalloc:true";]]></programlisting>
           <literal>r-</literal>
           [<option>--enable-tcache</option>]
         </term>
-        <listitem><para>Thread-specific caching enabled/disabled.  When there
-        are multiple threads, each thread uses a thread-specific cache for
-        objects up to a certain size.  Thread-specific caching allows many
-        allocations to be satisfied without performing any thread
-        synchronization, at the cost of increased memory use.  See the
-        <link
+        <listitem><para>Thread-specific caching (tcache) enabled/disabled.  When
+        there are multiple threads, each thread uses a tcache for objects up to
+        a certain size.  Thread-specific caching allows many allocations to be
+        satisfied without performing any thread synchronization, at the cost of
+        increased memory use.  See the <link
         linkend="opt.lg_tcache_max"><mallctl>opt.lg_tcache_max</mallctl></link>
         option for related tuning information.  This option is enabled by
         default unless running inside <ulink
@@ -998,8 +1121,8 @@ malloc_conf = "xmalloc:true";]]></programlisting>
           [<option>--enable-tcache</option>]
         </term>
         <listitem><para>Maximum size class (log base 2) to cache in the
-        thread-specific cache.  At a minimum, all small size classes are
-        cached, and at a maximum all large size classes are cached.  The
+        thread-specific cache (tcache).  At a minimum, all small size classes
+        are cached, and at a maximum all large size classes are cached.  The
         default maximum is 32 KiB (2^15).</para></listitem>
       </varlistentry>
 
@@ -1024,8 +1147,9 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         option for information on high-water-triggered profile dumping, and the
         <link linkend="opt.prof_final"><mallctl>opt.prof_final</mallctl></link>
         option for final profile dumping.  Profile output is compatible with
-        the included <command>pprof</command> Perl script, which originates
-        from the <ulink url="http://code.google.com/p/gperftools/">gperftools
+        the <command>jeprof</command> command, which is based on the
+        <command>pprof</command> that is developed as part of the <ulink
+        url="http://code.google.com/p/gperftools/">gperftools
         package</ulink>.</para></listitem>
       </varlistentry>
 
@@ -1047,7 +1171,7 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         <term>
           <mallctl>opt.prof_active</mallctl>
           (<type>bool</type>)
-          <literal>rw</literal>
+          <literal>r-</literal>
           [<option>--enable-prof</option>]
         </term>
         <listitem><para>Profiling activated/deactivated.  This is a secondary
@@ -1132,13 +1256,11 @@ malloc_conf = "xmalloc:true";]]></programlisting>
           <literal>r-</literal>
           [<option>--enable-prof</option>]
         </term>
-        <listitem><para>Trigger a memory profile dump every time the total
-        virtual memory exceeds the previous maximum.  Profiles are dumped to
-        files named according to the pattern
-        <filename>&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.u&lt;useq&gt;.heap</filename>,
-        where <literal>&lt;prefix&gt;</literal> is controlled by the <link
-        linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link>
-        option.  This option is disabled by default.</para></listitem>
+        <listitem><para>Set the initial state of <link
+        linkend="prof.gdump"><mallctl>prof.gdump</mallctl></link>, which when
+        enabled triggers a memory profile dump every time the total virtual
+        memory exceeds the previous maximum.  This option is disabled by
+        default.</para></listitem>
       </varlistentry>
 
       <varlistentry id="opt.prof_final">
@@ -1155,7 +1277,13 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         <filename>&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.f.heap</filename>,
         where <literal>&lt;prefix&gt;</literal> is controlled by the <link
         linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link>
-        option.  This option is enabled by default.</para></listitem>
+        option.  Note that <function>atexit<parameter/></function> may allocate
+        memory during application initialization and then deadlock internally
+        when jemalloc in turn calls <function>atexit<parameter/></function>, so
+        this option is not univerally usable (though the application can
+        register its own <function>atexit<parameter/></function> function with
+        equivalent functionality).  This option is disabled by
+        default.</para></listitem>
       </varlistentry>
 
       <varlistentry id="opt.prof_leak">
@@ -1252,7 +1380,7 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         <listitem><para>Enable/disable calling thread's tcache.  The tcache is
         implicitly flushed as a side effect of becoming
         disabled (see <link
-        lenkend="thread.tcache.flush"><mallctl>thread.tcache.flush</mallctl></link>).
+        linkend="thread.tcache.flush"><mallctl>thread.tcache.flush</mallctl></link>).
         </para></listitem>
       </varlistentry>
 
@@ -1263,9 +1391,9 @@ malloc_conf = "xmalloc:true";]]></programlisting>
           <literal>--</literal>
           [<option>--enable-tcache</option>]
         </term>
-        <listitem><para>Flush calling thread's tcache.  This interface releases
-        all cached objects and internal data structures associated with the
-        calling thread's thread-specific cache.  Ordinarily, this interface
+        <listitem><para>Flush calling thread's thread-specific cache (tcache).
+        This interface releases all cached objects and internal data structures
+        associated with the calling thread's tcache.  Ordinarily, this interface
         need not be called, since automatic periodic incremental garbage
         collection occurs, and the thread cache is automatically discarded when
         a thread exits.  However, garbage collection is triggered by allocation
@@ -1290,8 +1418,8 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         can cause asynchronous string deallocation.  Furthermore, each
         invocation of this interface can only read or write; simultaneous
         read/write is not supported due to string lifetime limitations.  The
-        name string must nil-terminated and comprised only of characters in the
-        sets recognized
+        name string must be nil-terminated and comprised only of characters in
+        the sets recognized
         by <citerefentry><refentrytitle>isgraph</refentrytitle>
         <manvolnum>3</manvolnum></citerefentry> and
         <citerefentry><refentrytitle>isblank</refentrytitle>
@@ -1312,6 +1440,49 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         default.</para></listitem>
       </varlistentry>
 
+      <varlistentry id="tcache.create">
+        <term>
+          <mallctl>tcache.create</mallctl>
+          (<type>unsigned</type>)
+          <literal>r-</literal>
+          [<option>--enable-tcache</option>]
+        </term>
+        <listitem><para>Create an explicit thread-specific cache (tcache) and
+        return an identifier that can be passed to the <link
+        linkend="MALLOCX_TCACHE"><constant>MALLOCX_TCACHE(<parameter>tc</parameter>)</constant></link>
+        macro to explicitly use the specified cache rather than the
+        automatically managed one that is used by default.  Each explicit cache
+        can be used by only one thread at a time; the application must assure
+        that this constraint holds.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry id="tcache.flush">
+        <term>
+          <mallctl>tcache.flush</mallctl>
+          (<type>unsigned</type>)
+          <literal>-w</literal>
+          [<option>--enable-tcache</option>]
+        </term>
+        <listitem><para>Flush the specified thread-specific cache (tcache).  The
+        same considerations apply to this interface as to <link
+        linkend="thread.tcache.flush"><mallctl>thread.tcache.flush</mallctl></link>,
+        except that the tcache will never be automatically be discarded.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry id="tcache.destroy">
+        <term>
+          <mallctl>tcache.destroy</mallctl>
+          (<type>unsigned</type>)
+          <literal>-w</literal>
+          [<option>--enable-tcache</option>]
+        </term>
+        <listitem><para>Flush the specified thread-specific cache (tcache) and
+        make the identifier available for use during a future tcache creation.
+        </para></listitem>
+      </varlistentry>
+
       <varlistentry id="arena.i.purge">
         <term>
           <mallctl>arena.&lt;i&gt;.purge</mallctl>
@@ -1338,75 +1509,217 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         settings.</para></listitem>
       </varlistentry>
 
-      <varlistentry id="arena.i.chunk.alloc">
+      <varlistentry id="arena.i.lg_dirty_mult">
         <term>
-          <mallctl>arena.&lt;i&gt;.chunk.alloc</mallctl>
-          (<type>chunk_alloc_t *</type>)
+          <mallctl>arena.&lt;i&gt;.lg_dirty_mult</mallctl>
+          (<type>ssize_t</type>)
           <literal>rw</literal>
         </term>
-        <listitem><para>Get or set the chunk allocation function for arena
-        &lt;i&gt;.  If setting, the chunk deallocation function should
-        also be set via <link linkend="arena.i.chunk.dalloc">
-        <mallctl>arena.&lt;i&gt;.chunk.dalloc</mallctl></link> to a companion
-        function that knows how to deallocate the chunks.
+        <listitem><para>Current per-arena minimum ratio (log base 2) of active
+        to dirty pages for arena &lt;i&gt;.  Each time this interface is set and
+        the ratio is increased, pages are synchronously purged as necessary to
+        impose the new ratio.  See <link
+        linkend="opt.lg_dirty_mult"><mallctl>opt.lg_dirty_mult</mallctl></link>
+        for additional information.</para></listitem>
+      </varlistentry>
+
+      <varlistentry id="arena.i.chunk_hooks">
+        <term>
+          <mallctl>arena.&lt;i&gt;.chunk_hooks</mallctl>
+          (<type>chunk_hooks_t</type>)
+          <literal>rw</literal>
+        </term>
+        <listitem><para>Get or set the chunk management hook functions for arena
+        &lt;i&gt;.  The functions must be capable of operating on all extant
+        chunks associated with arena &lt;i&gt;, usually by passing unknown
+        chunks to the replaced functions.  In practice, it is feasible to
+        control allocation for arenas created via <link
+        linkend="arenas.extend"><mallctl>arenas.extend</mallctl></link> such
+        that all chunks originate from an application-supplied chunk allocator
+        (by setting custom chunk hook functions just after arena creation), but
+        the automatically created arenas may have already created chunks prior
+        to the application having an opportunity to take over chunk
+        allocation.</para>
+
+        <programlisting language="C"><![CDATA[
+typedef struct {
+       chunk_alloc_t           *alloc;
+       chunk_dalloc_t          *dalloc;
+       chunk_commit_t          *commit;
+       chunk_decommit_t        *decommit;
+       chunk_purge_t           *purge;
+       chunk_split_t           *split;
+       chunk_merge_t           *merge;
+} chunk_hooks_t;]]></programlisting>
+        <para>The <type>chunk_hooks_t</type> structure comprises function
+        pointers which are described individually below.  jemalloc uses these
+        functions to manage chunk lifetime, which starts off with allocation of
+        mapped committed memory, in the simplest case followed by deallocation.
+        However, there are performance and platform reasons to retain chunks for
+        later reuse.  Cleanup attempts cascade from deallocation to decommit to
+        purging, which gives the chunk management functions opportunities to
+        reject the most permanent cleanup operations in favor of less permanent
+        (and often less costly) operations.  The chunk splitting and merging
+        operations can also be opted out of, but this is mainly intended to
+        support platforms on which virtual memory mappings provided by the
+        operating system kernel do not automatically coalesce and split, e.g.
+        Windows.</para>
+
         <funcsynopsis><funcprototype>
           <funcdef>typedef void *<function>(chunk_alloc_t)</function></funcdef>
           <paramdef>void *<parameter>chunk</parameter></paramdef>
           <paramdef>size_t <parameter>size</parameter></paramdef>
           <paramdef>size_t <parameter>alignment</parameter></paramdef>
           <paramdef>bool *<parameter>zero</parameter></paramdef>
+          <paramdef>bool *<parameter>commit</parameter></paramdef>
           <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
         </funcprototype></funcsynopsis>
-        A chunk allocation function conforms to the <type>chunk_alloc_t</type>
-        type and upon success returns a pointer to <parameter>size</parameter>
-        bytes of memory on behalf of arena <parameter>arena_ind</parameter> such
-        that the chunk's base address is a multiple of
-        <parameter>alignment</parameter>, as well as setting
-        <parameter>*zero</parameter> to indicate whether the chunk is zeroed.
-        Upon error the function returns <constant>NULL</constant> and leaves
-        <parameter>*zero</parameter> unmodified.  The
+        <literallayout></literallayout>
+        <para>A chunk allocation function conforms to the
+        <type>chunk_alloc_t</type> type and upon success returns a pointer to
+        <parameter>size</parameter> bytes of mapped memory on behalf of arena
+        <parameter>arena_ind</parameter> such that the chunk's base address is a
+        multiple of <parameter>alignment</parameter>, as well as setting
+        <parameter>*zero</parameter> to indicate whether the chunk is zeroed and
+        <parameter>*commit</parameter> to indicate whether the chunk is
+        committed.  Upon error the function returns <constant>NULL</constant>
+        and leaves <parameter>*zero</parameter> and
+        <parameter>*commit</parameter> unmodified.  The
         <parameter>size</parameter> parameter is always a multiple of the chunk
         size.  The <parameter>alignment</parameter> parameter is always a power
         of two at least as large as the chunk size.  Zeroing is mandatory if
-        <parameter>*zero</parameter> is true upon function entry.  If
-        <parameter>chunk</parameter> is not <constant>NULL</constant>, the
-        returned pointer must be <parameter>chunk</parameter> or
-        <constant>NULL</constant> if it could not be allocated.</para>
-
-        <para>Note that replacing the default chunk allocation function makes
-        the arena's <link
+        <parameter>*zero</parameter> is true upon function entry.  Committing is
+        mandatory if <parameter>*commit</parameter> is true upon function entry.
+        If <parameter>chunk</parameter> is not <constant>NULL</constant>, the
+        returned pointer must be <parameter>chunk</parameter> on success or
+        <constant>NULL</constant> on error.  Committed memory may be committed
+        in absolute terms as on a system that does not overcommit, or in
+        implicit terms as on a system that overcommits and satisfies physical
+        memory needs on demand via soft page faults.  Note that replacing the
+        default chunk allocation function makes the arena's <link
         linkend="arena.i.dss"><mallctl>arena.&lt;i&gt;.dss</mallctl></link>
-        setting irrelevant.</para></listitem>
-      </varlistentry>
+        setting irrelevant.</para>
 
-      <varlistentry id="arena.i.chunk.dalloc">
-        <term>
-          <mallctl>arena.&lt;i&gt;.chunk.dalloc</mallctl>
-          (<type>chunk_dalloc_t *</type>)
-          <literal>rw</literal>
-        </term>
-        <listitem><para>Get or set the chunk deallocation function for arena
-        &lt;i&gt;.  If setting, the chunk deallocation function must
-        be capable of deallocating all extant chunks associated with arena
-        &lt;i&gt;, usually by passing unknown chunks to the deallocation
-        function that was replaced.  In practice, it is feasible to control
-        allocation for arenas created via <link
-        linkend="arenas.extend"><mallctl>arenas.extend</mallctl></link> such
-        that all chunks originate from an application-supplied chunk allocator
-        (by setting custom chunk allocation/deallocation functions just after
-        arena creation), but the automatically created arenas may have already
-        created chunks prior to the application having an opportunity to take
-        over chunk allocation.
         <funcsynopsis><funcprototype>
-          <funcdef>typedef void <function>(chunk_dalloc_t)</function></funcdef>
+          <funcdef>typedef bool <function>(chunk_dalloc_t)</function></funcdef>
           <paramdef>void *<parameter>chunk</parameter></paramdef>
           <paramdef>size_t <parameter>size</parameter></paramdef>
+          <paramdef>bool <parameter>committed</parameter></paramdef>
           <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
         </funcprototype></funcsynopsis>
+        <literallayout></literallayout>
+        <para>
         A chunk deallocation function conforms to the
         <type>chunk_dalloc_t</type> type and deallocates a
-        <parameter>chunk</parameter> of given <parameter>size</parameter> on
-        behalf of arena <parameter>arena_ind</parameter>.</para></listitem>
+        <parameter>chunk</parameter> of given <parameter>size</parameter> with
+        <parameter>committed</parameter>/decommited memory as indicated, on
+        behalf of arena <parameter>arena_ind</parameter>, returning false upon
+        success.  If the function returns true, this indicates opt-out from
+        deallocation; the virtual memory mapping associated with the chunk
+        remains mapped, in the same commit state, and available for future use,
+        in which case it will be automatically retained for later reuse.</para>
+
+        <funcsynopsis><funcprototype>
+          <funcdef>typedef bool <function>(chunk_commit_t)</function></funcdef>
+          <paramdef>void *<parameter>chunk</parameter></paramdef>
+          <paramdef>size_t <parameter>size</parameter></paramdef>
+          <paramdef>size_t <parameter>offset</parameter></paramdef>
+          <paramdef>size_t <parameter>length</parameter></paramdef>
+          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+        </funcprototype></funcsynopsis>
+        <literallayout></literallayout>
+        <para>A chunk commit function conforms to the
+        <type>chunk_commit_t</type> type and commits zeroed physical memory to
+        back pages within a <parameter>chunk</parameter> of given
+        <parameter>size</parameter> at <parameter>offset</parameter> bytes,
+        extending for <parameter>length</parameter> on behalf of arena
+        <parameter>arena_ind</parameter>, returning false upon success.
+        Committed memory may be committed in absolute terms as on a system that
+        does not overcommit, or in implicit terms as on a system that
+        overcommits and satisfies physical memory needs on demand via soft page
+        faults. If the function returns true, this indicates insufficient
+        physical memory to satisfy the request.</para>
+
+        <funcsynopsis><funcprototype>
+          <funcdef>typedef bool <function>(chunk_decommit_t)</function></funcdef>
+          <paramdef>void *<parameter>chunk</parameter></paramdef>
+          <paramdef>size_t <parameter>size</parameter></paramdef>
+          <paramdef>size_t <parameter>offset</parameter></paramdef>
+          <paramdef>size_t <parameter>length</parameter></paramdef>
+          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+        </funcprototype></funcsynopsis>
+        <literallayout></literallayout>
+        <para>A chunk decommit function conforms to the
+        <type>chunk_decommit_t</type> type and decommits any physical memory
+        that is backing pages within a <parameter>chunk</parameter> of given
+        <parameter>size</parameter> at <parameter>offset</parameter> bytes,
+        extending for <parameter>length</parameter> on behalf of arena
+        <parameter>arena_ind</parameter>, returning false upon success, in which
+        case the pages will be committed via the chunk commit function before
+        being reused.  If the function returns true, this indicates opt-out from
+        decommit; the memory remains committed and available for future use, in
+        which case it will be automatically retained for later reuse.</para>
+
+        <funcsynopsis><funcprototype>
+          <funcdef>typedef bool <function>(chunk_purge_t)</function></funcdef>
+          <paramdef>void *<parameter>chunk</parameter></paramdef>
+          <paramdef>size_t<parameter>size</parameter></paramdef>
+          <paramdef>size_t <parameter>offset</parameter></paramdef>
+          <paramdef>size_t <parameter>length</parameter></paramdef>
+          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+        </funcprototype></funcsynopsis>
+        <literallayout></literallayout>
+        <para>A chunk purge function conforms to the <type>chunk_purge_t</type>
+        type and optionally discards physical pages within the virtual memory
+        mapping associated with <parameter>chunk</parameter> of given
+        <parameter>size</parameter> at <parameter>offset</parameter> bytes,
+        extending for <parameter>length</parameter> on behalf of arena
+        <parameter>arena_ind</parameter>, returning false if pages within the
+        purged virtual memory range will be zero-filled the next time they are
+        accessed.</para>
+
+        <funcsynopsis><funcprototype>
+          <funcdef>typedef bool <function>(chunk_split_t)</function></funcdef>
+          <paramdef>void *<parameter>chunk</parameter></paramdef>
+          <paramdef>size_t <parameter>size</parameter></paramdef>
+          <paramdef>size_t <parameter>size_a</parameter></paramdef>
+          <paramdef>size_t <parameter>size_b</parameter></paramdef>
+          <paramdef>bool <parameter>committed</parameter></paramdef>
+          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+        </funcprototype></funcsynopsis>
+        <literallayout></literallayout>
+        <para>A chunk split function conforms to the <type>chunk_split_t</type>
+        type and optionally splits <parameter>chunk</parameter> of given
+        <parameter>size</parameter> into two adjacent chunks, the first of
+        <parameter>size_a</parameter> bytes, and the second of
+        <parameter>size_b</parameter> bytes, operating on
+        <parameter>committed</parameter>/decommitted memory as indicated, on
+        behalf of arena <parameter>arena_ind</parameter>, returning false upon
+        success.  If the function returns true, this indicates that the chunk
+        remains unsplit and therefore should continue to be operated on as a
+        whole.</para>
+
+        <funcsynopsis><funcprototype>
+          <funcdef>typedef bool <function>(chunk_merge_t)</function></funcdef>
+          <paramdef>void *<parameter>chunk_a</parameter></paramdef>
+          <paramdef>size_t <parameter>size_a</parameter></paramdef>
+          <paramdef>void *<parameter>chunk_b</parameter></paramdef>
+          <paramdef>size_t <parameter>size_b</parameter></paramdef>
+          <paramdef>bool <parameter>committed</parameter></paramdef>
+          <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+        </funcprototype></funcsynopsis>
+        <literallayout></literallayout>
+        <para>A chunk merge function conforms to the <type>chunk_merge_t</type>
+        type and optionally merges adjacent chunks,
+        <parameter>chunk_a</parameter> of given <parameter>size_a</parameter>
+        and <parameter>chunk_b</parameter> of given
+        <parameter>size_b</parameter> into one contiguous chunk, operating on
+        <parameter>committed</parameter>/decommitted memory as indicated, on
+        behalf of arena <parameter>arena_ind</parameter>, returning false upon
+        success.  If the function returns true, this indicates that the chunks
+        remain distinct mappings and therefore should continue to be operated on
+        independently.</para>
+        </listitem>
       </varlistentry>
 
       <varlistentry id="arenas.narenas">
@@ -1430,6 +1743,20 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         initialized.</para></listitem>
       </varlistentry>
 
+      <varlistentry id="arenas.lg_dirty_mult">
+        <term>
+          <mallctl>arenas.lg_dirty_mult</mallctl>
+          (<type>ssize_t</type>)
+          <literal>rw</literal>
+        </term>
+        <listitem><para>Current default per-arena minimum ratio (log base 2) of
+        active to dirty pages, used to initialize <link
+        linkend="arena.i.lg_dirty_mult"><mallctl>arena.&lt;i&gt;.lg_dirty_mult</mallctl></link>
+        during arena creation.  See <link
+        linkend="opt.lg_dirty_mult"><mallctl>opt.lg_dirty_mult</mallctl></link>
+        for additional information.</para></listitem>
+      </varlistentry>
+
       <varlistentry id="arenas.quantum">
         <term>
           <mallctl>arenas.quantum</mallctl>
@@ -1508,7 +1835,7 @@ malloc_conf = "xmalloc:true";]]></programlisting>
       <varlistentry id="arenas.nlruns">
         <term>
           <mallctl>arenas.nlruns</mallctl>
-          (<type>size_t</type>)
+          (<type>unsigned</type>)
           <literal>r-</literal>
         </term>
         <listitem><para>Total number of large size classes.</para></listitem>
@@ -1524,6 +1851,25 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         class.</para></listitem>
       </varlistentry>
 
+      <varlistentry id="arenas.nhchunks">
+        <term>
+          <mallctl>arenas.nhchunks</mallctl>
+          (<type>unsigned</type>)
+          <literal>r-</literal>
+        </term>
+        <listitem><para>Total number of huge size classes.</para></listitem>
+      </varlistentry>
+
+      <varlistentry id="arenas.hchunk.i.size">
+        <term>
+          <mallctl>arenas.hchunk.&lt;i&gt;.size</mallctl>
+          (<type>size_t</type>)
+          <literal>r-</literal>
+        </term>
+        <listitem><para>Maximum size supported by this huge size
+        class.</para></listitem>
+      </varlistentry>
+
       <varlistentry id="arenas.extend">
         <term>
           <mallctl>arenas.extend</mallctl>
@@ -1579,6 +1925,22 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         option.</para></listitem>
       </varlistentry>
 
+      <varlistentry id="prof.gdump">
+        <term>
+          <mallctl>prof.gdump</mallctl>
+          (<type>bool</type>)
+          <literal>rw</literal>
+          [<option>--enable-prof</option>]
+        </term>
+        <listitem><para>When enabled, trigger a memory profile dump every time
+        the total virtual memory exceeds the previous maximum.  Profiles are
+        dumped to files named according to the pattern
+        <filename>&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.u&lt;useq&gt;.heap</filename>,
+        where <literal>&lt;prefix&gt;</literal> is controlled by the <link
+        linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link>
+        option.</para></listitem>
+      </varlistentry>
+
       <varlistentry id="prof.reset">
         <term>
           <mallctl>prof.reset</mallctl>
@@ -1629,9 +1991,8 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         </term>
         <listitem><para>Pointer to a counter that contains an approximate count
         of the current number of bytes in active pages.  The estimate may be
-        high, but never low, because each arena rounds up to the nearest
-        multiple of the chunk size when computing its contribution to the
-        counter.  Note that the <link
+        high, but never low, because each arena rounds up when computing its
+        contribution to the counter.  Note that the <link
         linkend="epoch"><mallctl>epoch</mallctl></link> mallctl has no bearing
         on this counter.  Furthermore, counter consistency is maintained via
         atomic operations, so it is necessary to use an atomic operation in
@@ -1662,55 +2023,56 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         equal to <link
         linkend="stats.allocated"><mallctl>stats.allocated</mallctl></link>.
         This does not include <link linkend="stats.arenas.i.pdirty">
-        <mallctl>stats.arenas.&lt;i&gt;.pdirty</mallctl></link> and pages
+        <mallctl>stats.arenas.&lt;i&gt;.pdirty</mallctl></link>, nor pages
         entirely devoted to allocator metadata.</para></listitem>
       </varlistentry>
 
-      <varlistentry id="stats.mapped">
+      <varlistentry id="stats.metadata">
         <term>
-          <mallctl>stats.mapped</mallctl>
+          <mallctl>stats.metadata</mallctl>
           (<type>size_t</type>)
           <literal>r-</literal>
           [<option>--enable-stats</option>]
         </term>
-        <listitem><para>Total number of bytes in chunks mapped on behalf of the
-        application.  This is a multiple of the chunk size, and is at least as
-        large as <link
-        linkend="stats.active"><mallctl>stats.active</mallctl></link>.  This
-        does not include inactive chunks.</para></listitem>
+        <listitem><para>Total number of bytes dedicated to metadata, which
+        comprise base allocations used for bootstrap-sensitive internal
+        allocator data structures, arena chunk headers (see <link
+        linkend="stats.arenas.i.metadata.mapped"><mallctl>stats.arenas.&lt;i&gt;.metadata.mapped</mallctl></link>),
+        and internal allocations (see <link
+        linkend="stats.arenas.i.metadata.allocated"><mallctl>stats.arenas.&lt;i&gt;.metadata.allocated</mallctl></link>).</para></listitem>
       </varlistentry>
 
-      <varlistentry id="stats.chunks.current">
+      <varlistentry id="stats.resident">
         <term>
-          <mallctl>stats.chunks.current</mallctl>
+          <mallctl>stats.resident</mallctl>
           (<type>size_t</type>)
           <literal>r-</literal>
           [<option>--enable-stats</option>]
         </term>
-        <listitem><para>Total number of chunks actively mapped on behalf of the
-        application.  This does not include inactive chunks.
-        </para></listitem>
-      </varlistentry>
-
-      <varlistentry id="stats.chunks.total">
-        <term>
-          <mallctl>stats.chunks.total</mallctl>
-          (<type>uint64_t</type>)
-          <literal>r-</literal>
-          [<option>--enable-stats</option>]
-        </term>
-        <listitem><para>Cumulative number of chunks allocated.</para></listitem>
+        <listitem><para>Maximum number of bytes in physically resident data
+        pages mapped by the allocator, comprising all pages dedicated to
+        allocator metadata, pages backing active allocations, and unused dirty
+        pages.  This is a maximum rather than precise because pages may not
+        actually be physically resident if they correspond to demand-zeroed
+        virtual memory that has not yet been touched.  This is a multiple of the
+        page size, and is larger than <link
+        linkend="stats.active"><mallctl>stats.active</mallctl></link>.</para></listitem>
       </varlistentry>
 
-      <varlistentry id="stats.chunks.high">
+      <varlistentry id="stats.mapped">
         <term>
-          <mallctl>stats.chunks.high</mallctl>
+          <mallctl>stats.mapped</mallctl>
           (<type>size_t</type>)
           <literal>r-</literal>
           [<option>--enable-stats</option>]
         </term>
-        <listitem><para>Maximum number of active chunks at any time thus far.
-        </para></listitem>
+        <listitem><para>Total number of bytes in active chunks mapped by the
+        allocator.  This is a multiple of the chunk size, and is larger than
+        <link linkend="stats.active"><mallctl>stats.active</mallctl></link>.
+        This does not include inactive chunks, even those that contain unused
+        dirty pages, which means that there is no strict ordering between this
+        and <link
+        linkend="stats.resident"><mallctl>stats.resident</mallctl></link>.</para></listitem>
       </varlistentry>
 
       <varlistentry id="stats.arenas.i.dss">
@@ -1727,6 +2089,18 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         </para></listitem>
       </varlistentry>
 
+      <varlistentry id="stats.arenas.i.lg_dirty_mult">
+        <term>
+          <mallctl>stats.arenas.&lt;i&gt;.lg_dirty_mult</mallctl>
+          (<type>ssize_t</type>)
+          <literal>r-</literal>
+        </term>
+        <listitem><para>Minimum ratio (log base 2) of active to dirty pages.
+        See <link
+        linkend="opt.lg_dirty_mult"><mallctl>opt.lg_dirty_mult</mallctl></link>
+        for details.</para></listitem>
+      </varlistentry>
+
       <varlistentry id="stats.arenas.i.nthreads">
         <term>
           <mallctl>stats.arenas.&lt;i&gt;.nthreads</mallctl>
@@ -1768,6 +2142,38 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         <listitem><para>Number of mapped bytes.</para></listitem>
       </varlistentry>
 
+      <varlistentry id="stats.arenas.i.metadata.mapped">
+        <term>
+          <mallctl>stats.arenas.&lt;i&gt;.metadata.mapped</mallctl>
+          (<type>size_t</type>)
+          <literal>r-</literal>
+          [<option>--enable-stats</option>]
+        </term>
+        <listitem><para>Number of mapped bytes in arena chunk headers, which
+        track the states of the non-metadata pages.</para></listitem>
+      </varlistentry>
+
+      <varlistentry id="stats.arenas.i.metadata.allocated">
+        <term>
+          <mallctl>stats.arenas.&lt;i&gt;.metadata.allocated</mallctl>
+          (<type>size_t</type>)
+          <literal>r-</literal>
+          [<option>--enable-stats</option>]
+        </term>
+        <listitem><para>Number of bytes dedicated to internal allocations.
+        Internal allocations differ from application-originated allocations in
+        that they are for internal use, and that they are omitted from heap
+        profiles.  This statistic is reported separately from <link
+        linkend="stats.metadata"><mallctl>stats.metadata</mallctl></link> and
+        <link
+        linkend="stats.arenas.i.metadata.mapped"><mallctl>stats.arenas.&lt;i&gt;.metadata.mapped</mallctl></link>
+        because it overlaps with e.g. the <link
+        linkend="stats.allocated"><mallctl>stats.allocated</mallctl></link> and
+        <link linkend="stats.active"><mallctl>stats.active</mallctl></link>
+        statistics, whereas the other metadata statistics do
+        not.</para></listitem>
+      </varlistentry>
+
       <varlistentry id="stats.arenas.i.npurge">
         <term>
           <mallctl>stats.arenas.&lt;i&gt;.npurge</mallctl>
@@ -1933,17 +2339,6 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         </para></listitem>
       </varlistentry>
 
-      <varlistentry id="stats.arenas.i.bins.j.allocated">
-        <term>
-          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.allocated</mallctl>
-          (<type>size_t</type>)
-          <literal>r-</literal>
-          [<option>--enable-stats</option>]
-        </term>
-        <listitem><para>Current number of bytes allocated by
-        bin.</para></listitem>
-      </varlistentry>
-
       <varlistentry id="stats.arenas.i.bins.j.nmalloc">
         <term>
           <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nmalloc</mallctl>
@@ -1977,6 +2372,17 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         requests.</para></listitem>
       </varlistentry>
 
+      <varlistentry id="stats.arenas.i.bins.j.curregs">
+        <term>
+          <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.curregs</mallctl>
+          (<type>size_t</type>)
+          <literal>r-</literal>
+          [<option>--enable-stats</option>]
+        </term>
+        <listitem><para>Current number of regions for this size
+        class.</para></listitem>
+      </varlistentry>
+
       <varlistentry id="stats.arenas.i.bins.j.nfills">
         <term>
           <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nfills</mallctl>
@@ -2071,6 +2477,50 @@ malloc_conf = "xmalloc:true";]]></programlisting>
         <listitem><para>Current number of runs for this size class.
         </para></listitem>
       </varlistentry>
+
+      <varlistentry id="stats.arenas.i.hchunks.j.nmalloc">
+        <term>
+          <mallctl>stats.arenas.&lt;i&gt;.hchunks.&lt;j&gt;.nmalloc</mallctl>
+          (<type>uint64_t</type>)
+          <literal>r-</literal>
+          [<option>--enable-stats</option>]
+        </term>
+        <listitem><para>Cumulative number of allocation requests for this size
+        class served directly by the arena.</para></listitem>
+      </varlistentry>
+
+      <varlistentry id="stats.arenas.i.hchunks.j.ndalloc">
+        <term>
+          <mallctl>stats.arenas.&lt;i&gt;.hchunks.&lt;j&gt;.ndalloc</mallctl>
+          (<type>uint64_t</type>)
+          <literal>r-</literal>
+          [<option>--enable-stats</option>]
+        </term>
+        <listitem><para>Cumulative number of deallocation requests for this
+        size class served directly by the arena.</para></listitem>
+      </varlistentry>
+
+      <varlistentry id="stats.arenas.i.hchunks.j.nrequests">
+        <term>
+          <mallctl>stats.arenas.&lt;i&gt;.hchunks.&lt;j&gt;.nrequests</mallctl>
+          (<type>uint64_t</type>)
+          <literal>r-</literal>
+          [<option>--enable-stats</option>]
+        </term>
+        <listitem><para>Cumulative number of allocation requests for this size
+        class.</para></listitem>
+      </varlistentry>
+
+      <varlistentry id="stats.arenas.i.hchunks.j.curhchunks">
+        <term>
+          <mallctl>stats.arenas.&lt;i&gt;.hchunks.&lt;j&gt;.curhchunks</mallctl>
+          (<type>size_t</type>)
+          <literal>r-</literal>
+          [<option>--enable-stats</option>]
+        </term>
+        <listitem><para>Current number of huge allocations for this size class.
+        </para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
   <refsect1 id="debugging_malloc_problems">
index 1f9857239c8ac11a4eea1986a02be3202224ccf3..12c617979f9aecb03fb187706b63ab4392b98ad8 100644 (file)
@@ -1,6 +1,8 @@
 /******************************************************************************/
 #ifdef JEMALLOC_H_TYPES
 
+#define        LARGE_MINCLASS          (ZU(1) << LG_LARGE_MINCLASS)
+
 /* Maximum number of regions in one run. */
 #define        LG_RUN_MAXREGS          (LG_PAGE - LG_TINY_MIN)
 #define        RUN_MAXREGS             (1U << LG_RUN_MAXREGS)
 /*
  * The minimum ratio of active:dirty pages per arena is computed as:
  *
- *   (nactive >> opt_lg_dirty_mult) >= ndirty
+ *   (nactive >> lg_dirty_mult) >= ndirty
  *
- * So, supposing that opt_lg_dirty_mult is 3, there can be no less than 8 times
- * as many active pages as dirty pages.
+ * So, supposing that lg_dirty_mult is 3, there can be no less than 8 times as
+ * many active pages as dirty pages.
  */
 #define        LG_DIRTY_MULT_DEFAULT   3
 
+typedef struct arena_runs_dirty_link_s arena_runs_dirty_link_t;
 typedef struct arena_run_s arena_run_t;
 typedef struct arena_chunk_map_bits_s arena_chunk_map_bits_t;
 typedef struct arena_chunk_map_misc_s arena_chunk_map_misc_t;
@@ -33,12 +36,10 @@ typedef struct arena_s arena_t;
 /******************************************************************************/
 #ifdef JEMALLOC_H_STRUCTS
 
+#ifdef JEMALLOC_ARENA_STRUCTS_A
 struct arena_run_s {
-       /* Bin this run is associated with. */
-       arena_bin_t     *bin;
-
-       /* Index of next region that has never been allocated, or nregs. */
-       uint32_t        nextind;
+       /* Index of bin this run is associated with. */
+       szind_t         binind;
 
        /* Number of free regions in run. */
        unsigned        nfree;
@@ -53,15 +54,16 @@ struct arena_chunk_map_bits_s {
         * Run address (or size) and various flags are stored together.  The bit
         * layout looks like (assuming 32-bit system):
         *
-        *   ???????? ???????? ????nnnn nnnndula
+        *   ???????? ???????? ???nnnnn nnndumla
         *
         * ? : Unallocated: Run address for first/last pages, unset for internal
         *                  pages.
         *     Small: Run page offset.
-        *     Large: Run size for first page, unset for trailing pages.
+        *     Large: Run page count for first page, unset for trailing pages.
         * n : binind for small size class, BININD_INVALID for large size class.
         * d : dirty?
         * u : unzeroed?
+        * m : decommitted?
         * l : large?
         * a : allocated?
         *
@@ -73,47 +75,62 @@ struct arena_chunk_map_bits_s {
         * x : don't care
         * - : 0
         * + : 1
-        * [DULA] : bit set
-        * [dula] : bit unset
+        * [DUMLA] : bit set
+        * [dumla] : bit unset
         *
         *   Unallocated (clean):
-        *     ssssssss ssssssss ssss++++ ++++du-a
-        *     xxxxxxxx xxxxxxxx xxxxxxxx xxxx-Uxx
-        *     ssssssss ssssssss ssss++++ ++++dU-a
+        *     ssssssss ssssssss sss+++++ +++dum-a
+        *     xxxxxxxx xxxxxxxx xxxxxxxx xxx-Uxxx
+        *     ssssssss ssssssss sss+++++ +++dUm-a
         *
         *   Unallocated (dirty):
-        *     ssssssss ssssssss ssss++++ ++++D--a
+        *     ssssssss ssssssss sss+++++ +++D-m-a
         *     xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
-        *     ssssssss ssssssss ssss++++ ++++D--a
+        *     ssssssss ssssssss sss+++++ +++D-m-a
         *
         *   Small:
-        *     pppppppp pppppppp ppppnnnn nnnnd--A
-        *     pppppppp pppppppp ppppnnnn nnnn---A
-        *     pppppppp pppppppp ppppnnnn nnnnd--A
+        *     pppppppp pppppppp pppnnnnn nnnd---A
+        *     pppppppp pppppppp pppnnnnn nnn----A
+        *     pppppppp pppppppp pppnnnnn nnnd---A
         *
         *   Large:
-        *     ssssssss ssssssss ssss++++ ++++D-LA
+        *     ssssssss ssssssss sss+++++ +++D--LA
         *     xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
-        *     -------- -------- ----++++ ++++D-LA
+        *     -------- -------- ---+++++ +++D--LA
         *
-        *   Large (sampled, size <= PAGE):
-        *     ssssssss ssssssss ssssnnnn nnnnD-LA
+        *   Large (sampled, size <= LARGE_MINCLASS):
+        *     ssssssss ssssssss sssnnnnn nnnD--LA
+        *     xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
+        *     -------- -------- ---+++++ +++D--LA
         *
-        *   Large (not sampled, size == PAGE):
-        *     ssssssss ssssssss ssss++++ ++++D-LA
+        *   Large (not sampled, size == LARGE_MINCLASS):
+        *     ssssssss ssssssss sss+++++ +++D--LA
+        *     xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
+        *     -------- -------- ---+++++ +++D--LA
         */
        size_t                          bits;
-#define        CHUNK_MAP_BININD_SHIFT  4
+#define        CHUNK_MAP_ALLOCATED     ((size_t)0x01U)
+#define        CHUNK_MAP_LARGE         ((size_t)0x02U)
+#define        CHUNK_MAP_STATE_MASK    ((size_t)0x3U)
+
+#define        CHUNK_MAP_DECOMMITTED   ((size_t)0x04U)
+#define        CHUNK_MAP_UNZEROED      ((size_t)0x08U)
+#define        CHUNK_MAP_DIRTY         ((size_t)0x10U)
+#define        CHUNK_MAP_FLAGS_MASK    ((size_t)0x1cU)
+
+#define        CHUNK_MAP_BININD_SHIFT  5
 #define        BININD_INVALID          ((size_t)0xffU)
-/*     CHUNK_MAP_BININD_MASK == (BININD_INVALID << CHUNK_MAP_BININD_SHIFT) */
-#define        CHUNK_MAP_BININD_MASK   ((size_t)0xff0U)
+#define        CHUNK_MAP_BININD_MASK   (BININD_INVALID << CHUNK_MAP_BININD_SHIFT)
 #define        CHUNK_MAP_BININD_INVALID CHUNK_MAP_BININD_MASK
-#define        CHUNK_MAP_FLAGS_MASK    ((size_t)0xcU)
-#define        CHUNK_MAP_DIRTY         ((size_t)0x8U)
-#define        CHUNK_MAP_UNZEROED      ((size_t)0x4U)
-#define        CHUNK_MAP_LARGE         ((size_t)0x2U)
-#define        CHUNK_MAP_ALLOCATED     ((size_t)0x1U)
-#define        CHUNK_MAP_KEY           CHUNK_MAP_ALLOCATED
+
+#define        CHUNK_MAP_RUNIND_SHIFT  (CHUNK_MAP_BININD_SHIFT + 8)
+#define        CHUNK_MAP_SIZE_SHIFT    (CHUNK_MAP_RUNIND_SHIFT - LG_PAGE)
+#define        CHUNK_MAP_SIZE_MASK                                             \
+    (~(CHUNK_MAP_BININD_MASK | CHUNK_MAP_FLAGS_MASK | CHUNK_MAP_STATE_MASK))
+};
+
+struct arena_runs_dirty_link_s {
+       qr(arena_runs_dirty_link_t)     rd_link;
 };
 
 /*
@@ -127,16 +144,19 @@ struct arena_chunk_map_misc_s {
         *
         * 1) arena_t's runs_avail tree.
         * 2) arena_run_t conceptually uses this linkage for in-use non-full
-        * runs, rather than directly embedding linkage.
+        *    runs, rather than directly embedding linkage.
         */
        rb_node(arena_chunk_map_misc_t)         rb_link;
 
        union {
                /* Linkage for list of dirty runs. */
-               ql_elm(arena_chunk_map_misc_t)  dr_link;
+               arena_runs_dirty_link_t         rd;
 
                /* Profile counters, used for large object runs. */
-               prof_tctx_t                     *prof_tctx;
+               union {
+                       void                            *prof_tctx_pun;
+                       prof_tctx_t                     *prof_tctx;
+               };
 
                /* Small region run metadata. */
                arena_run_t                     run;
@@ -144,12 +164,17 @@ struct arena_chunk_map_misc_s {
 };
 typedef rb_tree(arena_chunk_map_misc_t) arena_avail_tree_t;
 typedef rb_tree(arena_chunk_map_misc_t) arena_run_tree_t;
-typedef ql_head(arena_chunk_map_misc_t) arena_chunk_miscelms_t;
+#endif /* JEMALLOC_ARENA_STRUCTS_A */
 
+#ifdef JEMALLOC_ARENA_STRUCTS_B
 /* Arena chunk header. */
 struct arena_chunk_s {
-       /* Arena that owns the chunk. */
-       arena_t                 *arena;
+       /*
+        * A pointer to the arena that owns the chunk is stored within the node.
+        * This field as a whole is used by chunks_rtree to support both
+        * ivsalloc() and core-based debugging.
+        */
+       extent_node_t           node;
 
        /*
         * Map of pages within chunk that keeps track of free/large/small.  The
@@ -260,8 +285,7 @@ struct arena_s {
        /*
         * There are three classes of arena operations from a locking
         * perspective:
-        * 1) Thread asssignment (modifies nthreads) is protected by
-        *    arenas_lock.
+        * 1) Thread assignment (modifies nthreads) is protected by arenas_lock.
         * 2) Bin-related operations are protected by bin locks.
         * 3) Chunk- and run-related operations are protected by this mutex.
         */
@@ -270,12 +294,19 @@ struct arena_s {
        arena_stats_t           stats;
        /*
         * List of tcaches for extant threads associated with this arena.
-        * Stats from these are merged incrementally, and at exit.
+        * Stats from these are merged incrementally, and at exit if
+        * opt_stats_print is enabled.
         */
        ql_head(tcache_t)       tcache_ql;
 
        uint64_t                prof_accumbytes;
 
+       /*
+        * PRNG state for cache index randomization of large allocation base
+        * pointers.
+        */
+       uint64_t                offset_state;
+
        dss_prec_t              dss_prec;
 
        /*
@@ -290,6 +321,12 @@ struct arena_s {
         */
        arena_chunk_t           *spare;
 
+       /* Minimum ratio (log base 2) of nactive:ndirty. */
+       ssize_t                 lg_dirty_mult;
+
+       /* True if a thread is currently executing arena_purge(). */
+       bool                    purging;
+
        /* Number of pages in active runs and huge regions. */
        size_t                  nactive;
 
@@ -302,53 +339,116 @@ struct arena_s {
        size_t                  ndirty;
 
        /*
-        * Size/address-ordered trees of this arena's available runs.  The trees
-        * are used for first-best-fit run allocation.
+        * Size/address-ordered tree of this arena's available runs.  The tree
+        * is used for first-best-fit run allocation.
         */
        arena_avail_tree_t      runs_avail;
 
-       /* List of dirty runs this arena manages. */
-       arena_chunk_miscelms_t  runs_dirty;
+       /*
+        * Unused dirty memory this arena manages.  Dirty memory is conceptually
+        * tracked as an arbitrarily interleaved LRU of dirty runs and cached
+        * chunks, but the list linkage is actually semi-duplicated in order to
+        * avoid extra arena_chunk_map_misc_t space overhead.
+        *
+        *   LRU-----------------------------------------------------------MRU
+        *
+        *        /-- arena ---\
+        *        |            |
+        *        |            |
+        *        |------------|                             /- chunk -\
+        *   ...->|chunks_cache|<--------------------------->|  /----\ |<--...
+        *        |------------|                             |  |node| |
+        *        |            |                             |  |    | |
+        *        |            |    /- run -\    /- run -\   |  |    | |
+        *        |            |    |       |    |       |   |  |    | |
+        *        |            |    |       |    |       |   |  |    | |
+        *        |------------|    |-------|    |-------|   |  |----| |
+        *   ...->|runs_dirty  |<-->|rd     |<-->|rd     |<---->|rd  |<----...
+        *        |------------|    |-------|    |-------|   |  |----| |
+        *        |            |    |       |    |       |   |  |    | |
+        *        |            |    |       |    |       |   |  \----/ |
+        *        |            |    \-------/    \-------/   |         |
+        *        |            |                             |         |
+        *        |            |                             |         |
+        *        \------------/                             \---------/
+        */
+       arena_runs_dirty_link_t runs_dirty;
+       extent_node_t           chunks_cache;
+
+       /* Extant huge allocations. */
+       ql_head(extent_node_t)  huge;
+       /* Synchronizes all huge allocation/update/deallocation. */
+       malloc_mutex_t          huge_mtx;
 
        /*
-        * user-configureable chunk allocation and deallocation functions.
+        * Trees of chunks that were previously allocated (trees differ only in
+        * node ordering).  These are used when allocating chunks, in an attempt
+        * to re-use address space.  Depending on function, different tree
+        * orderings are needed, which is why there are two trees with the same
+        * contents.
         */
-       chunk_alloc_t           *chunk_alloc;
-       chunk_dalloc_t          *chunk_dalloc;
+       extent_tree_t           chunks_szad_cached;
+       extent_tree_t           chunks_ad_cached;
+       extent_tree_t           chunks_szad_retained;
+       extent_tree_t           chunks_ad_retained;
+
+       malloc_mutex_t          chunks_mtx;
+       /* Cache of nodes that were allocated via base_alloc(). */
+       ql_head(extent_node_t)  node_cache;
+       malloc_mutex_t          node_cache_mtx;
+
+       /* User-configurable chunk hook functions. */
+       chunk_hooks_t           chunk_hooks;
 
        /* bins is used to store trees of free regions. */
        arena_bin_t             bins[NBINS];
 };
+#endif /* JEMALLOC_ARENA_STRUCTS_B */
 
 #endif /* JEMALLOC_H_STRUCTS */
 /******************************************************************************/
 #ifdef JEMALLOC_H_EXTERNS
 
-extern ssize_t opt_lg_dirty_mult;
-/*
- * small_size2bin_tab is a compact lookup table that rounds request sizes up to
- * size classes.  In order to reduce cache footprint, the table is compressed,
- * and all accesses are via small_size2bin().
- */
-extern uint8_t const   small_size2bin_tab[];
-/*
- * small_bin2size_tab duplicates information in arena_bin_info, but in a const
- * array, for which it is easier for the compiler to optimize repeated
- * dereferences.
- */
-extern uint32_t const  small_bin2size_tab[NBINS];
+static const size_t    large_pad =
+#ifdef JEMALLOC_CACHE_OBLIVIOUS
+    PAGE
+#else
+    0
+#endif
+    ;
 
-extern arena_bin_info_t        arena_bin_info[NBINS];
+extern ssize_t         opt_lg_dirty_mult;
 
-/* Number of large size classes. */
-#define                        nlclasses (chunk_npages - map_bias)
+extern arena_bin_info_t        arena_bin_info[NBINS];
 
-void   *arena_chunk_alloc_huge(arena_t *arena, void *new_addr, size_t size,
-    size_t alignment, bool *zero);
-void   arena_chunk_dalloc_huge(arena_t *arena, void *chunk, size_t size);
+extern size_t          map_bias; /* Number of arena chunk header pages. */
+extern size_t          map_misc_offset;
+extern size_t          arena_maxrun; /* Max run size for arenas. */
+extern size_t          large_maxclass; /* Max large size class. */
+extern unsigned                nlclasses; /* Number of large size classes. */
+extern unsigned                nhclasses; /* Number of huge size classes. */
+
+void   arena_chunk_cache_maybe_insert(arena_t *arena, extent_node_t *node,
+    bool cache);
+void   arena_chunk_cache_maybe_remove(arena_t *arena, extent_node_t *node,
+    bool cache);
+extent_node_t  *arena_node_alloc(arena_t *arena);
+void   arena_node_dalloc(arena_t *arena, extent_node_t *node);
+void   *arena_chunk_alloc_huge(arena_t *arena, size_t usize, size_t alignment,
+    bool *zero);
+void   arena_chunk_dalloc_huge(arena_t *arena, void *chunk, size_t usize);
+void   arena_chunk_ralloc_huge_similar(arena_t *arena, void *chunk,
+    size_t oldsize, size_t usize);
+void   arena_chunk_ralloc_huge_shrink(arena_t *arena, void *chunk,
+    size_t oldsize, size_t usize);
+bool   arena_chunk_ralloc_huge_expand(arena_t *arena, void *chunk,
+    size_t oldsize, size_t usize, bool *zero);
+ssize_t        arena_lg_dirty_mult_get(arena_t *arena);
+bool   arena_lg_dirty_mult_set(arena_t *arena, ssize_t lg_dirty_mult);
+void   arena_maybe_purge(arena_t *arena);
 void   arena_purge_all(arena_t *arena);
 void   arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin,
-    size_t binind, uint64_t prof_accumbytes);
+    szind_t binind, uint64_t prof_accumbytes);
 void   arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info,
     bool zero);
 #ifdef JEMALLOC_JET
@@ -363,10 +463,11 @@ void      arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info);
 void   arena_quarantine_junk_small(void *ptr, size_t usize);
 void   *arena_malloc_small(arena_t *arena, size_t size, bool zero);
 void   *arena_malloc_large(arena_t *arena, size_t size, bool zero);
-void   *arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero);
+void   *arena_palloc(tsd_t *tsd, arena_t *arena, size_t usize,
+    size_t alignment, bool zero, tcache_t *tcache);
 void   arena_prof_promoted(const void *ptr, size_t size);
-void   arena_dalloc_bin_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr,
-    arena_chunk_map_bits_t *bitselm);
+void   arena_dalloc_bin_junked_locked(arena_t *arena, arena_chunk_t *chunk,
+    void *ptr, arena_chunk_map_bits_t *bitselm);
 void   arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr,
     size_t pageind, arena_chunk_map_bits_t *bitselm);
 void   arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr,
@@ -374,8 +475,10 @@ void       arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr,
 #ifdef JEMALLOC_JET
 typedef void (arena_dalloc_junk_large_t)(void *, size_t);
 extern arena_dalloc_junk_large_t *arena_dalloc_junk_large;
+#else
+void   arena_dalloc_junk_large(void *ptr, size_t usize);
 #endif
-void   arena_dalloc_large_locked(arena_t *arena, arena_chunk_t *chunk,
+void   arena_dalloc_large_junked_locked(arena_t *arena, arena_chunk_t *chunk,
     void *ptr);
 void   arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr);
 #ifdef JEMALLOC_JET
@@ -385,15 +488,17 @@ extern arena_ralloc_junk_large_t *arena_ralloc_junk_large;
 bool   arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size,
     size_t extra, bool zero);
 void   *arena_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize,
-    size_t size, size_t extra, size_t alignment, bool zero,
-    bool try_tcache_alloc, bool try_tcache_dalloc);
+    size_t size, size_t alignment, bool zero, tcache_t *tcache);
 dss_prec_t     arena_dss_prec_get(arena_t *arena);
 bool   arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);
-void   arena_stats_merge(arena_t *arena, const char **dss, size_t *nactive,
-    size_t *ndirty, arena_stats_t *astats, malloc_bin_stats_t *bstats,
-    malloc_large_stats_t *lstats);
-bool   arena_new(arena_t *arena, unsigned ind);
-void   arena_boot(void);
+ssize_t        arena_lg_dirty_mult_default_get(void);
+bool   arena_lg_dirty_mult_default_set(ssize_t lg_dirty_mult);
+void   arena_stats_merge(arena_t *arena, const char **dss,
+    ssize_t *lg_dirty_mult, size_t *nactive, size_t *ndirty,
+    arena_stats_t *astats, malloc_bin_stats_t *bstats,
+    malloc_large_stats_t *lstats, malloc_huge_stats_t *hstats);
+arena_t        *arena_new(unsigned ind);
+bool   arena_boot(void);
 void   arena_prefork(arena_t *arena);
 void   arena_postfork_parent(arena_t *arena);
 void   arena_postfork_child(arena_t *arena);
@@ -403,209 +508,66 @@ void     arena_postfork_child(arena_t *arena);
 #ifdef JEMALLOC_H_INLINES
 
 #ifndef JEMALLOC_ENABLE_INLINE
-size_t small_size2bin_compute(size_t size);
-size_t small_size2bin_lookup(size_t size);
-size_t small_size2bin(size_t size);
-size_t small_bin2size_compute(size_t binind);
-size_t small_bin2size_lookup(size_t binind);
-size_t small_bin2size(size_t binind);
-size_t small_s2u_compute(size_t size);
-size_t small_s2u_lookup(size_t size);
-size_t small_s2u(size_t size);
 arena_chunk_map_bits_t *arena_bitselm_get(arena_chunk_t *chunk,
     size_t pageind);
 arena_chunk_map_misc_t *arena_miscelm_get(arena_chunk_t *chunk,
     size_t pageind);
 size_t arena_miscelm_to_pageind(arena_chunk_map_misc_t *miscelm);
 void   *arena_miscelm_to_rpages(arena_chunk_map_misc_t *miscelm);
+arena_chunk_map_misc_t *arena_rd_to_miscelm(arena_runs_dirty_link_t *rd);
 arena_chunk_map_misc_t *arena_run_to_miscelm(arena_run_t *run);
 size_t *arena_mapbitsp_get(arena_chunk_t *chunk, size_t pageind);
 size_t arena_mapbitsp_read(size_t *mapbitsp);
 size_t arena_mapbits_get(arena_chunk_t *chunk, size_t pageind);
+size_t arena_mapbits_size_decode(size_t mapbits);
 size_t arena_mapbits_unallocated_size_get(arena_chunk_t *chunk,
     size_t pageind);
 size_t arena_mapbits_large_size_get(arena_chunk_t *chunk, size_t pageind);
 size_t arena_mapbits_small_runind_get(arena_chunk_t *chunk, size_t pageind);
-size_t arena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind);
+szind_t        arena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind);
 size_t arena_mapbits_dirty_get(arena_chunk_t *chunk, size_t pageind);
 size_t arena_mapbits_unzeroed_get(arena_chunk_t *chunk, size_t pageind);
+size_t arena_mapbits_decommitted_get(arena_chunk_t *chunk, size_t pageind);
 size_t arena_mapbits_large_get(arena_chunk_t *chunk, size_t pageind);
 size_t arena_mapbits_allocated_get(arena_chunk_t *chunk, size_t pageind);
 void   arena_mapbitsp_write(size_t *mapbitsp, size_t mapbits);
+size_t arena_mapbits_size_encode(size_t size);
 void   arena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind,
     size_t size, size_t flags);
 void   arena_mapbits_unallocated_size_set(arena_chunk_t *chunk, size_t pageind,
     size_t size);
+void   arena_mapbits_internal_set(arena_chunk_t *chunk, size_t pageind,
+    size_t flags);
 void   arena_mapbits_large_set(arena_chunk_t *chunk, size_t pageind,
     size_t size, size_t flags);
 void   arena_mapbits_large_binind_set(arena_chunk_t *chunk, size_t pageind,
-    size_t binind);
+    szind_t binind);
 void   arena_mapbits_small_set(arena_chunk_t *chunk, size_t pageind,
-    size_t runind, size_t binind, size_t flags);
-void   arena_mapbits_unzeroed_set(arena_chunk_t *chunk, size_t pageind,
-    size_t unzeroed);
+    size_t runind, szind_t binind, size_t flags);
+void   arena_metadata_allocated_add(arena_t *arena, size_t size);
+void   arena_metadata_allocated_sub(arena_t *arena, size_t size);
+size_t arena_metadata_allocated_get(arena_t *arena);
 bool   arena_prof_accum_impl(arena_t *arena, uint64_t accumbytes);
 bool   arena_prof_accum_locked(arena_t *arena, uint64_t accumbytes);
 bool   arena_prof_accum(arena_t *arena, uint64_t accumbytes);
-size_t arena_ptr_small_binind_get(const void *ptr, size_t mapbits);
-size_t arena_bin_index(arena_t *arena, arena_bin_t *bin);
+szind_t        arena_ptr_small_binind_get(const void *ptr, size_t mapbits);
+szind_t        arena_bin_index(arena_t *arena, arena_bin_t *bin);
 unsigned       arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info,
     const void *ptr);
 prof_tctx_t    *arena_prof_tctx_get(const void *ptr);
-void   arena_prof_tctx_set(const void *ptr, prof_tctx_t *tctx);
+void   arena_prof_tctx_set(const void *ptr, size_t usize, prof_tctx_t *tctx);
+void   arena_prof_tctx_reset(const void *ptr, size_t usize,
+    const void *old_ptr, prof_tctx_t *old_tctx);
 void   *arena_malloc(tsd_t *tsd, arena_t *arena, size_t size, bool zero,
-    bool try_tcache);
+    tcache_t *tcache);
+arena_t        *arena_aalloc(const void *ptr);
 size_t arena_salloc(const void *ptr, bool demote);
-void   arena_dalloc(tsd_t *tsd, arena_chunk_t *chunk, void *ptr,
-    bool try_tcache);
-void   arena_sdalloc(tsd_t *tsd, arena_chunk_t *chunk, void *ptr, size_t size,
-    bool try_tcache);
+void   arena_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache);
+void   arena_sdalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache);
 #endif
 
 #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_ARENA_C_))
 #  ifdef JEMALLOC_ARENA_INLINE_A
-JEMALLOC_INLINE size_t
-small_size2bin_compute(size_t size)
-{
-#if (NTBINS != 0)
-       if (size <= (ZU(1) << LG_TINY_MAXCLASS)) {
-               size_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1;
-               size_t lg_ceil = lg_floor(pow2_ceil(size));
-               return (lg_ceil < lg_tmin ? 0 : lg_ceil - lg_tmin);
-       } else
-#endif
-       {
-               size_t x = lg_floor((size<<1)-1);
-               size_t shift = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM) ? 0 :
-                   x - (LG_SIZE_CLASS_GROUP + LG_QUANTUM);
-               size_t grp = shift << LG_SIZE_CLASS_GROUP;
-
-               size_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM + 1)
-                   ? LG_QUANTUM : x - LG_SIZE_CLASS_GROUP - 1;
-
-               size_t delta_inverse_mask = ZI(-1) << lg_delta;
-               size_t mod = ((((size-1) & delta_inverse_mask) >> lg_delta)) &
-                   ((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);
-
-               size_t bin = NTBINS + grp + mod;
-               return (bin);
-       }
-}
-
-JEMALLOC_ALWAYS_INLINE size_t
-small_size2bin_lookup(size_t size)
-{
-
-       assert(size <= LOOKUP_MAXCLASS);
-       {
-               size_t ret = ((size_t)(small_size2bin_tab[(size-1) >>
-                   LG_TINY_MIN]));
-               assert(ret == small_size2bin_compute(size));
-               return (ret);
-       }
-}
-
-JEMALLOC_ALWAYS_INLINE size_t
-small_size2bin(size_t size)
-{
-
-       assert(size > 0);
-       if (likely(size <= LOOKUP_MAXCLASS))
-               return (small_size2bin_lookup(size));
-       else
-               return (small_size2bin_compute(size));
-}
-
-JEMALLOC_INLINE size_t
-small_bin2size_compute(size_t binind)
-{
-#if (NTBINS > 0)
-       if (binind < NTBINS)
-               return (ZU(1) << (LG_TINY_MAXCLASS - NTBINS + 1 + binind));
-       else
-#endif
-       {
-               size_t reduced_binind = binind - NTBINS;
-               size_t grp = reduced_binind >> LG_SIZE_CLASS_GROUP;
-               size_t mod = reduced_binind & ((ZU(1) << LG_SIZE_CLASS_GROUP) -
-                   1);
-
-               size_t grp_size_mask = ~((!!grp)-1);
-               size_t grp_size = ((ZU(1) << (LG_QUANTUM +
-                   (LG_SIZE_CLASS_GROUP-1))) << grp) & grp_size_mask;
-
-               size_t shift = (grp == 0) ? 1 : grp;
-               size_t lg_delta = shift + (LG_QUANTUM-1);
-               size_t mod_size = (mod+1) << lg_delta;
-
-               size_t usize = grp_size + mod_size;
-               return (usize);
-       }
-}
-
-JEMALLOC_ALWAYS_INLINE size_t
-small_bin2size_lookup(size_t binind)
-{
-
-       assert(binind < NBINS);
-       {
-               size_t ret = (size_t)small_bin2size_tab[binind];
-               assert(ret == small_bin2size_compute(binind));
-               return (ret);
-       }
-}
-
-JEMALLOC_ALWAYS_INLINE size_t
-small_bin2size(size_t binind)
-{
-
-       return (small_bin2size_lookup(binind));
-}
-
-JEMALLOC_ALWAYS_INLINE size_t
-small_s2u_compute(size_t size)
-{
-#if (NTBINS > 0)
-       if (size <= (ZU(1) << LG_TINY_MAXCLASS)) {
-               size_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1;
-               size_t lg_ceil = lg_floor(pow2_ceil(size));
-               return (lg_ceil < lg_tmin ? (ZU(1) << lg_tmin) :
-                   (ZU(1) << lg_ceil));
-       } else
-#endif
-       {
-               size_t x = lg_floor((size<<1)-1);
-               size_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM + 1)
-                   ?  LG_QUANTUM : x - LG_SIZE_CLASS_GROUP - 1;
-               size_t delta = ZU(1) << lg_delta;
-               size_t delta_mask = delta - 1;
-               size_t usize = (size + delta_mask) & ~delta_mask;
-               return (usize);
-       }
-}
-
-JEMALLOC_ALWAYS_INLINE size_t
-small_s2u_lookup(size_t size)
-{
-       size_t ret = small_bin2size(small_size2bin(size));
-
-       assert(ret == small_s2u_compute(size));
-       return (ret);
-}
-
-JEMALLOC_ALWAYS_INLINE size_t
-small_s2u(size_t size)
-{
-
-       assert(size > 0);
-       if (likely(size <= LOOKUP_MAXCLASS))
-               return (small_s2u_lookup(size));
-       else
-               return (small_s2u_compute(size));
-}
-#  endif /* JEMALLOC_ARENA_INLINE_A */
-
-#  ifdef JEMALLOC_ARENA_INLINE_B
 JEMALLOC_ALWAYS_INLINE arena_chunk_map_bits_t *
 arena_bitselm_get(arena_chunk_t *chunk, size_t pageind)
 {
@@ -649,6 +611,18 @@ arena_miscelm_to_rpages(arena_chunk_map_misc_t *miscelm)
        return ((void *)((uintptr_t)chunk + (pageind << LG_PAGE)));
 }
 
+JEMALLOC_ALWAYS_INLINE arena_chunk_map_misc_t *
+arena_rd_to_miscelm(arena_runs_dirty_link_t *rd)
+{
+       arena_chunk_map_misc_t *miscelm = (arena_chunk_map_misc_t
+           *)((uintptr_t)rd - offsetof(arena_chunk_map_misc_t, rd));
+
+       assert(arena_miscelm_to_pageind(miscelm) >= map_bias);
+       assert(arena_miscelm_to_pageind(miscelm) < chunk_npages);
+
+       return (miscelm);
+}
+
 JEMALLOC_ALWAYS_INLINE arena_chunk_map_misc_t *
 arena_run_to_miscelm(arena_run_t *run)
 {
@@ -682,6 +656,22 @@ arena_mapbits_get(arena_chunk_t *chunk, size_t pageind)
        return (arena_mapbitsp_read(arena_mapbitsp_get(chunk, pageind)));
 }
 
+JEMALLOC_ALWAYS_INLINE size_t
+arena_mapbits_size_decode(size_t mapbits)
+{
+       size_t size;
+
+#if CHUNK_MAP_SIZE_SHIFT > 0
+       size = (mapbits & CHUNK_MAP_SIZE_MASK) >> CHUNK_MAP_SIZE_SHIFT;
+#elif CHUNK_MAP_SIZE_SHIFT == 0
+       size = mapbits & CHUNK_MAP_SIZE_MASK;
+#else
+       size = (mapbits & CHUNK_MAP_SIZE_MASK) << -CHUNK_MAP_SIZE_SHIFT;
+#endif
+
+       return (size);
+}
+
 JEMALLOC_ALWAYS_INLINE size_t
 arena_mapbits_unallocated_size_get(arena_chunk_t *chunk, size_t pageind)
 {
@@ -689,7 +679,7 @@ arena_mapbits_unallocated_size_get(arena_chunk_t *chunk, size_t pageind)
 
        mapbits = arena_mapbits_get(chunk, pageind);
        assert((mapbits & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) == 0);
-       return (mapbits & ~PAGE_MASK);
+       return (arena_mapbits_size_decode(mapbits));
 }
 
 JEMALLOC_ALWAYS_INLINE size_t
@@ -700,7 +690,7 @@ arena_mapbits_large_size_get(arena_chunk_t *chunk, size_t pageind)
        mapbits = arena_mapbits_get(chunk, pageind);
        assert((mapbits & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) ==
            (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED));
-       return (mapbits & ~PAGE_MASK);
+       return (arena_mapbits_size_decode(mapbits));
 }
 
 JEMALLOC_ALWAYS_INLINE size_t
@@ -711,14 +701,14 @@ arena_mapbits_small_runind_get(arena_chunk_t *chunk, size_t pageind)
        mapbits = arena_mapbits_get(chunk, pageind);
        assert((mapbits & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) ==
            CHUNK_MAP_ALLOCATED);
-       return (mapbits >> LG_PAGE);
+       return (mapbits >> CHUNK_MAP_RUNIND_SHIFT);
 }
 
-JEMALLOC_ALWAYS_INLINE size_t
+JEMALLOC_ALWAYS_INLINE szind_t
 arena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind)
 {
        size_t mapbits;
-       size_t binind;
+       szind_t binind;
 
        mapbits = arena_mapbits_get(chunk, pageind);
        binind = (mapbits & CHUNK_MAP_BININD_MASK) >> CHUNK_MAP_BININD_SHIFT;
@@ -732,6 +722,8 @@ arena_mapbits_dirty_get(arena_chunk_t *chunk, size_t pageind)
        size_t mapbits;
 
        mapbits = arena_mapbits_get(chunk, pageind);
+       assert((mapbits & CHUNK_MAP_DECOMMITTED) == 0 || (mapbits &
+           (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == 0);
        return (mapbits & CHUNK_MAP_DIRTY);
 }
 
@@ -741,9 +733,22 @@ arena_mapbits_unzeroed_get(arena_chunk_t *chunk, size_t pageind)
        size_t mapbits;
 
        mapbits = arena_mapbits_get(chunk, pageind);
+       assert((mapbits & CHUNK_MAP_DECOMMITTED) == 0 || (mapbits &
+           (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == 0);
        return (mapbits & CHUNK_MAP_UNZEROED);
 }
 
+JEMALLOC_ALWAYS_INLINE size_t
+arena_mapbits_decommitted_get(arena_chunk_t *chunk, size_t pageind)
+{
+       size_t mapbits;
+
+       mapbits = arena_mapbits_get(chunk, pageind);
+       assert((mapbits & CHUNK_MAP_DECOMMITTED) == 0 || (mapbits &
+           (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == 0);
+       return (mapbits & CHUNK_MAP_DECOMMITTED);
+}
+
 JEMALLOC_ALWAYS_INLINE size_t
 arena_mapbits_large_get(arena_chunk_t *chunk, size_t pageind)
 {
@@ -769,6 +774,23 @@ arena_mapbitsp_write(size_t *mapbitsp, size_t mapbits)
        *mapbitsp = mapbits;
 }
 
+JEMALLOC_ALWAYS_INLINE size_t
+arena_mapbits_size_encode(size_t size)
+{
+       size_t mapbits;
+
+#if CHUNK_MAP_SIZE_SHIFT > 0
+       mapbits = size << CHUNK_MAP_SIZE_SHIFT;
+#elif CHUNK_MAP_SIZE_SHIFT == 0
+       mapbits = size;
+#else
+       mapbits = size >> -CHUNK_MAP_SIZE_SHIFT;
+#endif
+
+       assert((mapbits & ~CHUNK_MAP_SIZE_MASK) == 0);
+       return (mapbits);
+}
+
 JEMALLOC_ALWAYS_INLINE void
 arena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind, size_t size,
     size_t flags)
@@ -776,9 +798,11 @@ arena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind, size_t size,
        size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind);
 
        assert((size & PAGE_MASK) == 0);
-       assert((flags & ~CHUNK_MAP_FLAGS_MASK) == 0);
-       assert((flags & (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == flags);
-       arena_mapbitsp_write(mapbitsp, size | CHUNK_MAP_BININD_INVALID | flags);
+       assert((flags & CHUNK_MAP_FLAGS_MASK) == flags);
+       assert((flags & CHUNK_MAP_DECOMMITTED) == 0 || (flags &
+           (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == 0);
+       arena_mapbitsp_write(mapbitsp, arena_mapbits_size_encode(size) |
+           CHUNK_MAP_BININD_INVALID | flags);
 }
 
 JEMALLOC_ALWAYS_INLINE void
@@ -790,7 +814,17 @@ arena_mapbits_unallocated_size_set(arena_chunk_t *chunk, size_t pageind,
 
        assert((size & PAGE_MASK) == 0);
        assert((mapbits & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) == 0);
-       arena_mapbitsp_write(mapbitsp, size | (mapbits & PAGE_MASK));
+       arena_mapbitsp_write(mapbitsp, arena_mapbits_size_encode(size) |
+           (mapbits & ~CHUNK_MAP_SIZE_MASK));
+}
+
+JEMALLOC_ALWAYS_INLINE void
+arena_mapbits_internal_set(arena_chunk_t *chunk, size_t pageind, size_t flags)
+{
+       size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind);
+
+       assert((flags & CHUNK_MAP_UNZEROED) == flags);
+       arena_mapbitsp_write(mapbitsp, flags);
 }
 
 JEMALLOC_ALWAYS_INLINE void
@@ -798,54 +832,62 @@ arena_mapbits_large_set(arena_chunk_t *chunk, size_t pageind, size_t size,
     size_t flags)
 {
        size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind);
-       size_t mapbits = arena_mapbitsp_read(mapbitsp);
-       size_t unzeroed;
 
        assert((size & PAGE_MASK) == 0);
-       assert((flags & CHUNK_MAP_DIRTY) == flags);
-       unzeroed = mapbits & CHUNK_MAP_UNZEROED; /* Preserve unzeroed. */
-       arena_mapbitsp_write(mapbitsp, size | CHUNK_MAP_BININD_INVALID | flags
-           | unzeroed | CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED);
+       assert((flags & CHUNK_MAP_FLAGS_MASK) == flags);
+       assert((flags & CHUNK_MAP_DECOMMITTED) == 0 || (flags &
+           (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == 0);
+       arena_mapbitsp_write(mapbitsp, arena_mapbits_size_encode(size) |
+           CHUNK_MAP_BININD_INVALID | flags | CHUNK_MAP_LARGE |
+           CHUNK_MAP_ALLOCATED);
 }
 
 JEMALLOC_ALWAYS_INLINE void
 arena_mapbits_large_binind_set(arena_chunk_t *chunk, size_t pageind,
-    size_t binind)
+    szind_t binind)
 {
        size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind);
        size_t mapbits = arena_mapbitsp_read(mapbitsp);
 
        assert(binind <= BININD_INVALID);
-       assert(arena_mapbits_large_size_get(chunk, pageind) == PAGE);
+       assert(arena_mapbits_large_size_get(chunk, pageind) == LARGE_MINCLASS +
+           large_pad);
        arena_mapbitsp_write(mapbitsp, (mapbits & ~CHUNK_MAP_BININD_MASK) |
            (binind << CHUNK_MAP_BININD_SHIFT));
 }
 
 JEMALLOC_ALWAYS_INLINE void
 arena_mapbits_small_set(arena_chunk_t *chunk, size_t pageind, size_t runind,
-    size_t binind, size_t flags)
+    szind_t binind, size_t flags)
 {
        size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind);
-       size_t mapbits = arena_mapbitsp_read(mapbitsp);
-       size_t unzeroed;
 
        assert(binind < BININD_INVALID);
        assert(pageind - runind >= map_bias);
-       assert((flags & CHUNK_MAP_DIRTY) == flags);
-       unzeroed = mapbits & CHUNK_MAP_UNZEROED; /* Preserve unzeroed. */
-       arena_mapbitsp_write(mapbitsp, (runind << LG_PAGE) | (binind <<
-           CHUNK_MAP_BININD_SHIFT) | flags | unzeroed | CHUNK_MAP_ALLOCATED);
+       assert((flags & CHUNK_MAP_UNZEROED) == flags);
+       arena_mapbitsp_write(mapbitsp, (runind << CHUNK_MAP_RUNIND_SHIFT) |
+           (binind << CHUNK_MAP_BININD_SHIFT) | flags | CHUNK_MAP_ALLOCATED);
 }
 
-JEMALLOC_ALWAYS_INLINE void
-arena_mapbits_unzeroed_set(arena_chunk_t *chunk, size_t pageind,
-    size_t unzeroed)
+JEMALLOC_INLINE void
+arena_metadata_allocated_add(arena_t *arena, size_t size)
 {
-       size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind);
-       size_t mapbits = arena_mapbitsp_read(mapbitsp);
 
-       arena_mapbitsp_write(mapbitsp, (mapbits & ~CHUNK_MAP_UNZEROED) |
-           unzeroed);
+       atomic_add_z(&arena->stats.metadata_allocated, size);
+}
+
+JEMALLOC_INLINE void
+arena_metadata_allocated_sub(arena_t *arena, size_t size)
+{
+
+       atomic_sub_z(&arena->stats.metadata_allocated, size);
+}
+
+JEMALLOC_INLINE size_t
+arena_metadata_allocated_get(arena_t *arena)
+{
+
+       return (atomic_read_z(&arena->stats.metadata_allocated));
 }
 
 JEMALLOC_INLINE bool
@@ -893,10 +935,10 @@ arena_prof_accum(arena_t *arena, uint64_t accumbytes)
        }
 }
 
-JEMALLOC_ALWAYS_INLINE size_t
+JEMALLOC_ALWAYS_INLINE szind_t
 arena_ptr_small_binind_get(const void *ptr, size_t mapbits)
 {
-       size_t binind;
+       szind_t binind;
 
        binind = (mapbits & CHUNK_MAP_BININD_MASK) >> CHUNK_MAP_BININD_SHIFT;
 
@@ -908,7 +950,7 @@ arena_ptr_small_binind_get(const void *ptr, size_t mapbits)
                size_t rpages_ind;
                arena_run_t *run;
                arena_bin_t *bin;
-               size_t actual_binind;
+               szind_t run_binind, actual_binind;
                arena_bin_info_t *bin_info;
                arena_chunk_map_misc_t *miscelm;
                void *rpages;
@@ -916,7 +958,7 @@ arena_ptr_small_binind_get(const void *ptr, size_t mapbits)
                assert(binind != BININD_INVALID);
                assert(binind < NBINS);
                chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-               arena = chunk->arena;
+               arena = extent_node_arena_get(&chunk->node);
                pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
                actual_mapbits = arena_mapbits_get(chunk, pageind);
                assert(mapbits == actual_mapbits);
@@ -926,9 +968,10 @@ arena_ptr_small_binind_get(const void *ptr, size_t mapbits)
                    pageind);
                miscelm = arena_miscelm_get(chunk, rpages_ind);
                run = &miscelm->run;
-               bin = run->bin;
+               run_binind = run->binind;
+               bin = &arena->bins[run_binind];
                actual_binind = bin - arena->bins;
-               assert(binind == actual_binind);
+               assert(run_binind == actual_binind);
                bin_info = &arena_bin_info[actual_binind];
                rpages = arena_miscelm_to_rpages(miscelm);
                assert(((uintptr_t)ptr - ((uintptr_t)rpages +
@@ -938,13 +981,13 @@ arena_ptr_small_binind_get(const void *ptr, size_t mapbits)
 
        return (binind);
 }
-#  endif /* JEMALLOC_ARENA_INLINE_B */
+#  endif /* JEMALLOC_ARENA_INLINE_A */
 
-#  ifdef JEMALLOC_ARENA_INLINE_C
-JEMALLOC_INLINE size_t
+#  ifdef JEMALLOC_ARENA_INLINE_B
+JEMALLOC_INLINE szind_t
 arena_bin_index(arena_t *arena, arena_bin_t *bin)
 {
-       size_t binind = bin - arena->bins;
+       szind_t binind = bin - arena->bins;
        assert(binind < NBINS);
        return (binind);
 }
@@ -1028,72 +1071,133 @@ arena_prof_tctx_get(const void *ptr)
 {
        prof_tctx_t *ret;
        arena_chunk_t *chunk;
-       size_t pageind, mapbits;
 
        cassert(config_prof);
        assert(ptr != NULL);
-       assert(CHUNK_ADDR2BASE(ptr) != ptr);
 
        chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-       pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
-       mapbits = arena_mapbits_get(chunk, pageind);
-       assert((mapbits & CHUNK_MAP_ALLOCATED) != 0);
-       if (likely((mapbits & CHUNK_MAP_LARGE) == 0))
-               ret = (prof_tctx_t *)(uintptr_t)1U;
-       else
-               ret = arena_miscelm_get(chunk, pageind)->prof_tctx;
+       if (likely(chunk != ptr)) {
+               size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
+               size_t mapbits = arena_mapbits_get(chunk, pageind);
+               assert((mapbits & CHUNK_MAP_ALLOCATED) != 0);
+               if (likely((mapbits & CHUNK_MAP_LARGE) == 0))
+                       ret = (prof_tctx_t *)(uintptr_t)1U;
+               else {
+                       arena_chunk_map_misc_t *elm = arena_miscelm_get(chunk,
+                           pageind);
+                       ret = atomic_read_p(&elm->prof_tctx_pun);
+               }
+       } else
+               ret = huge_prof_tctx_get(ptr);
 
        return (ret);
 }
 
 JEMALLOC_INLINE void
-arena_prof_tctx_set(const void *ptr, prof_tctx_t *tctx)
+arena_prof_tctx_set(const void *ptr, size_t usize, prof_tctx_t *tctx)
 {
        arena_chunk_t *chunk;
-       size_t pageind;
 
        cassert(config_prof);
        assert(ptr != NULL);
-       assert(CHUNK_ADDR2BASE(ptr) != ptr);
 
        chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-       pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
-       assert(arena_mapbits_allocated_get(chunk, pageind) != 0);
+       if (likely(chunk != ptr)) {
+               size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
+
+               assert(arena_mapbits_allocated_get(chunk, pageind) != 0);
 
-       if (unlikely(arena_mapbits_large_get(chunk, pageind) != 0))
-               arena_miscelm_get(chunk, pageind)->prof_tctx = tctx;
+               if (unlikely(usize > SMALL_MAXCLASS || (uintptr_t)tctx >
+                   (uintptr_t)1U)) {
+                       arena_chunk_map_misc_t *elm;
+
+                       assert(arena_mapbits_large_get(chunk, pageind) != 0);
+
+                       elm = arena_miscelm_get(chunk, pageind);
+                       atomic_write_p(&elm->prof_tctx_pun, tctx);
+               } else {
+                       /*
+                        * tctx must always be initialized for large runs.
+                        * Assert that the surrounding conditional logic is
+                        * equivalent to checking whether ptr refers to a large
+                        * run.
+                        */
+                       assert(arena_mapbits_large_get(chunk, pageind) == 0);
+               }
+       } else
+               huge_prof_tctx_set(ptr, tctx);
+}
+
+JEMALLOC_INLINE void
+arena_prof_tctx_reset(const void *ptr, size_t usize, const void *old_ptr,
+    prof_tctx_t *old_tctx)
+{
+
+       cassert(config_prof);
+       assert(ptr != NULL);
+
+       if (unlikely(usize > SMALL_MAXCLASS || (ptr == old_ptr &&
+           (uintptr_t)old_tctx > (uintptr_t)1U))) {
+               arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+               if (likely(chunk != ptr)) {
+                       size_t pageind;
+                       arena_chunk_map_misc_t *elm;
+
+                       pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >>
+                           LG_PAGE;
+                       assert(arena_mapbits_allocated_get(chunk, pageind) !=
+                           0);
+                       assert(arena_mapbits_large_get(chunk, pageind) != 0);
+
+                       elm = arena_miscelm_get(chunk, pageind);
+                       atomic_write_p(&elm->prof_tctx_pun,
+                           (prof_tctx_t *)(uintptr_t)1U);
+               } else
+                       huge_prof_tctx_reset(ptr);
+       }
 }
 
 JEMALLOC_ALWAYS_INLINE void *
 arena_malloc(tsd_t *tsd, arena_t *arena, size_t size, bool zero,
-    bool try_tcache)
+    tcache_t *tcache)
 {
-       tcache_t *tcache;
 
        assert(size != 0);
-       assert(size <= arena_maxclass);
+
+       arena = arena_choose(tsd, arena);
+       if (unlikely(arena == NULL))
+               return (NULL);
 
        if (likely(size <= SMALL_MAXCLASS)) {
-               if (likely(try_tcache) && likely((tcache = tcache_get(tsd,
-                   true)) != NULL))
-                       return (tcache_alloc_small(tcache, size, zero));
-               else {
-                       return (arena_malloc_small(choose_arena(tsd, arena),
-                           size, zero));
-               }
-       } else {
+               if (likely(tcache != NULL)) {
+                       return (tcache_alloc_small(tsd, arena, tcache, size,
+                           zero));
+               } else
+                       return (arena_malloc_small(arena, size, zero));
+       } else if (likely(size <= large_maxclass)) {
                /*
                 * Initialize tcache after checking size in order to avoid
                 * infinite recursion during tcache initialization.
                 */
-               if (try_tcache && size <= tcache_maxclass && likely((tcache =
-                   tcache_get(tsd, true)) != NULL))
-                       return (tcache_alloc_large(tcache, size, zero));
-               else {
-                       return (arena_malloc_large(choose_arena(tsd, arena),
-                           size, zero));
-               }
-       }
+               if (likely(tcache != NULL) && size <= tcache_maxclass) {
+                       return (tcache_alloc_large(tsd, arena, tcache, size,
+                           zero));
+               } else
+                       return (arena_malloc_large(arena, size, zero));
+       } else
+               return (huge_malloc(tsd, arena, size, zero, tcache));
+}
+
+JEMALLOC_ALWAYS_INLINE arena_t *
+arena_aalloc(const void *ptr)
+{
+       arena_chunk_t *chunk;
+
+       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+       if (likely(chunk != ptr))
+               return (extent_node_arena_get(&chunk->node));
+       else
+               return (huge_aalloc(ptr));
 }
 
 /* Return the size of the allocation pointed to by ptr. */
@@ -1102,108 +1206,141 @@ arena_salloc(const void *ptr, bool demote)
 {
        size_t ret;
        arena_chunk_t *chunk;
-       size_t pageind, binind;
+       size_t pageind;
+       szind_t binind;
 
        assert(ptr != NULL);
-       assert(CHUNK_ADDR2BASE(ptr) != ptr);
 
        chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-       pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
-       assert(arena_mapbits_allocated_get(chunk, pageind) != 0);
-       binind = arena_mapbits_binind_get(chunk, pageind);
-       if (unlikely(binind == BININD_INVALID || (config_prof && !demote &&
-           arena_mapbits_large_get(chunk, pageind) != 0))) {
-               /*
-                * Large allocation.  In the common case (demote), and as this
-                * is an inline function, most callers will only end up looking
-                * at binind to determine that ptr is a small allocation.
-                */
-               assert(((uintptr_t)ptr & PAGE_MASK) == 0);
-               ret = arena_mapbits_large_size_get(chunk, pageind);
-               assert(ret != 0);
-               assert(pageind + (ret>>LG_PAGE) <= chunk_npages);
-               assert(ret == PAGE || arena_mapbits_large_size_get(chunk,
-                   pageind+(ret>>LG_PAGE)-1) == 0);
-               assert(binind == arena_mapbits_binind_get(chunk,
-                   pageind+(ret>>LG_PAGE)-1));
-               assert(arena_mapbits_dirty_get(chunk, pageind) ==
-                   arena_mapbits_dirty_get(chunk, pageind+(ret>>LG_PAGE)-1));
-       } else {
-               /* Small allocation (possibly promoted to a large object). */
-               assert(arena_mapbits_large_get(chunk, pageind) != 0 ||
-                   arena_ptr_small_binind_get(ptr, arena_mapbits_get(chunk,
-                   pageind)) == binind);
-               ret = small_bin2size(binind);
-       }
+       if (likely(chunk != ptr)) {
+               pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
+               assert(arena_mapbits_allocated_get(chunk, pageind) != 0);
+               binind = arena_mapbits_binind_get(chunk, pageind);
+               if (unlikely(binind == BININD_INVALID || (config_prof && !demote
+                   && arena_mapbits_large_get(chunk, pageind) != 0))) {
+                       /*
+                        * Large allocation.  In the common case (demote), and
+                        * as this is an inline function, most callers will only
+                        * end up looking at binind to determine that ptr is a
+                        * small allocation.
+                        */
+                       assert(config_cache_oblivious || ((uintptr_t)ptr &
+                           PAGE_MASK) == 0);
+                       ret = arena_mapbits_large_size_get(chunk, pageind) -
+                           large_pad;
+                       assert(ret != 0);
+                       assert(pageind + ((ret+large_pad)>>LG_PAGE) <=
+                           chunk_npages);
+                       assert(arena_mapbits_dirty_get(chunk, pageind) ==
+                           arena_mapbits_dirty_get(chunk,
+                           pageind+((ret+large_pad)>>LG_PAGE)-1));
+               } else {
+                       /*
+                        * Small allocation (possibly promoted to a large
+                        * object).
+                        */
+                       assert(arena_mapbits_large_get(chunk, pageind) != 0 ||
+                           arena_ptr_small_binind_get(ptr,
+                           arena_mapbits_get(chunk, pageind)) == binind);
+                       ret = index2size(binind);
+               }
+       } else
+               ret = huge_salloc(ptr);
 
        return (ret);
 }
 
 JEMALLOC_ALWAYS_INLINE void
-arena_dalloc(tsd_t *tsd, arena_chunk_t *chunk, void *ptr, bool try_tcache)
+arena_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache)
 {
+       arena_chunk_t *chunk;
        size_t pageind, mapbits;
-       tcache_t *tcache;
 
        assert(ptr != NULL);
-       assert(CHUNK_ADDR2BASE(ptr) != ptr);
-
-       pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
-       mapbits = arena_mapbits_get(chunk, pageind);
-       assert(arena_mapbits_allocated_get(chunk, pageind) != 0);
-       if (likely((mapbits & CHUNK_MAP_LARGE) == 0)) {
-               /* Small allocation. */
-               if (likely(try_tcache) && likely((tcache = tcache_get(tsd,
-                   false)) != NULL)) {
-                       size_t binind = arena_ptr_small_binind_get(ptr,
-                           mapbits);
-                       tcache_dalloc_small(tcache, ptr, binind);
-               } else
-                       arena_dalloc_small(chunk->arena, chunk, ptr, pageind);
-       } else {
-               size_t size = arena_mapbits_large_size_get(chunk, pageind);
 
-               assert(((uintptr_t)ptr & PAGE_MASK) == 0);
-
-               if (try_tcache && size <= tcache_maxclass && likely((tcache =
-                   tcache_get(tsd, false)) != NULL)) {
-                       tcache_dalloc_large(tcache, ptr, size);
-               } else
-                       arena_dalloc_large(chunk->arena, chunk, ptr);
-       }
+       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+       if (likely(chunk != ptr)) {
+               pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
+               mapbits = arena_mapbits_get(chunk, pageind);
+               assert(arena_mapbits_allocated_get(chunk, pageind) != 0);
+               if (likely((mapbits & CHUNK_MAP_LARGE) == 0)) {
+                       /* Small allocation. */
+                       if (likely(tcache != NULL)) {
+                               szind_t binind = arena_ptr_small_binind_get(ptr,
+                                   mapbits);
+                               tcache_dalloc_small(tsd, tcache, ptr, binind);
+                       } else {
+                               arena_dalloc_small(extent_node_arena_get(
+                                   &chunk->node), chunk, ptr, pageind);
+                       }
+               } else {
+                       size_t size = arena_mapbits_large_size_get(chunk,
+                           pageind);
+
+                       assert(config_cache_oblivious || ((uintptr_t)ptr &
+                           PAGE_MASK) == 0);
+
+                       if (likely(tcache != NULL) && size - large_pad <=
+                           tcache_maxclass) {
+                               tcache_dalloc_large(tsd, tcache, ptr, size -
+                                   large_pad);
+                       } else {
+                               arena_dalloc_large(extent_node_arena_get(
+                                   &chunk->node), chunk, ptr);
+                       }
+               }
+       } else
+               huge_dalloc(tsd, ptr, tcache);
 }
 
 JEMALLOC_ALWAYS_INLINE void
-arena_sdalloc(tsd_t *tsd, arena_chunk_t *chunk, void *ptr, size_t size,
-    bool try_tcache)
+arena_sdalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache)
 {
-       tcache_t *tcache;
-
-       assert(ptr != NULL);
-       assert(CHUNK_ADDR2BASE(ptr) != ptr);
+       arena_chunk_t *chunk;
 
-       if (likely(size <= SMALL_MAXCLASS)) {
-               /* Small allocation. */
-               if (likely(try_tcache) && likely((tcache = tcache_get(tsd,
-                   false)) != NULL)) {
-                       size_t binind = small_size2bin(size);
-                       tcache_dalloc_small(tcache, ptr, binind);
-               } else {
+       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+       if (likely(chunk != ptr)) {
+               if (config_prof && opt_prof) {
                        size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >>
                            LG_PAGE;
-                       arena_dalloc_small(chunk->arena, chunk, ptr, pageind);
+                       assert(arena_mapbits_allocated_get(chunk, pageind) != 0);
+                       if (arena_mapbits_large_get(chunk, pageind) != 0) {
+                               /*
+                                * Make sure to use promoted size, not request
+                                * size.
+                                */
+                               size = arena_mapbits_large_size_get(chunk,
+                                   pageind) - large_pad;
+                       }
                }
-       } else {
-               assert(((uintptr_t)ptr & PAGE_MASK) == 0);
-
-               if (try_tcache && size <= tcache_maxclass && (tcache =
-                   tcache_get(tsd, false)) != NULL) {
-                       tcache_dalloc_large(tcache, ptr, size);
-               } else
-                       arena_dalloc_large(chunk->arena, chunk, ptr);
-       }
+               assert(s2u(size) == s2u(arena_salloc(ptr, false)));
+
+               if (likely(size <= SMALL_MAXCLASS)) {
+                       /* Small allocation. */
+                       if (likely(tcache != NULL)) {
+                               szind_t binind = size2index(size);
+                               tcache_dalloc_small(tsd, tcache, ptr, binind);
+                       } else {
+                               size_t pageind = ((uintptr_t)ptr -
+                                   (uintptr_t)chunk) >> LG_PAGE;
+                               arena_dalloc_small(extent_node_arena_get(
+                                   &chunk->node), chunk, ptr, pageind);
+                       }
+               } else {
+                       assert(config_cache_oblivious || ((uintptr_t)ptr &
+                           PAGE_MASK) == 0);
+
+                       if (likely(tcache != NULL) && size <= tcache_maxclass)
+                               tcache_dalloc_large(tsd, tcache, ptr, size);
+                       else {
+                               arena_dalloc_large(extent_node_arena_get(
+                                   &chunk->node), chunk, ptr);
+                       }
+               }
+       } else
+               huge_dalloc(tsd, ptr, tcache);
 }
-#  endif /* JEMALLOC_ARENA_INLINE_C */
+#  endif /* JEMALLOC_ARENA_INLINE_B */
 #endif
 
 #endif /* JEMALLOC_H_INLINES */
index a04881579674a59e10839685771e0f1b417603c2..a9aad35d1213e502930c0c346a1536953dfc09d0 100644 (file)
@@ -11,6 +11,7 @@
 
 #define        atomic_read_uint64(p)   atomic_add_uint64(p, 0)
 #define        atomic_read_uint32(p)   atomic_add_uint32(p, 0)
+#define        atomic_read_p(p)        atomic_add_p(p, NULL)
 #define        atomic_read_z(p)        atomic_add_z(p, 0)
 #define        atomic_read_u(p)        atomic_add_u(p, 0)
 
 #ifdef JEMALLOC_H_INLINES
 
 /*
- * All functions return the arithmetic result of the atomic operation.  Some
- * atomic operation APIs return the value prior to mutation, in which case the
- * following functions must redundantly compute the result so that it can be
- * returned.  These functions are normally inlined, so the extra operations can
- * be optimized away if the return values aren't used by the callers.
+ * All arithmetic functions return the arithmetic result of the atomic
+ * operation.  Some atomic operation APIs return the value prior to mutation, in
+ * which case the following functions must redundantly compute the result so
+ * that it can be returned.  These functions are normally inlined, so the extra
+ * operations can be optimized away if the return values aren't used by the
+ * callers.
  *
+ *   <t> atomic_read_<t>(<t> *p) { return (*p); }
  *   <t> atomic_add_<t>(<t> *p, <t> x) { return (*p + x); }
  *   <t> atomic_sub_<t>(<t> *p, <t> x) { return (*p - x); }
+ *   bool atomic_cas_<t>(<t> *p, <t> c, <t> s)
+ *   {
+ *     if (*p != c)
+ *       return (true);
+ *     *p = s;
+ *     return (false);
+ *   }
+ *   void atomic_write_<t>(<t> *p, <t> x) { *p = x; }
  */
 
 #ifndef JEMALLOC_ENABLE_INLINE
 uint64_t       atomic_add_uint64(uint64_t *p, uint64_t x);
 uint64_t       atomic_sub_uint64(uint64_t *p, uint64_t x);
+bool   atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s);
+void   atomic_write_uint64(uint64_t *p, uint64_t x);
 uint32_t       atomic_add_uint32(uint32_t *p, uint32_t x);
 uint32_t       atomic_sub_uint32(uint32_t *p, uint32_t x);
+bool   atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s);
+void   atomic_write_uint32(uint32_t *p, uint32_t x);
+void   *atomic_add_p(void **p, void *x);
+void   *atomic_sub_p(void **p, void *x);
+bool   atomic_cas_p(void **p, void *c, void *s);
+void   atomic_write_p(void **p, const void *x);
 size_t atomic_add_z(size_t *p, size_t x);
 size_t atomic_sub_z(size_t *p, size_t x);
+bool   atomic_cas_z(size_t *p, size_t c, size_t s);
+void   atomic_write_z(size_t *p, size_t x);
 unsigned       atomic_add_u(unsigned *p, unsigned x);
 unsigned       atomic_sub_u(unsigned *p, unsigned x);
+bool   atomic_cas_u(unsigned *p, unsigned c, unsigned s);
+void   atomic_write_u(unsigned *p, unsigned x);
 #endif
 
 #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_ATOMIC_C_))
 /******************************************************************************/
 /* 64-bit operations. */
 #if (LG_SIZEOF_PTR == 3 || LG_SIZEOF_INT == 3)
-#  ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8
+#  if (defined(__amd64__) || defined(__x86_64__))
 JEMALLOC_INLINE uint64_t
 atomic_add_uint64(uint64_t *p, uint64_t x)
 {
+       uint64_t t = x;
 
-       return (__sync_add_and_fetch(p, x));
+       asm volatile (
+           "lock; xaddq %0, %1;"
+           : "+r" (t), "=m" (*p) /* Outputs. */
+           : "m" (*p) /* Inputs. */
+           );
+
+       return (t + x);
 }
 
 JEMALLOC_INLINE uint64_t
 atomic_sub_uint64(uint64_t *p, uint64_t x)
 {
+       uint64_t t;
 
-       return (__sync_sub_and_fetch(p, x));
+       x = (uint64_t)(-(int64_t)x);
+       t = x;
+       asm volatile (
+           "lock; xaddq %0, %1;"
+           : "+r" (t), "=m" (*p) /* Outputs. */
+           : "m" (*p) /* Inputs. */
+           );
+
+       return (t + x);
 }
-#elif (defined(_MSC_VER))
+
+JEMALLOC_INLINE bool
+atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s)
+{
+       uint8_t success;
+
+       asm volatile (
+           "lock; cmpxchgq %4, %0;"
+           "sete %1;"
+           : "=m" (*p), "=a" (success) /* Outputs. */
+           : "m" (*p), "a" (c), "r" (s) /* Inputs. */
+           : "memory" /* Clobbers. */
+           );
+
+       return (!(bool)success);
+}
+
+JEMALLOC_INLINE void
+atomic_write_uint64(uint64_t *p, uint64_t x)
+{
+
+       asm volatile (
+           "xchgq %1, %0;" /* Lock is implied by xchgq. */
+           : "=m" (*p), "+r" (x) /* Outputs. */
+           : "m" (*p) /* Inputs. */
+           : "memory" /* Clobbers. */
+           );
+}
+#  elif (defined(JEMALLOC_C11ATOMICS))
 JEMALLOC_INLINE uint64_t
 atomic_add_uint64(uint64_t *p, uint64_t x)
 {
-
-       return (InterlockedExchangeAdd64(p, x) + x);
+       volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p;
+       return (atomic_fetch_add(a, x) + x);
 }
 
 JEMALLOC_INLINE uint64_t
 atomic_sub_uint64(uint64_t *p, uint64_t x)
 {
+       volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p;
+       return (atomic_fetch_sub(a, x) - x);
+}
 
-       return (InterlockedExchangeAdd64(p, -((int64_t)x)) - x);
+JEMALLOC_INLINE bool
+atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s)
+{
+       volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p;
+       return (!atomic_compare_exchange_strong(a, &c, s));
 }
-#elif (defined(JEMALLOC_OSATOMIC))
+
+JEMALLOC_INLINE void
+atomic_write_uint64(uint64_t *p, uint64_t x)
+{
+       volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p;
+       atomic_store(a, x);
+}
+#  elif (defined(JEMALLOC_ATOMIC9))
 JEMALLOC_INLINE uint64_t
 atomic_add_uint64(uint64_t *p, uint64_t x)
 {
 
-       return (OSAtomicAdd64((int64_t)x, (int64_t *)p));
+       /*
+        * atomic_fetchadd_64() doesn't exist, but we only ever use this
+        * function on LP64 systems, so atomic_fetchadd_long() will do.
+        */
+       assert(sizeof(uint64_t) == sizeof(unsigned long));
+
+       return (atomic_fetchadd_long(p, (unsigned long)x) + x);
 }
 
 JEMALLOC_INLINE uint64_t
 atomic_sub_uint64(uint64_t *p, uint64_t x)
 {
 
-       return (OSAtomicAdd64(-((int64_t)x), (int64_t *)p));
+       assert(sizeof(uint64_t) == sizeof(unsigned long));
+
+       return (atomic_fetchadd_long(p, (unsigned long)(-(long)x)) - x);
+}
+
+JEMALLOC_INLINE bool
+atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s)
+{
+
+       assert(sizeof(uint64_t) == sizeof(unsigned long));
+
+       return (!atomic_cmpset_long(p, (unsigned long)c, (unsigned long)s));
 }
-#  elif (defined(__amd64__) || defined(__x86_64__))
+
+JEMALLOC_INLINE void
+atomic_write_uint64(uint64_t *p, uint64_t x)
+{
+
+       assert(sizeof(uint64_t) == sizeof(unsigned long));
+
+       atomic_store_rel_long(p, x);
+}
+#  elif (defined(JEMALLOC_OSATOMIC))
 JEMALLOC_INLINE uint64_t
 atomic_add_uint64(uint64_t *p, uint64_t x)
 {
-       uint64_t t = x;
-
-       asm volatile (
-           "lock; xaddq %0, %1;"
-           : "+r" (t), "=m" (*p) /* Outputs. */
-           : "m" (*p) /* Inputs. */
-           );
 
-       return (t + x);
+       return (OSAtomicAdd64((int64_t)x, (int64_t *)p));
 }
 
 JEMALLOC_INLINE uint64_t
 atomic_sub_uint64(uint64_t *p, uint64_t x)
 {
-       uint64_t t;
 
-       x = (uint64_t)(-(int64_t)x);
-       t = x;
-       asm volatile (
-           "lock; xaddq %0, %1;"
-           : "+r" (t), "=m" (*p) /* Outputs. */
-           : "m" (*p) /* Inputs. */
-           );
+       return (OSAtomicAdd64(-((int64_t)x), (int64_t *)p));
+}
 
-       return (t + x);
+JEMALLOC_INLINE bool
+atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s)
+{
+
+       return (!OSAtomicCompareAndSwap64(c, s, (int64_t *)p));
 }
-#  elif (defined(JEMALLOC_ATOMIC9))
+
+JEMALLOC_INLINE void
+atomic_write_uint64(uint64_t *p, uint64_t x)
+{
+       uint64_t o;
+
+       /*The documented OSAtomic*() API does not expose an atomic exchange. */
+       do {
+               o = atomic_read_uint64(p);
+       } while (atomic_cas_uint64(p, o, x));
+}
+#  elif (defined(_MSC_VER))
 JEMALLOC_INLINE uint64_t
 atomic_add_uint64(uint64_t *p, uint64_t x)
 {
 
-       /*
-        * atomic_fetchadd_64() doesn't exist, but we only ever use this
-        * function on LP64 systems, so atomic_fetchadd_long() will do.
-        */
-       assert(sizeof(uint64_t) == sizeof(unsigned long));
-
-       return (atomic_fetchadd_long(p, (unsigned long)x) + x);
+       return (InterlockedExchangeAdd64(p, x) + x);
 }
 
 JEMALLOC_INLINE uint64_t
 atomic_sub_uint64(uint64_t *p, uint64_t x)
 {
 
-       assert(sizeof(uint64_t) == sizeof(unsigned long));
+       return (InterlockedExchangeAdd64(p, -((int64_t)x)) - x);
+}
 
-       return (atomic_fetchadd_long(p, (unsigned long)(-(long)x)) - x);
+JEMALLOC_INLINE bool
+atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s)
+{
+       uint64_t o;
+
+       o = InterlockedCompareExchange64(p, s, c);
+       return (o != c);
 }
-#  elif (defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_8))
+
+JEMALLOC_INLINE void
+atomic_write_uint64(uint64_t *p, uint64_t x)
+{
+
+       InterlockedExchange64(p, x);
+}
+#  elif (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) || \
+    defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_8))
 JEMALLOC_INLINE uint64_t
 atomic_add_uint64(uint64_t *p, uint64_t x)
 {
@@ -152,6 +270,20 @@ atomic_sub_uint64(uint64_t *p, uint64_t x)
 
        return (__sync_sub_and_fetch(p, x));
 }
+
+JEMALLOC_INLINE bool
+atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s)
+{
+
+       return (!__sync_bool_compare_and_swap(p, c, s));
+}
+
+JEMALLOC_INLINE void
+atomic_write_uint64(uint64_t *p, uint64_t x)
+{
+
+       __sync_lock_test_and_set(p, x);
+}
 #  else
 #    error "Missing implementation for 64-bit atomic operations"
 #  endif
@@ -159,93 +291,184 @@ atomic_sub_uint64(uint64_t *p, uint64_t x)
 
 /******************************************************************************/
 /* 32-bit operations. */
-#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4
+#if (defined(__i386__) || defined(__amd64__) || defined(__x86_64__))
 JEMALLOC_INLINE uint32_t
 atomic_add_uint32(uint32_t *p, uint32_t x)
 {
+       uint32_t t = x;
 
-       return (__sync_add_and_fetch(p, x));
+       asm volatile (
+           "lock; xaddl %0, %1;"
+           : "+r" (t), "=m" (*p) /* Outputs. */
+           : "m" (*p) /* Inputs. */
+           );
+
+       return (t + x);
 }
 
 JEMALLOC_INLINE uint32_t
 atomic_sub_uint32(uint32_t *p, uint32_t x)
 {
+       uint32_t t;
 
-       return (__sync_sub_and_fetch(p, x));
+       x = (uint32_t)(-(int32_t)x);
+       t = x;
+       asm volatile (
+           "lock; xaddl %0, %1;"
+           : "+r" (t), "=m" (*p) /* Outputs. */
+           : "m" (*p) /* Inputs. */
+           );
+
+       return (t + x);
 }
-#elif (defined(_MSC_VER))
+
+JEMALLOC_INLINE bool
+atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s)
+{
+       uint8_t success;
+
+       asm volatile (
+           "lock; cmpxchgl %4, %0;"
+           "sete %1;"
+           : "=m" (*p), "=a" (success) /* Outputs. */
+           : "m" (*p), "a" (c), "r" (s) /* Inputs. */
+           : "memory"
+           );
+
+       return (!(bool)success);
+}
+
+JEMALLOC_INLINE void
+atomic_write_uint32(uint32_t *p, uint32_t x)
+{
+
+       asm volatile (
+           "xchgl %1, %0;" /* Lock is implied by xchgl. */
+           : "=m" (*p), "+r" (x) /* Outputs. */
+           : "m" (*p) /* Inputs. */
+           : "memory" /* Clobbers. */
+           );
+}
+#  elif (defined(JEMALLOC_C11ATOMICS))
 JEMALLOC_INLINE uint32_t
 atomic_add_uint32(uint32_t *p, uint32_t x)
 {
-
-       return (InterlockedExchangeAdd(p, x) + x);
+       volatile atomic_uint_least32_t *a = (volatile atomic_uint_least32_t *)p;
+       return (atomic_fetch_add(a, x) + x);
 }
 
 JEMALLOC_INLINE uint32_t
 atomic_sub_uint32(uint32_t *p, uint32_t x)
 {
+       volatile atomic_uint_least32_t *a = (volatile atomic_uint_least32_t *)p;
+       return (atomic_fetch_sub(a, x) - x);
+}
 
-       return (InterlockedExchangeAdd(p, -((int32_t)x)) - x);
+JEMALLOC_INLINE bool
+atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s)
+{
+       volatile atomic_uint_least32_t *a = (volatile atomic_uint_least32_t *)p;
+       return (!atomic_compare_exchange_strong(a, &c, s));
 }
-#elif (defined(JEMALLOC_OSATOMIC))
+
+JEMALLOC_INLINE void
+atomic_write_uint32(uint32_t *p, uint32_t x)
+{
+       volatile atomic_uint_least32_t *a = (volatile atomic_uint_least32_t *)p;
+       atomic_store(a, x);
+}
+#elif (defined(JEMALLOC_ATOMIC9))
 JEMALLOC_INLINE uint32_t
 atomic_add_uint32(uint32_t *p, uint32_t x)
 {
 
-       return (OSAtomicAdd32((int32_t)x, (int32_t *)p));
+       return (atomic_fetchadd_32(p, x) + x);
 }
 
 JEMALLOC_INLINE uint32_t
 atomic_sub_uint32(uint32_t *p, uint32_t x)
 {
 
-       return (OSAtomicAdd32(-((int32_t)x), (int32_t *)p));
+       return (atomic_fetchadd_32(p, (uint32_t)(-(int32_t)x)) - x);
 }
-#elif (defined(__i386__) || defined(__amd64__) || defined(__x86_64__))
+
+JEMALLOC_INLINE bool
+atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s)
+{
+
+       return (!atomic_cmpset_32(p, c, s));
+}
+
+JEMALLOC_INLINE void
+atomic_write_uint32(uint32_t *p, uint32_t x)
+{
+
+       atomic_store_rel_32(p, x);
+}
+#elif (defined(JEMALLOC_OSATOMIC))
 JEMALLOC_INLINE uint32_t
 atomic_add_uint32(uint32_t *p, uint32_t x)
 {
-       uint32_t t = x;
 
-       asm volatile (
-           "lock; xaddl %0, %1;"
-           : "+r" (t), "=m" (*p) /* Outputs. */
-           : "m" (*p) /* Inputs. */
-           );
-
-       return (t + x);
+       return (OSAtomicAdd32((int32_t)x, (int32_t *)p));
 }
 
 JEMALLOC_INLINE uint32_t
 atomic_sub_uint32(uint32_t *p, uint32_t x)
 {
-       uint32_t t;
 
-       x = (uint32_t)(-(int32_t)x);
-       t = x;
-       asm volatile (
-           "lock; xaddl %0, %1;"
-           : "+r" (t), "=m" (*p) /* Outputs. */
-           : "m" (*p) /* Inputs. */
-           );
+       return (OSAtomicAdd32(-((int32_t)x), (int32_t *)p));
+}
 
-       return (t + x);
+JEMALLOC_INLINE bool
+atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s)
+{
+
+       return (!OSAtomicCompareAndSwap32(c, s, (int32_t *)p));
 }
-#elif (defined(JEMALLOC_ATOMIC9))
+
+JEMALLOC_INLINE void
+atomic_write_uint32(uint32_t *p, uint32_t x)
+{
+       uint32_t o;
+
+       /*The documented OSAtomic*() API does not expose an atomic exchange. */
+       do {
+               o = atomic_read_uint32(p);
+       } while (atomic_cas_uint32(p, o, x));
+}
+#elif (defined(_MSC_VER))
 JEMALLOC_INLINE uint32_t
 atomic_add_uint32(uint32_t *p, uint32_t x)
 {
 
-       return (atomic_fetchadd_32(p, x) + x);
+       return (InterlockedExchangeAdd(p, x) + x);
 }
 
 JEMALLOC_INLINE uint32_t
 atomic_sub_uint32(uint32_t *p, uint32_t x)
 {
 
-       return (atomic_fetchadd_32(p, (uint32_t)(-(int32_t)x)) - x);
+       return (InterlockedExchangeAdd(p, -((int32_t)x)) - x);
+}
+
+JEMALLOC_INLINE bool
+atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s)
+{
+       uint32_t o;
+
+       o = InterlockedCompareExchange(p, s, c);
+       return (o != c);
 }
-#elif (defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_4))
+
+JEMALLOC_INLINE void
+atomic_write_uint32(uint32_t *p, uint32_t x)
+{
+
+       InterlockedExchange(p, x);
+}
+#elif (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || \
+ defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_4))
 JEMALLOC_INLINE uint32_t
 atomic_add_uint32(uint32_t *p, uint32_t x)
 {
@@ -259,10 +482,72 @@ atomic_sub_uint32(uint32_t *p, uint32_t x)
 
        return (__sync_sub_and_fetch(p, x));
 }
+
+JEMALLOC_INLINE bool
+atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s)
+{
+
+       return (!__sync_bool_compare_and_swap(p, c, s));
+}
+
+JEMALLOC_INLINE void
+atomic_write_uint32(uint32_t *p, uint32_t x)
+{
+
+       __sync_lock_test_and_set(p, x);
+}
 #else
 #  error "Missing implementation for 32-bit atomic operations"
 #endif
 
+/******************************************************************************/
+/* Pointer operations. */
+JEMALLOC_INLINE void *
+atomic_add_p(void **p, void *x)
+{
+
+#if (LG_SIZEOF_PTR == 3)
+       return ((void *)atomic_add_uint64((uint64_t *)p, (uint64_t)x));
+#elif (LG_SIZEOF_PTR == 2)
+       return ((void *)atomic_add_uint32((uint32_t *)p, (uint32_t)x));
+#endif
+}
+
+JEMALLOC_INLINE void *
+atomic_sub_p(void **p, void *x)
+{
+
+#if (LG_SIZEOF_PTR == 3)
+       return ((void *)atomic_add_uint64((uint64_t *)p,
+           (uint64_t)-((int64_t)x)));
+#elif (LG_SIZEOF_PTR == 2)
+       return ((void *)atomic_add_uint32((uint32_t *)p,
+           (uint32_t)-((int32_t)x)));
+#endif
+}
+
+JEMALLOC_INLINE bool
+atomic_cas_p(void **p, void *c, void *s)
+{
+
+#if (LG_SIZEOF_PTR == 3)
+       return (atomic_cas_uint64((uint64_t *)p, (uint64_t)c, (uint64_t)s));
+#elif (LG_SIZEOF_PTR == 2)
+       return (atomic_cas_uint32((uint32_t *)p, (uint32_t)c, (uint32_t)s));
+#endif
+}
+
+JEMALLOC_INLINE void
+atomic_write_p(void **p, const void *x)
+{
+
+#if (LG_SIZEOF_PTR == 3)
+       atomic_write_uint64((uint64_t *)p, (uint64_t)x);
+#elif (LG_SIZEOF_PTR == 2)
+       atomic_write_uint32((uint32_t *)p, (uint32_t)x);
+#endif
+}
+
 /******************************************************************************/
 /* size_t operations. */
 JEMALLOC_INLINE size_t
@@ -289,6 +574,28 @@ atomic_sub_z(size_t *p, size_t x)
 #endif
 }
 
+JEMALLOC_INLINE bool
+atomic_cas_z(size_t *p, size_t c, size_t s)
+{
+
+#if (LG_SIZEOF_PTR == 3)
+       return (atomic_cas_uint64((uint64_t *)p, (uint64_t)c, (uint64_t)s));
+#elif (LG_SIZEOF_PTR == 2)
+       return (atomic_cas_uint32((uint32_t *)p, (uint32_t)c, (uint32_t)s));
+#endif
+}
+
+JEMALLOC_INLINE void
+atomic_write_z(size_t *p, size_t x)
+{
+
+#if (LG_SIZEOF_PTR == 3)
+       atomic_write_uint64((uint64_t *)p, (uint64_t)x);
+#elif (LG_SIZEOF_PTR == 2)
+       atomic_write_uint32((uint32_t *)p, (uint32_t)x);
+#endif
+}
+
 /******************************************************************************/
 /* unsigned operations. */
 JEMALLOC_INLINE unsigned
@@ -314,6 +621,29 @@ atomic_sub_u(unsigned *p, unsigned x)
            (uint32_t)-((int32_t)x)));
 #endif
 }
+
+JEMALLOC_INLINE bool
+atomic_cas_u(unsigned *p, unsigned c, unsigned s)
+{
+
+#if (LG_SIZEOF_INT == 3)
+       return (atomic_cas_uint64((uint64_t *)p, (uint64_t)c, (uint64_t)s));
+#elif (LG_SIZEOF_INT == 2)
+       return (atomic_cas_uint32((uint32_t *)p, (uint32_t)c, (uint32_t)s));
+#endif
+}
+
+JEMALLOC_INLINE void
+atomic_write_u(unsigned *p, unsigned x)
+{
+
+#if (LG_SIZEOF_INT == 3)
+       atomic_write_uint64((uint64_t *)p, (uint64_t)x);
+#elif (LG_SIZEOF_INT == 2)
+       atomic_write_uint32((uint32_t *)p, (uint32_t)x);
+#endif
+}
+
 /******************************************************************************/
 #endif
 
index 3fb80b92a9874040d3c4cbb2ca32367ab9717dab..39e46ee445d378a20b2731b6320790a3c5eb009e 100644 (file)
@@ -10,9 +10,7 @@
 #ifdef JEMALLOC_H_EXTERNS
 
 void   *base_alloc(size_t size);
-void   *base_calloc(size_t number, size_t size);
-extent_node_t *base_node_alloc(void);
-void   base_node_dalloc(extent_node_t *node);
+void   base_stats_get(size_t *allocated, size_t *resident, size_t *mapped);
 bool   base_boot(void);
 void   base_prefork(void);
 void   base_postfork_parent(void);
index 2e68a020296ec43a02783b7048853a7f5bb83147..5d19383530334419fb24cb8332e55044d84dc82d 100644 (file)
@@ -5,7 +5,7 @@
  * Size and alignment of memory chunks that are allocated by the OS's virtual
  * memory system.
  */
-#define        LG_CHUNK_DEFAULT        22
+#define        LG_CHUNK_DEFAULT        21
 
 /* Return the chunk address for allocation address a. */
 #define        CHUNK_ADDR2BASE(a)                                              \
 #define        CHUNK_CEILING(s)                                                \
        (((s) + chunksize_mask) & ~chunksize_mask)
 
+#define        CHUNK_HOOKS_INITIALIZER {                                       \
+    NULL,                                                              \
+    NULL,                                                              \
+    NULL,                                                              \
+    NULL,                                                              \
+    NULL,                                                              \
+    NULL,                                                              \
+    NULL                                                               \
+}
+
 #endif /* JEMALLOC_H_TYPES */
 /******************************************************************************/
 #ifdef JEMALLOC_H_STRUCTS
 extern size_t          opt_lg_chunk;
 extern const char      *opt_dss;
 
-/* Protects stats_chunks; currently not used for any other purpose. */
-extern malloc_mutex_t  chunks_mtx;
-/* Chunk statistics. */
-extern chunk_stats_t   stats_chunks;
-
-extern rtree_t         *chunks_rtree;
+extern rtree_t         chunks_rtree;
 
 extern size_t          chunksize;
 extern size_t          chunksize_mask; /* (chunksize - 1). */
 extern size_t          chunk_npages;
-extern size_t          map_bias; /* Number of arena chunk header pages. */
-extern size_t          map_misc_offset;
-extern size_t          arena_maxclass; /* Max size class for arenas. */
 
+extern const chunk_hooks_t     chunk_hooks_default;
+
+chunk_hooks_t  chunk_hooks_get(arena_t *arena);
+chunk_hooks_t  chunk_hooks_set(arena_t *arena,
+    const chunk_hooks_t *chunk_hooks);
+
+bool   chunk_register(const void *chunk, const extent_node_t *node);
+void   chunk_deregister(const void *chunk, const extent_node_t *node);
 void   *chunk_alloc_base(size_t size);
-void   *chunk_alloc_arena(chunk_alloc_t *chunk_alloc,
-    chunk_dalloc_t *chunk_dalloc, unsigned arena_ind, void *new_addr,
-    size_t size, size_t alignment, bool *zero);
-void   *chunk_alloc_default(void *new_addr, size_t size, size_t alignment,
-    bool *zero, unsigned arena_ind);
-void   chunk_unmap(void *chunk, size_t size);
-bool   chunk_dalloc_default(void *chunk, size_t size, unsigned arena_ind);
+void   *chunk_alloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    void *new_addr, size_t size, size_t alignment, bool *zero,
+    bool dalloc_node);
+void   *chunk_alloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit);
+void   chunk_dalloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    void *chunk, size_t size, bool committed);
+void   chunk_dalloc_arena(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    void *chunk, size_t size, bool zeroed, bool committed);
+void   chunk_dalloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    void *chunk, size_t size, bool committed);
+bool   chunk_purge_arena(arena_t *arena, void *chunk, size_t offset,
+    size_t length);
+bool   chunk_purge_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    void *chunk, size_t size, size_t offset, size_t length);
 bool   chunk_boot(void);
 void   chunk_prefork(void);
 void   chunk_postfork_parent(void);
@@ -61,6 +79,19 @@ void chunk_postfork_child(void);
 /******************************************************************************/
 #ifdef JEMALLOC_H_INLINES
 
+#ifndef JEMALLOC_ENABLE_INLINE
+extent_node_t  *chunk_lookup(const void *chunk, bool dependent);
+#endif
+
+#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_CHUNK_C_))
+JEMALLOC_INLINE extent_node_t *
+chunk_lookup(const void *ptr, bool dependent)
+{
+
+       return (rtree_get(&chunks_rtree, (uintptr_t)ptr, dependent));
+}
+#endif
+
 #endif /* JEMALLOC_H_INLINES */
 /******************************************************************************/
 
index 4535ce09c09a822b2cb74bc1b13c5b24feb5c5d3..388f46be0801be97ff341e75d939383ad13eae3a 100644 (file)
@@ -23,7 +23,8 @@ extern const char *dss_prec_names[];
 
 dss_prec_t     chunk_dss_prec_get(void);
 bool   chunk_dss_prec_set(dss_prec_t dss_prec);
-void   *chunk_alloc_dss(size_t size, size_t alignment, bool *zero);
+void   *chunk_alloc_dss(arena_t *arena, void *new_addr, size_t size,
+    size_t alignment, bool *zero, bool *commit);
 bool   chunk_in_dss(void *chunk);
 bool   chunk_dss_boot(void);
 void   chunk_dss_prefork(void);
index c5d5c6c0c7ac41fa42bb1766a21e959a3d1db84f..7d8014c58173f13861e2907e55a40a7a6d9a79ce 100644 (file)
@@ -9,9 +9,8 @@
 /******************************************************************************/
 #ifdef JEMALLOC_H_EXTERNS
 
-bool   pages_purge(void *addr, size_t length);
-
-void   *chunk_alloc_mmap(size_t size, size_t alignment, bool *zero);
+void   *chunk_alloc_mmap(size_t size, size_t alignment, bool *zero,
+    bool *commit);
 bool   chunk_dalloc_mmap(void *chunk, size_t size);
 
 #endif /* JEMALLOC_H_EXTERNS */
index 2d301bf1786cfb897b349a27f72e81d68ad2c16c..751c14b5bada897a91e15c183a0f08725e9d3f95 100644 (file)
@@ -34,6 +34,7 @@ struct ctl_arena_stats_s {
        bool                    initialized;
        unsigned                nthreads;
        const char              *dss;
+       ssize_t                 lg_dirty_mult;
        size_t                  pactive;
        size_t                  pdirty;
        arena_stats_t           astats;
@@ -46,17 +47,15 @@ struct ctl_arena_stats_s {
 
        malloc_bin_stats_t      bstats[NBINS];
        malloc_large_stats_t    *lstats;        /* nlclasses elements. */
+       malloc_huge_stats_t     *hstats;        /* nhclasses elements. */
 };
 
 struct ctl_stats_s {
        size_t                  allocated;
        size_t                  active;
+       size_t                  metadata;
+       size_t                  resident;
        size_t                  mapped;
-       struct {
-               size_t          current;        /* stats_chunks.curchunks */
-               uint64_t        total;          /* stats_chunks.nchunks */
-               size_t          high;           /* stats_chunks.highchunks */
-       } chunks;
        unsigned                narenas;
        ctl_arena_stats_t       *arenas;        /* (narenas + 1) elements. */
 };
index 5b00076f26d2c644f836424eef4716d856d003fa..386d50ef4cd5eda34cfb0bb232c8484163c7eeee 100644 (file)
@@ -7,28 +7,53 @@ typedef struct extent_node_s extent_node_t;
 /******************************************************************************/
 #ifdef JEMALLOC_H_STRUCTS
 
-/* Tree of extents. */
+/* Tree of extents.  Use accessor functions for en_* fields. */
 struct extent_node_s {
-       /* Linkage for the size/address-ordered tree. */
-       rb_node(extent_node_t)  link_szad;
+       /* Arena from which this extent came, if any. */
+       arena_t                 *en_arena;
 
-       /* Linkage for the address-ordered tree. */
-       rb_node(extent_node_t)  link_ad;
+       /* Pointer to the extent that this tree node is responsible for. */
+       void                    *en_addr;
+
+       /* Total region size. */
+       size_t                  en_size;
+
+       /*
+        * The zeroed flag is used by chunk recycling code to track whether
+        * memory is zero-filled.
+        */
+       bool                    en_zeroed;
+
+       /*
+        * True if physical memory is committed to the extent, whether
+        * explicitly or implicitly as on a system that overcommits and
+        * satisfies physical memory needs on demand via soft page faults.
+        */
+       bool                    en_committed;
+
+       /*
+        * The achunk flag is used to validate that huge allocation lookups
+        * don't return arena chunks.
+        */
+       bool                    en_achunk;
 
        /* Profile counters, used for huge objects. */
-       prof_tctx_t             *prof_tctx;
+       prof_tctx_t             *en_prof_tctx;
 
-       /* Pointer to the extent that this tree node is responsible for. */
-       void                    *addr;
+       /* Linkage for arena's runs_dirty and chunks_cache rings. */
+       arena_runs_dirty_link_t rd;
+       qr(extent_node_t)       cc_link;
 
-       /* Total region size. */
-       size_t                  size;
+       union {
+               /* Linkage for the size/address-ordered tree. */
+               rb_node(extent_node_t)  szad_link;
 
-       /* Arena from which this extent came, if any */
-       arena_t                 *arena;
+               /* Linkage for arena's huge and node_cache lists. */
+               ql_elm(extent_node_t)   ql_link;
+       };
 
-       /* True if zero-filled; used by chunk recycling code. */
-       bool                    zeroed;
+       /* Linkage for the address-ordered tree. */
+       rb_node(extent_node_t)  ad_link;
 };
 typedef rb_tree(extent_node_t) extent_tree_t;
 
@@ -44,6 +69,171 @@ rb_proto(, extent_tree_ad_, extent_tree_t, extent_node_t)
 /******************************************************************************/
 #ifdef JEMALLOC_H_INLINES
 
+#ifndef JEMALLOC_ENABLE_INLINE
+arena_t        *extent_node_arena_get(const extent_node_t *node);
+void   *extent_node_addr_get(const extent_node_t *node);
+size_t extent_node_size_get(const extent_node_t *node);
+bool   extent_node_zeroed_get(const extent_node_t *node);
+bool   extent_node_committed_get(const extent_node_t *node);
+bool   extent_node_achunk_get(const extent_node_t *node);
+prof_tctx_t    *extent_node_prof_tctx_get(const extent_node_t *node);
+void   extent_node_arena_set(extent_node_t *node, arena_t *arena);
+void   extent_node_addr_set(extent_node_t *node, void *addr);
+void   extent_node_size_set(extent_node_t *node, size_t size);
+void   extent_node_zeroed_set(extent_node_t *node, bool zeroed);
+void   extent_node_committed_set(extent_node_t *node, bool committed);
+void   extent_node_achunk_set(extent_node_t *node, bool achunk);
+void   extent_node_prof_tctx_set(extent_node_t *node, prof_tctx_t *tctx);
+void   extent_node_init(extent_node_t *node, arena_t *arena, void *addr,
+    size_t size, bool zeroed, bool committed);
+void   extent_node_dirty_linkage_init(extent_node_t *node);
+void   extent_node_dirty_insert(extent_node_t *node,
+    arena_runs_dirty_link_t *runs_dirty, extent_node_t *chunks_dirty);
+void   extent_node_dirty_remove(extent_node_t *node);
+#endif
+
+#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_EXTENT_C_))
+JEMALLOC_INLINE arena_t *
+extent_node_arena_get(const extent_node_t *node)
+{
+
+       return (node->en_arena);
+}
+
+JEMALLOC_INLINE void *
+extent_node_addr_get(const extent_node_t *node)
+{
+
+       return (node->en_addr);
+}
+
+JEMALLOC_INLINE size_t
+extent_node_size_get(const extent_node_t *node)
+{
+
+       return (node->en_size);
+}
+
+JEMALLOC_INLINE bool
+extent_node_zeroed_get(const extent_node_t *node)
+{
+
+       return (node->en_zeroed);
+}
+
+JEMALLOC_INLINE bool
+extent_node_committed_get(const extent_node_t *node)
+{
+
+       assert(!node->en_achunk);
+       return (node->en_committed);
+}
+
+JEMALLOC_INLINE bool
+extent_node_achunk_get(const extent_node_t *node)
+{
+
+       return (node->en_achunk);
+}
+
+JEMALLOC_INLINE prof_tctx_t *
+extent_node_prof_tctx_get(const extent_node_t *node)
+{
+
+       return (node->en_prof_tctx);
+}
+
+JEMALLOC_INLINE void
+extent_node_arena_set(extent_node_t *node, arena_t *arena)
+{
+
+       node->en_arena = arena;
+}
+
+JEMALLOC_INLINE void
+extent_node_addr_set(extent_node_t *node, void *addr)
+{
+
+       node->en_addr = addr;
+}
+
+JEMALLOC_INLINE void
+extent_node_size_set(extent_node_t *node, size_t size)
+{
+
+       node->en_size = size;
+}
+
+JEMALLOC_INLINE void
+extent_node_zeroed_set(extent_node_t *node, bool zeroed)
+{
+
+       node->en_zeroed = zeroed;
+}
+
+JEMALLOC_INLINE void
+extent_node_committed_set(extent_node_t *node, bool committed)
+{
+
+       node->en_committed = committed;
+}
+
+JEMALLOC_INLINE void
+extent_node_achunk_set(extent_node_t *node, bool achunk)
+{
+
+       node->en_achunk = achunk;
+}
+
+JEMALLOC_INLINE void
+extent_node_prof_tctx_set(extent_node_t *node, prof_tctx_t *tctx)
+{
+
+       node->en_prof_tctx = tctx;
+}
+
+JEMALLOC_INLINE void
+extent_node_init(extent_node_t *node, arena_t *arena, void *addr, size_t size,
+    bool zeroed, bool committed)
+{
+
+       extent_node_arena_set(node, arena);
+       extent_node_addr_set(node, addr);
+       extent_node_size_set(node, size);
+       extent_node_zeroed_set(node, zeroed);
+       extent_node_committed_set(node, committed);
+       extent_node_achunk_set(node, false);
+       if (config_prof)
+               extent_node_prof_tctx_set(node, NULL);
+}
+
+JEMALLOC_INLINE void
+extent_node_dirty_linkage_init(extent_node_t *node)
+{
+
+       qr_new(&node->rd, rd_link);
+       qr_new(node, cc_link);
+}
+
+JEMALLOC_INLINE void
+extent_node_dirty_insert(extent_node_t *node,
+    arena_runs_dirty_link_t *runs_dirty, extent_node_t *chunks_dirty)
+{
+
+       qr_meld(runs_dirty, &node->rd, rd_link);
+       qr_meld(chunks_dirty, node, cc_link);
+}
+
+JEMALLOC_INLINE void
+extent_node_dirty_remove(extent_node_t *node)
+{
+
+       qr_remove(&node->rd, rd_link);
+       qr_remove(node, cc_link);
+}
+
+#endif
+
 #endif /* JEMALLOC_H_INLINES */
 /******************************************************************************/
 
index a43bbbeccd4615ab96b00ddcf23276db76e2eb35..bcead337abc1e70d3431936ff453b922671bd364 100644 (file)
@@ -35,13 +35,14 @@ JEMALLOC_INLINE uint32_t
 hash_rotl_32(uint32_t x, int8_t r)
 {
 
-       return (x << r) | (x >> (32 - r));
+       return ((x << r) | (x >> (32 - r)));
 }
 
 JEMALLOC_INLINE uint64_t
 hash_rotl_64(uint64_t x, int8_t r)
 {
-       return (x << r) | (x >> (64 - r));
+
+       return ((x << r) | (x >> (64 - r)));
 }
 
 JEMALLOC_INLINE uint32_t
index 00d8c09dd8b9ed22ad137c5e7d5a59845feeacb9..ece7af980aa99a98cf56790ba8d7088fdc31777e 100644 (file)
@@ -9,26 +9,24 @@
 /******************************************************************************/
 #ifdef JEMALLOC_H_EXTERNS
 
-void   *huge_malloc(tsd_t *tsd, arena_t *arena, size_t size, bool zero);
+void   *huge_malloc(tsd_t *tsd, arena_t *arena, size_t size, bool zero,
+    tcache_t *tcache);
 void   *huge_palloc(tsd_t *tsd, arena_t *arena, size_t size, size_t alignment,
-    bool zero);
-bool   huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size,
-    size_t extra, bool zero);
+    bool zero, tcache_t *tcache);
+bool   huge_ralloc_no_move(void *ptr, size_t oldsize, size_t usize_min,
+    size_t usize_max, bool zero);
 void   *huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize,
-    size_t size, size_t extra, size_t alignment, bool zero,
-    bool try_tcache_dalloc);
+    size_t usize, size_t alignment, bool zero, tcache_t *tcache);
 #ifdef JEMALLOC_JET
 typedef void (huge_dalloc_junk_t)(void *, size_t);
 extern huge_dalloc_junk_t *huge_dalloc_junk;
 #endif
-void   huge_dalloc(void *ptr);
+void   huge_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache);
+arena_t        *huge_aalloc(const void *ptr);
 size_t huge_salloc(const void *ptr);
 prof_tctx_t    *huge_prof_tctx_get(const void *ptr);
 void   huge_prof_tctx_set(const void *ptr, prof_tctx_t *tctx);
-bool   huge_boot(void);
-void   huge_prefork(void);
-void   huge_postfork_parent(void);
-void   huge_postfork_child(void);
+void   huge_prof_tctx_reset(const void *ptr);
 
 #endif /* JEMALLOC_H_EXTERNS */
 /******************************************************************************/
index a169221bb39ef1fdc096bdc76859129f51bd4c90..654cd08889b0dcad3e3eb901f5ef4d28274b2e8d 100644 (file)
@@ -70,6 +70,13 @@ static const bool config_prof_libunwind =
     false
 #endif
     ;
+static const bool maps_coalesce =
+#ifdef JEMALLOC_MAPS_COALESCE
+    true
+#else
+    false
+#endif
+    ;
 static const bool config_munmap =
 #ifdef JEMALLOC_MUNMAP
     true
@@ -126,6 +133,17 @@ static const bool config_ivsalloc =
     false
 #endif
     ;
+static const bool config_cache_oblivious =
+#ifdef JEMALLOC_CACHE_OBLIVIOUS
+    true
+#else
+    false
+#endif
+    ;
+
+#ifdef JEMALLOC_C11ATOMICS
+#include <stdatomic.h>
+#endif
 
 #ifdef JEMALLOC_ATOMIC9
 #include <machine/atomic.h>
@@ -165,7 +183,24 @@ static const bool config_ivsalloc =
 
 #include "jemalloc/internal/jemalloc_internal_macros.h"
 
-#define        MALLOCX_ARENA_MASK      ((int)~0xff)
+/* Size class index type. */
+typedef unsigned szind_t;
+
+/*
+ * Flags bits:
+ *
+ * a: arena
+ * t: tcache
+ * 0: unused
+ * z: zero
+ * n: alignment
+ *
+ * aaaaaaaa aaaatttt tttttttt 0znnnnnn
+ */
+#define        MALLOCX_ARENA_MASK      ((int)~0xfffff)
+#define        MALLOCX_ARENA_MAX       0xffe
+#define        MALLOCX_TCACHE_MASK     ((int)~0xfff000ffU)
+#define        MALLOCX_TCACHE_MAX      0xffd
 #define        MALLOCX_LG_ALIGN_MASK   ((int)0x3f)
 /* Use MALLOCX_ALIGN_GET() if alignment may not be specified in flags. */
 #define        MALLOCX_ALIGN_GET_SPECIFIED(flags)                              \
@@ -174,15 +209,17 @@ static const bool config_ivsalloc =
     (MALLOCX_ALIGN_GET_SPECIFIED(flags) & (SIZE_T_MAX-1))
 #define        MALLOCX_ZERO_GET(flags)                                         \
     ((bool)(flags & MALLOCX_ZERO))
+
+#define        MALLOCX_TCACHE_GET(flags)                                       \
+    (((unsigned)((flags & MALLOCX_TCACHE_MASK) >> 8)) - 2)
 #define        MALLOCX_ARENA_GET(flags)                                        \
-    (((unsigned)(flags >> 8)) - 1)
+    (((unsigned)(((unsigned)flags) >> 20)) - 1)
 
 /* Smallest size class to support. */
-#define        LG_TINY_MIN             3
 #define        TINY_MIN                (1U << LG_TINY_MIN)
 
 /*
- * Minimum alignment of allocations is 2^LG_QUANTUM bytes (ignoring tiny size
+ * Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size
  * classes).
  */
 #ifndef LG_QUANTUM
@@ -195,7 +232,7 @@ static const bool config_ivsalloc =
 #  ifdef __alpha__
 #    define LG_QUANTUM         4
 #  endif
-#  ifdef __sparc64__
+#  if (defined(__sparc64__) || defined(__sparcv9))
 #    define LG_QUANTUM         4
 #  endif
 #  if (defined(__amd64__) || defined(__x86_64__) || defined(_M_X64))
@@ -232,7 +269,8 @@ static const bool config_ivsalloc =
 #    define LG_QUANTUM         4
 #  endif
 #  ifndef LG_QUANTUM
-#    error "No LG_QUANTUM definition for architecture; specify via CPPFLAGS"
+#    error "Unknown minimum alignment for architecture; specify via "
+        "--with-lg-quantum"
 #  endif
 #endif
 
@@ -272,14 +310,17 @@ static const bool config_ivsalloc =
 #define        CACHELINE_CEILING(s)                                            \
        (((s) + CACHELINE_MASK) & ~CACHELINE_MASK)
 
-/* Page size.  STATIC_PAGE_SHIFT is determined by the configure script. */
+/* Page size.  LG_PAGE is determined by the configure script. */
 #ifdef PAGE_MASK
 #  undef PAGE_MASK
 #endif
-#define        LG_PAGE         STATIC_PAGE_SHIFT
-#define        PAGE            ((size_t)(1U << STATIC_PAGE_SHIFT))
+#define        PAGE            ((size_t)(1U << LG_PAGE))
 #define        PAGE_MASK       ((size_t)(PAGE - 1))
 
+/* Return the page base address for the page containing address a. */
+#define        PAGE_ADDR2BASE(a)                                               \
+       ((void *)((uintptr_t)(a) & ~PAGE_MASK))
+
 /* Return the smallest pagesize multiple that is >= s. */
 #define        PAGE_CEILING(s)                                                 \
        (((s) + PAGE_MASK) & ~PAGE_MASK)
@@ -296,7 +337,7 @@ static const bool config_ivsalloc =
 #define        ALIGNMENT_CEILING(s, alignment)                                 \
        (((s) + (alignment - 1)) & (-(alignment)))
 
-/* Declare a variable length array */
+/* Declare a variable-length array. */
 #if __STDC_VERSION__ < 199901L
 #  ifdef _MSC_VER
 #    include <malloc.h>
@@ -329,9 +370,10 @@ static const bool config_ivsalloc =
 #include "jemalloc/internal/arena.h"
 #include "jemalloc/internal/bitmap.h"
 #include "jemalloc/internal/base.h"
+#include "jemalloc/internal/rtree.h"
+#include "jemalloc/internal/pages.h"
 #include "jemalloc/internal/chunk.h"
 #include "jemalloc/internal/huge.h"
-#include "jemalloc/internal/rtree.h"
 #include "jemalloc/internal/tcache.h"
 #include "jemalloc/internal/hash.h"
 #include "jemalloc/internal/quarantine.h"
@@ -352,12 +394,18 @@ static const bool config_ivsalloc =
 #include "jemalloc/internal/mutex.h"
 #include "jemalloc/internal/mb.h"
 #include "jemalloc/internal/bitmap.h"
+#define        JEMALLOC_ARENA_STRUCTS_A
+#include "jemalloc/internal/arena.h"
+#undef JEMALLOC_ARENA_STRUCTS_A
 #include "jemalloc/internal/extent.h"
+#define        JEMALLOC_ARENA_STRUCTS_B
 #include "jemalloc/internal/arena.h"
+#undef JEMALLOC_ARENA_STRUCTS_B
 #include "jemalloc/internal/base.h"
+#include "jemalloc/internal/rtree.h"
+#include "jemalloc/internal/pages.h"
 #include "jemalloc/internal/chunk.h"
 #include "jemalloc/internal/huge.h"
-#include "jemalloc/internal/rtree.h"
 #include "jemalloc/internal/tcache.h"
 #include "jemalloc/internal/hash.h"
 #include "jemalloc/internal/quarantine.h"
@@ -370,7 +418,9 @@ static const bool config_ivsalloc =
 #define        JEMALLOC_H_EXTERNS
 
 extern bool    opt_abort;
-extern bool    opt_junk;
+extern const char      *opt_junk;
+extern bool    opt_junk_alloc;
+extern bool    opt_junk_free;
 extern size_t  opt_quarantine;
 extern bool    opt_redzone;
 extern bool    opt_utrace;
@@ -383,25 +433,37 @@ extern bool       in_valgrind;
 /* Number of CPUs. */
 extern unsigned                ncpus;
 
-/* Protects arenas initialization (arenas, arenas_total). */
-extern malloc_mutex_t  arenas_lock;
 /*
- * Arenas that are used to service external requests.  Not all elements of the
- * arenas array are necessarily used; arenas are created lazily as needed.
- *
- * arenas[0..narenas_auto) are used for automatic multiplexing of threads and
- * arenas.  arenas[narenas_auto..narenas_total) are only used if the application
- * takes some action to create them and allocate from them.
+ * index2size_tab encodes the same information as could be computed (at
+ * unacceptable cost in some code paths) by index2size_compute().
  */
-extern arena_t         **arenas;
-extern unsigned                narenas_total;
-extern unsigned                narenas_auto; /* Read-only after initialization. */
-
+extern size_t const    index2size_tab[NSIZES];
+/*
+ * size2index_tab is a compact lookup table that rounds request sizes up to
+ * size classes.  In order to reduce cache footprint, the table is compressed,
+ * and all accesses are via size2index().
+ */
+extern uint8_t const   size2index_tab[];
+
+arena_t        *a0get(void);
+void   *a0malloc(size_t size);
+void   a0dalloc(void *ptr);
+void   *bootstrap_malloc(size_t size);
+void   *bootstrap_calloc(size_t num, size_t size);
+void   bootstrap_free(void *ptr);
 arena_t        *arenas_extend(unsigned ind);
-arena_t        *choose_arena_hard(tsd_t *tsd);
+arena_t        *arena_init(unsigned ind);
+unsigned       narenas_total_get(void);
+arena_t        *arena_get_hard(tsd_t *tsd, unsigned ind, bool init_if_missing);
+arena_t        *arena_choose_hard(tsd_t *tsd);
+void   arena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind);
+unsigned       arena_nbound(unsigned ind);
 void   thread_allocated_cleanup(tsd_t *tsd);
 void   thread_deallocated_cleanup(tsd_t *tsd);
 void   arena_cleanup(tsd_t *tsd);
+void   arenas_cache_cleanup(tsd_t *tsd);
+void   narenas_cache_cleanup(tsd_t *tsd);
+void   arenas_cache_bypass_cleanup(tsd_t *tsd);
 void   jemalloc_prefork(void);
 void   jemalloc_postfork_parent(void);
 void   jemalloc_postfork_child(void);
@@ -420,9 +482,10 @@ void       jemalloc_postfork_child(void);
 #include "jemalloc/internal/extent.h"
 #include "jemalloc/internal/arena.h"
 #include "jemalloc/internal/base.h"
+#include "jemalloc/internal/rtree.h"
+#include "jemalloc/internal/pages.h"
 #include "jemalloc/internal/chunk.h"
 #include "jemalloc/internal/huge.h"
-#include "jemalloc/internal/rtree.h"
 #include "jemalloc/internal/tcache.h"
 #include "jemalloc/internal/hash.h"
 #include "jemalloc/internal/quarantine.h"
@@ -446,25 +509,159 @@ void     jemalloc_postfork_child(void);
 #include "jemalloc/internal/mb.h"
 #include "jemalloc/internal/extent.h"
 #include "jemalloc/internal/base.h"
+#include "jemalloc/internal/rtree.h"
+#include "jemalloc/internal/pages.h"
 #include "jemalloc/internal/chunk.h"
 #include "jemalloc/internal/huge.h"
 
-/*
- * Include arena.h the first time in order to provide inline functions for this
- * header's inlines.
- */
-#define        JEMALLOC_ARENA_INLINE_A
-#include "jemalloc/internal/arena.h"
-#undef JEMALLOC_ARENA_INLINE_A
-
 #ifndef JEMALLOC_ENABLE_INLINE
+szind_t        size2index_compute(size_t size);
+szind_t        size2index_lookup(size_t size);
+szind_t        size2index(size_t size);
+size_t index2size_compute(szind_t index);
+size_t index2size_lookup(szind_t index);
+size_t index2size(szind_t index);
+size_t s2u_compute(size_t size);
+size_t s2u_lookup(size_t size);
 size_t s2u(size_t size);
 size_t sa2u(size_t size, size_t alignment);
-unsigned       narenas_total_get(void);
-arena_t        *choose_arena(tsd_t *tsd, arena_t *arena);
+arena_t        *arena_choose(tsd_t *tsd, arena_t *arena);
+arena_t        *arena_get(tsd_t *tsd, unsigned ind, bool init_if_missing,
+    bool refresh_if_missing);
 #endif
 
 #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_))
+JEMALLOC_INLINE szind_t
+size2index_compute(size_t size)
+{
+
+#if (NTBINS != 0)
+       if (size <= (ZU(1) << LG_TINY_MAXCLASS)) {
+               size_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1;
+               size_t lg_ceil = lg_floor(pow2_ceil(size));
+               return (lg_ceil < lg_tmin ? 0 : lg_ceil - lg_tmin);
+       }
+#endif
+       {
+               size_t x = unlikely(ZI(size) < 0) ? ((size<<1) ?
+                   (ZU(1)<<(LG_SIZEOF_PTR+3)) : ((ZU(1)<<(LG_SIZEOF_PTR+3))-1))
+                   : lg_floor((size<<1)-1);
+               size_t shift = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM) ? 0 :
+                   x - (LG_SIZE_CLASS_GROUP + LG_QUANTUM);
+               size_t grp = shift << LG_SIZE_CLASS_GROUP;
+
+               size_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM + 1)
+                   ? LG_QUANTUM : x - LG_SIZE_CLASS_GROUP - 1;
+
+               size_t delta_inverse_mask = ZI(-1) << lg_delta;
+               size_t mod = ((((size-1) & delta_inverse_mask) >> lg_delta)) &
+                   ((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);
+
+               size_t index = NTBINS + grp + mod;
+               return (index);
+       }
+}
+
+JEMALLOC_ALWAYS_INLINE szind_t
+size2index_lookup(size_t size)
+{
+
+       assert(size <= LOOKUP_MAXCLASS);
+       {
+               size_t ret = ((size_t)(size2index_tab[(size-1) >>
+                   LG_TINY_MIN]));
+               assert(ret == size2index_compute(size));
+               return (ret);
+       }
+}
+
+JEMALLOC_ALWAYS_INLINE szind_t
+size2index(size_t size)
+{
+
+       assert(size > 0);
+       if (likely(size <= LOOKUP_MAXCLASS))
+               return (size2index_lookup(size));
+       return (size2index_compute(size));
+}
+
+JEMALLOC_INLINE size_t
+index2size_compute(szind_t index)
+{
+
+#if (NTBINS > 0)
+       if (index < NTBINS)
+               return (ZU(1) << (LG_TINY_MAXCLASS - NTBINS + 1 + index));
+#endif
+       {
+               size_t reduced_index = index - NTBINS;
+               size_t grp = reduced_index >> LG_SIZE_CLASS_GROUP;
+               size_t mod = reduced_index & ((ZU(1) << LG_SIZE_CLASS_GROUP) -
+                   1);
+
+               size_t grp_size_mask = ~((!!grp)-1);
+               size_t grp_size = ((ZU(1) << (LG_QUANTUM +
+                   (LG_SIZE_CLASS_GROUP-1))) << grp) & grp_size_mask;
+
+               size_t shift = (grp == 0) ? 1 : grp;
+               size_t lg_delta = shift + (LG_QUANTUM-1);
+               size_t mod_size = (mod+1) << lg_delta;
+
+               size_t usize = grp_size + mod_size;
+               return (usize);
+       }
+}
+
+JEMALLOC_ALWAYS_INLINE size_t
+index2size_lookup(szind_t index)
+{
+       size_t ret = (size_t)index2size_tab[index];
+       assert(ret == index2size_compute(index));
+       return (ret);
+}
+
+JEMALLOC_ALWAYS_INLINE size_t
+index2size(szind_t index)
+{
+
+       assert(index < NSIZES);
+       return (index2size_lookup(index));
+}
+
+JEMALLOC_ALWAYS_INLINE size_t
+s2u_compute(size_t size)
+{
+
+#if (NTBINS > 0)
+       if (size <= (ZU(1) << LG_TINY_MAXCLASS)) {
+               size_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1;
+               size_t lg_ceil = lg_floor(pow2_ceil(size));
+               return (lg_ceil < lg_tmin ? (ZU(1) << lg_tmin) :
+                   (ZU(1) << lg_ceil));
+       }
+#endif
+       {
+               size_t x = unlikely(ZI(size) < 0) ? ((size<<1) ?
+                   (ZU(1)<<(LG_SIZEOF_PTR+3)) : ((ZU(1)<<(LG_SIZEOF_PTR+3))-1))
+                   : lg_floor((size<<1)-1);
+               size_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM + 1)
+                   ?  LG_QUANTUM : x - LG_SIZE_CLASS_GROUP - 1;
+               size_t delta = ZU(1) << lg_delta;
+               size_t delta_mask = delta - 1;
+               size_t usize = (size + delta_mask) & ~delta_mask;
+               return (usize);
+       }
+}
+
+JEMALLOC_ALWAYS_INLINE size_t
+s2u_lookup(size_t size)
+{
+       size_t ret = index2size_lookup(size2index_lookup(size));
+
+       assert(ret == s2u_compute(size));
+       return (ret);
+}
+
 /*
  * Compute usable size that would result from allocating an object with the
  * specified size.
@@ -473,11 +670,10 @@ JEMALLOC_ALWAYS_INLINE size_t
 s2u(size_t size)
 {
 
-       if (size <= SMALL_MAXCLASS)
-               return (small_s2u(size));
-       if (size <= arena_maxclass)
-               return (PAGE_CEILING(size));
-       return (CHUNK_CEILING(size));
+       assert(size > 0);
+       if (likely(size <= LOOKUP_MAXCLASS))
+               return (s2u_lookup(size));
+       return (s2u_compute(size));
 }
 
 /*
@@ -491,247 +687,289 @@ sa2u(size_t size, size_t alignment)
 
        assert(alignment != 0 && ((alignment - 1) & alignment) == 0);
 
-       /*
-        * Round size up to the nearest multiple of alignment.
-        *
-        * This done, we can take advantage of the fact that for each small
-        * size class, every object is aligned at the smallest power of two
-        * that is non-zero in the base two representation of the size.  For
-        * example:
-        *
-        *   Size |   Base 2 | Minimum alignment
-        *   -----+----------+------------------
-        *     96 |  1100000 |  32
-        *    144 | 10100000 |  32
-        *    192 | 11000000 |  64
-        */
-       usize = ALIGNMENT_CEILING(size, alignment);
-       /*
-        * (usize < size) protects against the combination of maximal
-        * alignment and size greater than maximal alignment.
-        */
-       if (usize < size) {
-               /* size_t overflow. */
-               return (0);
+       /* Try for a small size class. */
+       if (size <= SMALL_MAXCLASS && alignment < PAGE) {
+               /*
+                * Round size up to the nearest multiple of alignment.
+                *
+                * This done, we can take advantage of the fact that for each
+                * small size class, every object is aligned at the smallest
+                * power of two that is non-zero in the base two representation
+                * of the size.  For example:
+                *
+                *   Size |   Base 2 | Minimum alignment
+                *   -----+----------+------------------
+                *     96 |  1100000 |  32
+                *    144 | 10100000 |  32
+                *    192 | 11000000 |  64
+                */
+               usize = s2u(ALIGNMENT_CEILING(size, alignment));
+               if (usize < LARGE_MINCLASS)
+                       return (usize);
        }
 
-       if (usize <= arena_maxclass && alignment <= PAGE) {
-               if (usize <= SMALL_MAXCLASS)
-                       return (small_s2u(usize));
-               return (PAGE_CEILING(usize));
-       } else {
-               size_t run_size;
-
+       /* Try for a large size class. */
+       if (likely(size <= large_maxclass) && likely(alignment < chunksize)) {
                /*
                 * We can't achieve subpage alignment, so round up alignment
-                * permanently; it makes later calculations simpler.
+                * to the minimum that can actually be supported.
                 */
                alignment = PAGE_CEILING(alignment);
-               usize = PAGE_CEILING(size);
-               /*
-                * (usize < size) protects against very large sizes within
-                * PAGE of SIZE_T_MAX.
-                *
-                * (usize + alignment < usize) protects against the
-                * combination of maximal alignment and usize large enough
-                * to cause overflow.  This is similar to the first overflow
-                * check above, but it needs to be repeated due to the new
-                * usize value, which may now be *equal* to maximal
-                * alignment, whereas before we only detected overflow if the
-                * original size was *greater* than maximal alignment.
-                */
-               if (usize < size || usize + alignment < usize) {
-                       /* size_t overflow. */
-                       return (0);
-               }
+
+               /* Make sure result is a large size class. */
+               usize = (size <= LARGE_MINCLASS) ? LARGE_MINCLASS : s2u(size);
 
                /*
                 * Calculate the size of the over-size run that arena_palloc()
                 * would need to allocate in order to guarantee the alignment.
-                * If the run wouldn't fit within a chunk, round up to a huge
-                * allocation size.
                 */
-               run_size = usize + alignment - PAGE;
-               if (run_size <= arena_maxclass)
-                       return (PAGE_CEILING(usize));
-               return (CHUNK_CEILING(usize));
+               if (usize + large_pad + alignment - PAGE <= arena_maxrun)
+                       return (usize);
        }
-}
 
-JEMALLOC_INLINE unsigned
-narenas_total_get(void)
-{
-       unsigned narenas;
+       /* Huge size class.  Beware of size_t overflow. */
 
-       malloc_mutex_lock(&arenas_lock);
-       narenas = narenas_total;
-       malloc_mutex_unlock(&arenas_lock);
+       /*
+        * We can't achieve subchunk alignment, so round up alignment to the
+        * minimum that can actually be supported.
+        */
+       alignment = CHUNK_CEILING(alignment);
+       if (alignment == 0) {
+               /* size_t overflow. */
+               return (0);
+       }
+
+       /* Make sure result is a huge size class. */
+       if (size <= chunksize)
+               usize = chunksize;
+       else {
+               usize = s2u(size);
+               if (usize < size) {
+                       /* size_t overflow. */
+                       return (0);
+               }
+       }
 
-       return (narenas);
+       /*
+        * Calculate the multi-chunk mapping that huge_palloc() would need in
+        * order to guarantee the alignment.
+        */
+       if (usize + alignment - PAGE < usize) {
+               /* size_t overflow. */
+               return (0);
+       }
+       return (usize);
 }
 
 /* Choose an arena based on a per-thread value. */
 JEMALLOC_INLINE arena_t *
-choose_arena(tsd_t *tsd, arena_t *arena)
+arena_choose(tsd_t *tsd, arena_t *arena)
 {
        arena_t *ret;
 
        if (arena != NULL)
                return (arena);
 
-       if (unlikely((ret = tsd_arena_get(tsd)) == NULL)) {
-               ret = choose_arena_hard(tsd);
-               assert(ret != NULL);
-       }
+       if (unlikely((ret = tsd_arena_get(tsd)) == NULL))
+               ret = arena_choose_hard(tsd);
 
        return (ret);
 }
+
+JEMALLOC_INLINE arena_t *
+arena_get(tsd_t *tsd, unsigned ind, bool init_if_missing,
+    bool refresh_if_missing)
+{
+       arena_t *arena;
+       arena_t **arenas_cache = tsd_arenas_cache_get(tsd);
+
+       /* init_if_missing requires refresh_if_missing. */
+       assert(!init_if_missing || refresh_if_missing);
+
+       if (unlikely(arenas_cache == NULL)) {
+               /* arenas_cache hasn't been initialized yet. */
+               return (arena_get_hard(tsd, ind, init_if_missing));
+       }
+       if (unlikely(ind >= tsd_narenas_cache_get(tsd))) {
+               /*
+                * ind is invalid, cache is old (too small), or arena to be
+                * initialized.
+                */
+               return (refresh_if_missing ? arena_get_hard(tsd, ind,
+                   init_if_missing) : NULL);
+       }
+       arena = arenas_cache[ind];
+       if (likely(arena != NULL) || !refresh_if_missing)
+               return (arena);
+       return (arena_get_hard(tsd, ind, init_if_missing));
+}
 #endif
 
 #include "jemalloc/internal/bitmap.h"
-#include "jemalloc/internal/rtree.h"
 /*
- * Include arena.h the second and third times in order to resolve circular
- * dependencies with tcache.h.
+ * Include portions of arena.h interleaved with tcache.h in order to resolve
+ * circular dependencies.
  */
-#define        JEMALLOC_ARENA_INLINE_B
+#define        JEMALLOC_ARENA_INLINE_A
 #include "jemalloc/internal/arena.h"
-#undef JEMALLOC_ARENA_INLINE_B
+#undef JEMALLOC_ARENA_INLINE_A
 #include "jemalloc/internal/tcache.h"
-#define        JEMALLOC_ARENA_INLINE_C
+#define        JEMALLOC_ARENA_INLINE_B
 #include "jemalloc/internal/arena.h"
-#undef JEMALLOC_ARENA_INLINE_C
+#undef JEMALLOC_ARENA_INLINE_B
 #include "jemalloc/internal/hash.h"
 #include "jemalloc/internal/quarantine.h"
 
 #ifndef JEMALLOC_ENABLE_INLINE
-void   *imalloct(tsd_t *tsd, size_t size, bool try_tcache, arena_t *arena);
+arena_t        *iaalloc(const void *ptr);
+size_t isalloc(const void *ptr, bool demote);
+void   *iallocztm(tsd_t *tsd, size_t size, bool zero, tcache_t *tcache,
+    bool is_metadata, arena_t *arena);
+void   *imalloct(tsd_t *tsd, size_t size, tcache_t *tcache, arena_t *arena);
 void   *imalloc(tsd_t *tsd, size_t size);
-void   *icalloct(tsd_t *tsd, size_t size, bool try_tcache, arena_t *arena);
+void   *icalloct(tsd_t *tsd, size_t size, tcache_t *tcache, arena_t *arena);
 void   *icalloc(tsd_t *tsd, size_t size);
+void   *ipallocztm(tsd_t *tsd, size_t usize, size_t alignment, bool zero,
+    tcache_t *tcache, bool is_metadata, arena_t *arena);
 void   *ipalloct(tsd_t *tsd, size_t usize, size_t alignment, bool zero,
-    bool try_tcache, arena_t *arena);
+    tcache_t *tcache, arena_t *arena);
 void   *ipalloc(tsd_t *tsd, size_t usize, size_t alignment, bool zero);
-size_t isalloc(const void *ptr, bool demote);
 size_t ivsalloc(const void *ptr, bool demote);
 size_t u2rz(size_t usize);
 size_t p2rz(const void *ptr);
-void   idalloct(tsd_t *tsd, void *ptr, bool try_tcache);
-void   isdalloct(tsd_t *tsd, void *ptr, size_t size, bool try_tcache);
+void   idalloctm(tsd_t *tsd, void *ptr, tcache_t *tcache, bool is_metadata);
+void   idalloct(tsd_t *tsd, void *ptr, tcache_t *tcache);
 void   idalloc(tsd_t *tsd, void *ptr);
-void   iqalloc(tsd_t *tsd, void *ptr, bool try_tcache);
-void   isqalloc(tsd_t *tsd, void *ptr, size_t size, bool try_tcache);
+void   iqalloc(tsd_t *tsd, void *ptr, tcache_t *tcache);
+void   isdalloct(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache);
+void   isqalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache);
 void   *iralloct_realign(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
-    size_t extra, size_t alignment, bool zero, bool try_tcache_alloc,
-    bool try_tcache_dalloc, arena_t *arena);
-void   *iralloct(tsd_t *tsd, void *ptr, size_t size, size_t alignment,
-    bool zero, bool try_tcache_alloc, bool try_tcache_dalloc, arena_t *arena);
-void   *iralloc(tsd_t *tsd, void *ptr, size_t size, size_t alignment,
-    bool zero);
-bool   ixalloc(void *ptr, size_t size, size_t extra, size_t alignment,
-    bool zero);
+    size_t extra, size_t alignment, bool zero, tcache_t *tcache,
+    arena_t *arena);
+void   *iralloct(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
+    size_t alignment, bool zero, tcache_t *tcache, arena_t *arena);
+void   *iralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
+    size_t alignment, bool zero);
+bool   ixalloc(void *ptr, size_t oldsize, size_t size, size_t extra,
+    size_t alignment, bool zero);
 #endif
 
 #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_))
+JEMALLOC_ALWAYS_INLINE arena_t *
+iaalloc(const void *ptr)
+{
+
+       assert(ptr != NULL);
+
+       return (arena_aalloc(ptr));
+}
+
+/*
+ * Typical usage:
+ *   void *ptr = [...]
+ *   size_t sz = isalloc(ptr, config_prof);
+ */
+JEMALLOC_ALWAYS_INLINE size_t
+isalloc(const void *ptr, bool demote)
+{
+
+       assert(ptr != NULL);
+       /* Demotion only makes sense if config_prof is true. */
+       assert(config_prof || !demote);
+
+       return (arena_salloc(ptr, demote));
+}
+
 JEMALLOC_ALWAYS_INLINE void *
-imalloct(tsd_t *tsd, size_t size, bool try_tcache, arena_t *arena)
+iallocztm(tsd_t *tsd, size_t size, bool zero, tcache_t *tcache, bool is_metadata,
+    arena_t *arena)
 {
+       void *ret;
 
        assert(size != 0);
 
-       if (size <= arena_maxclass)
-               return (arena_malloc(tsd, arena, size, false, try_tcache));
-       else
-               return (huge_malloc(tsd, arena, size, false));
+       ret = arena_malloc(tsd, arena, size, zero, tcache);
+       if (config_stats && is_metadata && likely(ret != NULL)) {
+               arena_metadata_allocated_add(iaalloc(ret), isalloc(ret,
+                   config_prof));
+       }
+       return (ret);
+}
+
+JEMALLOC_ALWAYS_INLINE void *
+imalloct(tsd_t *tsd, size_t size, tcache_t *tcache, arena_t *arena)
+{
+
+       return (iallocztm(tsd, size, false, tcache, false, arena));
 }
 
 JEMALLOC_ALWAYS_INLINE void *
 imalloc(tsd_t *tsd, size_t size)
 {
 
-       return (imalloct(tsd, size, true, NULL));
+       return (iallocztm(tsd, size, false, tcache_get(tsd, true), false, NULL));
 }
 
 JEMALLOC_ALWAYS_INLINE void *
-icalloct(tsd_t *tsd, size_t size, bool try_tcache, arena_t *arena)
+icalloct(tsd_t *tsd, size_t size, tcache_t *tcache, arena_t *arena)
 {
 
-       if (size <= arena_maxclass)
-               return (arena_malloc(tsd, arena, size, true, try_tcache));
-       else
-               return (huge_malloc(tsd, arena, size, true));
+       return (iallocztm(tsd, size, true, tcache, false, arena));
 }
 
 JEMALLOC_ALWAYS_INLINE void *
 icalloc(tsd_t *tsd, size_t size)
 {
 
-       return (icalloct(tsd, size, true, NULL));
+       return (iallocztm(tsd, size, true, tcache_get(tsd, true), false, NULL));
 }
 
 JEMALLOC_ALWAYS_INLINE void *
-ipalloct(tsd_t *tsd, size_t usize, size_t alignment, bool zero, bool try_tcache,
-    arena_t *arena)
+ipallocztm(tsd_t *tsd, size_t usize, size_t alignment, bool zero,
+    tcache_t *tcache, bool is_metadata, arena_t *arena)
 {
        void *ret;
 
        assert(usize != 0);
        assert(usize == sa2u(usize, alignment));
 
-       if (usize <= arena_maxclass && alignment <= PAGE)
-               ret = arena_malloc(tsd, arena, usize, zero, try_tcache);
-       else {
-               if (usize <= arena_maxclass) {
-                       ret = arena_palloc(choose_arena(tsd, arena), usize,
-                           alignment, zero);
-               } else if (alignment <= chunksize)
-                       ret = huge_malloc(tsd, arena, usize, zero);
-               else
-                       ret = huge_palloc(tsd, arena, usize, alignment, zero);
-       }
-
+       ret = arena_palloc(tsd, arena, usize, alignment, zero, tcache);
        assert(ALIGNMENT_ADDR2BASE(ret, alignment) == ret);
+       if (config_stats && is_metadata && likely(ret != NULL)) {
+               arena_metadata_allocated_add(iaalloc(ret), isalloc(ret,
+                   config_prof));
+       }
        return (ret);
 }
 
 JEMALLOC_ALWAYS_INLINE void *
-ipalloc(tsd_t *tsd, size_t usize, size_t alignment, bool zero)
+ipalloct(tsd_t *tsd, size_t usize, size_t alignment, bool zero,
+    tcache_t *tcache, arena_t *arena)
 {
 
-       return (ipalloct(tsd, usize, alignment, zero, true, NULL));
+       return (ipallocztm(tsd, usize, alignment, zero, tcache, false, arena));
 }
 
-/*
- * Typical usage:
- *   void *ptr = [...]
- *   size_t sz = isalloc(ptr, config_prof);
- */
-JEMALLOC_ALWAYS_INLINE size_t
-isalloc(const void *ptr, bool demote)
+JEMALLOC_ALWAYS_INLINE void *
+ipalloc(tsd_t *tsd, size_t usize, size_t alignment, bool zero)
 {
-       size_t ret;
-       arena_chunk_t *chunk;
 
-       assert(ptr != NULL);
-       /* Demotion only makes sense if config_prof is true. */
-       assert(config_prof || !demote);
-
-       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-       if (chunk != ptr)
-               ret = arena_salloc(ptr, demote);
-       else
-               ret = huge_salloc(ptr);
-
-       return (ret);
+       return (ipallocztm(tsd, usize, alignment, zero, tcache_get(tsd,
+           NULL), false, NULL));
 }
 
 JEMALLOC_ALWAYS_INLINE size_t
 ivsalloc(const void *ptr, bool demote)
 {
+       extent_node_t *node;
 
        /* Return 0 if ptr is not within a chunk managed by jemalloc. */
-       if (rtree_get(chunks_rtree, (uintptr_t)CHUNK_ADDR2BASE(ptr)) == 0)
+       node = chunk_lookup(ptr, false);
+       if (node == NULL)
                return (0);
+       /* Only arena chunks should be looked up via interior pointers. */
+       assert(extent_node_addr_get(node) == ptr ||
+           extent_node_achunk_get(node));
 
        return (isalloc(ptr, demote));
 }
@@ -742,7 +980,7 @@ u2rz(size_t usize)
        size_t ret;
 
        if (usize <= SMALL_MAXCLASS) {
-               size_t binind = small_size2bin(usize);
+               szind_t binind = size2index(usize);
                ret = arena_bin_info[binind].redzone_size;
        } else
                ret = 0;
@@ -759,64 +997,62 @@ p2rz(const void *ptr)
 }
 
 JEMALLOC_ALWAYS_INLINE void
-idalloct(tsd_t *tsd, void *ptr, bool try_tcache)
+idalloctm(tsd_t *tsd, void *ptr, tcache_t *tcache, bool is_metadata)
 {
-       arena_chunk_t *chunk;
 
        assert(ptr != NULL);
+       if (config_stats && is_metadata) {
+               arena_metadata_allocated_sub(iaalloc(ptr), isalloc(ptr,
+                   config_prof));
+       }
 
-       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-       if (chunk != ptr)
-               arena_dalloc(tsd, chunk, ptr, try_tcache);
-       else
-               huge_dalloc(ptr);
+       arena_dalloc(tsd, ptr, tcache);
 }
 
 JEMALLOC_ALWAYS_INLINE void
-isdalloct(tsd_t *tsd, void *ptr, size_t size, bool try_tcache)
+idalloct(tsd_t *tsd, void *ptr, tcache_t *tcache)
 {
-       arena_chunk_t *chunk;
-
-       assert(ptr != NULL);
 
-       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-       if (chunk != ptr)
-               arena_sdalloc(tsd, chunk, ptr, size, try_tcache);
-       else
-               huge_dalloc(ptr);
+       idalloctm(tsd, ptr, tcache, false);
 }
 
 JEMALLOC_ALWAYS_INLINE void
 idalloc(tsd_t *tsd, void *ptr)
 {
 
-       idalloct(tsd, ptr, true);
+       idalloctm(tsd, ptr, tcache_get(tsd, false), false);
 }
 
 JEMALLOC_ALWAYS_INLINE void
-iqalloc(tsd_t *tsd, void *ptr, bool try_tcache)
+iqalloc(tsd_t *tsd, void *ptr, tcache_t *tcache)
 {
 
        if (config_fill && unlikely(opt_quarantine))
                quarantine(tsd, ptr);
        else
-               idalloct(tsd, ptr, try_tcache);
+               idalloctm(tsd, ptr, tcache, false);
 }
 
 JEMALLOC_ALWAYS_INLINE void
-isqalloc(tsd_t *tsd, void *ptr, size_t size, bool try_tcache)
+isdalloct(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache)
+{
+
+       arena_sdalloc(tsd, ptr, size, tcache);
+}
+
+JEMALLOC_ALWAYS_INLINE void
+isqalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache)
 {
 
        if (config_fill && unlikely(opt_quarantine))
                quarantine(tsd, ptr);
        else
-               isdalloct(tsd, ptr, size, try_tcache);
+               isdalloct(tsd, ptr, size, tcache);
 }
 
 JEMALLOC_ALWAYS_INLINE void *
 iralloct_realign(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
-    size_t extra, size_t alignment, bool zero, bool try_tcache_alloc,
-    bool try_tcache_dalloc, arena_t *arena)
+    size_t extra, size_t alignment, bool zero, tcache_t *tcache, arena_t *arena)
 {
        void *p;
        size_t usize, copysize;
@@ -824,7 +1060,7 @@ iralloct_realign(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
        usize = sa2u(size + extra, alignment);
        if (usize == 0)
                return (NULL);
-       p = ipalloct(tsd, usize, alignment, zero, try_tcache_alloc, arena);
+       p = ipalloct(tsd, usize, alignment, zero, tcache, arena);
        if (p == NULL) {
                if (extra == 0)
                        return (NULL);
@@ -832,8 +1068,7 @@ iralloct_realign(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
                usize = sa2u(size, alignment);
                if (usize == 0)
                        return (NULL);
-               p = ipalloct(tsd, usize, alignment, zero, try_tcache_alloc,
-                   arena);
+               p = ipalloct(tsd, usize, alignment, zero, tcache, arena);
                if (p == NULL)
                        return (NULL);
        }
@@ -843,21 +1078,18 @@ iralloct_realign(tsd_t *tsd, void *ptr, size_t oldsize, size_t size,
         */
        copysize = (size < oldsize) ? size : oldsize;
        memcpy(p, ptr, copysize);
-       iqalloc(tsd, ptr, try_tcache_dalloc);
+       isqalloc(tsd, ptr, oldsize, tcache);
        return (p);
 }
 
 JEMALLOC_ALWAYS_INLINE void *
-iralloct(tsd_t *tsd, void *ptr, size_t size, size_t alignment, bool zero,
-    bool try_tcache_alloc, bool try_tcache_dalloc, arena_t *arena)
+iralloct(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment,
+    bool zero, tcache_t *tcache, arena_t *arena)
 {
-       size_t oldsize;
 
        assert(ptr != NULL);
        assert(size != 0);
 
-       oldsize = isalloc(ptr, config_prof);
-
        if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))
            != 0) {
                /*
@@ -865,44 +1097,37 @@ iralloct(tsd_t *tsd, void *ptr, size_t size, size_t alignment, bool zero,
                 * and copy.
                 */
                return (iralloct_realign(tsd, ptr, oldsize, size, 0, alignment,
-                   zero, try_tcache_alloc, try_tcache_dalloc, arena));
+                   zero, tcache, arena));
        }
 
-       if (size <= arena_maxclass) {
-               return (arena_ralloc(tsd, arena, ptr, oldsize, size, 0,
-                   alignment, zero, try_tcache_alloc, try_tcache_dalloc));
-       } else {
-               return (huge_ralloc(tsd, arena, ptr, oldsize, size, 0,
-                   alignment, zero, try_tcache_dalloc));
-       }
+       return (arena_ralloc(tsd, arena, ptr, oldsize, size, alignment, zero,
+           tcache));
 }
 
 JEMALLOC_ALWAYS_INLINE void *
-iralloc(tsd_t *tsd, void *ptr, size_t size, size_t alignment, bool zero)
+iralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment,
+    bool zero)
 {
 
-       return (iralloct(tsd, ptr, size, alignment, zero, true, true, NULL));
+       return (iralloct(tsd, ptr, oldsize, size, alignment, zero,
+           tcache_get(tsd, true), NULL));
 }
 
 JEMALLOC_ALWAYS_INLINE bool
-ixalloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero)
+ixalloc(void *ptr, size_t oldsize, size_t size, size_t extra, size_t alignment,
+    bool zero)
 {
-       size_t oldsize;
 
        assert(ptr != NULL);
        assert(size != 0);
 
-       oldsize = isalloc(ptr, config_prof);
        if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))
            != 0) {
                /* Existing object alignment is inadequate. */
                return (true);
        }
 
-       if (size <= arena_maxclass)
-               return (arena_ralloc_no_move(ptr, oldsize, size, extra, zero));
-       else
-               return (huge_ralloc_no_move(ptr, oldsize, size, extra, zero));
+       return (arena_ralloc_no_move(ptr, oldsize, size, extra, zero));
 }
 #endif
 
index fa590404708baf8129564203da583fe829be60bb..a601d6ebb2dcbfda2e8778feeddc703d01b32bc3 100644 (file)
@@ -4,14 +4,8 @@
 #include <math.h>
 #ifdef _WIN32
 #  include <windows.h>
-#  define ENOENT ERROR_PATH_NOT_FOUND
-#  define EINVAL ERROR_BAD_ARGUMENTS
-#  define EAGAIN ERROR_OUTOFMEMORY
-#  define EPERM  ERROR_WRITE_FAULT
-#  define EFAULT ERROR_INVALID_ADDRESS
-#  define ENOMEM ERROR_NOT_ENOUGH_MEMORY
-#  undef ERANGE
-#  define ERANGE ERROR_INVALID_DATA
+#  include "msvc_compat/windows_extra.h"
+
 #else
 #  include <sys/param.h>
 #  include <sys/mman.h>
@@ -40,7 +34,6 @@
 #ifndef offsetof
 #  define offsetof(type, member)       ((size_t)&(((type *)NULL)->member))
 #endif
-#include <inttypes.h>
 #include <string.h>
 #include <strings.h>
 #include <ctype.h>
@@ -50,8 +43,19 @@ typedef intptr_t ssize_t;
 #  define PATH_MAX 1024
 #  define STDERR_FILENO 2
 #  define __func__ __FUNCTION__
-/* Disable warnings about deprecated system functions */
+#  ifdef JEMALLOC_HAS_RESTRICT
+#    define restrict __restrict
+#  endif
+/* Disable warnings about deprecated system functions. */
 #  pragma warning(disable: 4996)
+#if _MSC_VER < 1800
+static int
+isblank(int c)
+{
+
+       return (c == '\t' || c == ' ');
+}
+#endif
 #else
 #  include <unistd.h>
 #endif
index fd85e5cf1323683798ca80e8d3096367d4422c27..b0f8caaf800255b6c5dd8bae407694df97f420bb 100644 (file)
@@ -22,6 +22,9 @@
  */
 #undef CPU_SPINWAIT
 
+/* Defined if C11 atomics are available. */
+#undef JEMALLOC_C11ATOMICS
+
 /* Defined if the equivalent of FreeBSD's atomic(9) functions are available. */
 #undef JEMALLOC_ATOMIC9
 
@@ -35,7 +38,7 @@
  * Defined if __sync_add_and_fetch(uint32_t *, uint32_t) and
  * __sync_sub_and_fetch(uint32_t *, uint32_t) are available, despite
  * __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 not being defined (which means the
- * functions are defined in libgcc instead of being inlines)
+ * functions are defined in libgcc instead of being inlines).
  */
 #undef JE_FORCE_SYNC_COMPARE_AND_SWAP_4
 
@@ -43,7 +46,7 @@
  * Defined if __sync_add_and_fetch(uint64_t *, uint64_t) and
  * __sync_sub_and_fetch(uint64_t *, uint64_t) are available, despite
  * __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 not being defined (which means the
- * functions are defined in libgcc instead of being inlines)
+ * functions are defined in libgcc instead of being inlines).
  */
 #undef JE_FORCE_SYNC_COMPARE_AND_SWAP_8
 
  */
 #undef JEMALLOC_OSSPIN
 
+/*
+ * Defined if secure_getenv(3) is available.
+ */
+#undef JEMALLOC_HAVE_SECURE_GETENV
+
+/*
+ * Defined if issetugid(2) is available.
+ */
+#undef JEMALLOC_HAVE_ISSETUGID
+
 /*
  * Defined if _malloc_thread_cleanup() exists.  At least in the case of
  * FreeBSD, pthread_key_create() allocates, which if used during malloc
 /* Support lazy locking (avoid locking unless a second thread is launched). */
 #undef JEMALLOC_LAZY_LOCK
 
-/* One page is 2^STATIC_PAGE_SHIFT bytes. */
-#undef STATIC_PAGE_SHIFT
+/* Minimum size class to support is 2^LG_TINY_MIN bytes. */
+#undef LG_TINY_MIN
+
+/*
+ * Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size
+ * classes).
+ */
+#undef LG_QUANTUM
+
+/* One page is 2^LG_PAGE bytes. */
+#undef LG_PAGE
+
+/*
+ * If defined, adjacent virtual memory mappings with identical attributes
+ * automatically coalesce, and they fragment when changes are made to subranges.
+ * This is the normal order of things for mmap()/munmap(), but on Windows
+ * VirtualAlloc()/VirtualFree() operations must be precisely matched, i.e.
+ * mappings do *not* coalesce/fragment.
+ */
+#undef JEMALLOC_MAPS_COALESCE
 
 /*
  * If defined, use munmap() to unmap freed chunks, rather than storing them for
  */
 #undef JEMALLOC_IVSALLOC
 
+/*
+ * If defined, explicitly attempt to more uniformly distribute large allocation
+ * pointer alignments across all cache indices.
+ */
+#undef JEMALLOC_CACHE_OBLIVIOUS
+
 /*
  * Darwin (OS X) uses zones to work around Mach-O symbol override shortcomings.
  */
 #undef JEMALLOC_PURGE_MADVISE_DONTNEED
 #undef JEMALLOC_PURGE_MADVISE_FREE
 
-/*
- * Define if operating system has alloca.h header.
- */
+/* Define if operating system has alloca.h header. */
 #undef JEMALLOC_HAS_ALLOCA_H
 
 /* C99 restrict keyword supported. */
 /* sizeof(intmax_t) == 2^LG_SIZEOF_INTMAX_T. */
 #undef LG_SIZEOF_INTMAX_T
 
-/* glibc malloc hooks (__malloc_hook, __realloc_hook, __free_hook) */
+/* glibc malloc hooks (__malloc_hook, __realloc_hook, __free_hook). */
 #undef JEMALLOC_GLIBC_MALLOC_HOOK
 
-/* glibc memalign hook */
+/* glibc memalign hook. */
 #undef JEMALLOC_GLIBC_MEMALIGN_HOOK
 
-/* adaptive mutex support in pthreads */
+/* Adaptive mutex support in pthreads. */
 #undef JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP
 
+/*
+ * If defined, jemalloc symbols are not exported (doesn't work when
+ * JEMALLOC_PREFIX is not defined).
+ */
+#undef JEMALLOC_EXPORT
+
 #endif /* JEMALLOC_INTERNAL_DEFS_H_ */
index 8a03d82504ecc5e3af73d0517dfb5ae2b7ea2d48..f051f2917b3b7611024a3b2cfb487b2875f28466 100644 (file)
@@ -26,7 +26,11 @@ typedef struct malloc_mutex_s malloc_mutex_t;
 
 struct malloc_mutex_s {
 #ifdef _WIN32
+#  if _WIN32_WINNT >= 0x0600
+       SRWLOCK                 lock;
+#  else
        CRITICAL_SECTION        lock;
+#  endif
 #elif (defined(JEMALLOC_OSSPIN))
        OSSpinLock              lock;
 #elif (defined(JEMALLOC_MUTEX_INIT_CB))
@@ -70,7 +74,11 @@ malloc_mutex_lock(malloc_mutex_t *mutex)
 
        if (isthreaded) {
 #ifdef _WIN32
+#  if _WIN32_WINNT >= 0x0600
+               AcquireSRWLockExclusive(&mutex->lock);
+#  else
                EnterCriticalSection(&mutex->lock);
+#  endif
 #elif (defined(JEMALLOC_OSSPIN))
                OSSpinLockLock(&mutex->lock);
 #else
@@ -85,7 +93,11 @@ malloc_mutex_unlock(malloc_mutex_t *mutex)
 
        if (isthreaded) {
 #ifdef _WIN32
+#  if _WIN32_WINNT >= 0x0600
+               ReleaseSRWLockExclusive(&mutex->lock);
+#  else
                LeaveCriticalSection(&mutex->lock);
+#  endif
 #elif (defined(JEMALLOC_OSSPIN))
                OSSpinLockUnlock(&mutex->lock);
 #else
diff --git a/src/jemalloc/include/jemalloc/internal/pages.h b/src/jemalloc/include/jemalloc/internal/pages.h
new file mode 100644 (file)
index 0000000..da7eb96
--- /dev/null
@@ -0,0 +1,26 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+void   *pages_map(void *addr, size_t size);
+void   pages_unmap(void *addr, size_t size);
+void   *pages_trim(void *addr, size_t alloc_size, size_t leadsize,
+    size_t size);
+bool   pages_commit(void *addr, size_t size);
+bool   pages_decommit(void *addr, size_t size);
+bool   pages_purge(void *addr, size_t size);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
+
index 4ea9a953d7eb88b1ad473f0477e1ff62d5d10a30..a90021aa6cd88da2b6e4bcfe1e463e31c4812dad 100644 (file)
@@ -1,50 +1,76 @@
-a0calloc
-a0free
+a0dalloc
+a0get
 a0malloc
+arena_aalloc
 arena_alloc_junk_small
 arena_bin_index
 arena_bin_info
 arena_bitselm_get
 arena_boot
+arena_choose
+arena_choose_hard
 arena_chunk_alloc_huge
+arena_chunk_cache_maybe_insert
+arena_chunk_cache_maybe_remove
 arena_chunk_dalloc_huge
+arena_chunk_ralloc_huge_expand
+arena_chunk_ralloc_huge_shrink
+arena_chunk_ralloc_huge_similar
 arena_cleanup
 arena_dalloc
 arena_dalloc_bin
-arena_dalloc_bin_locked
+arena_dalloc_bin_junked_locked
 arena_dalloc_junk_large
 arena_dalloc_junk_small
 arena_dalloc_large
-arena_dalloc_large_locked
+arena_dalloc_large_junked_locked
 arena_dalloc_small
 arena_dss_prec_get
 arena_dss_prec_set
+arena_get
+arena_get_hard
+arena_init
+arena_lg_dirty_mult_default_get
+arena_lg_dirty_mult_default_set
+arena_lg_dirty_mult_get
+arena_lg_dirty_mult_set
 arena_malloc
 arena_malloc_large
 arena_malloc_small
 arena_mapbits_allocated_get
 arena_mapbits_binind_get
+arena_mapbits_decommitted_get
 arena_mapbits_dirty_get
 arena_mapbits_get
+arena_mapbits_internal_set
 arena_mapbits_large_binind_set
 arena_mapbits_large_get
 arena_mapbits_large_set
 arena_mapbits_large_size_get
+arena_mapbitsp_get
+arena_mapbitsp_read
+arena_mapbitsp_write
+arena_mapbits_size_decode
+arena_mapbits_size_encode
 arena_mapbits_small_runind_get
 arena_mapbits_small_set
 arena_mapbits_unallocated_set
 arena_mapbits_unallocated_size_get
 arena_mapbits_unallocated_size_set
 arena_mapbits_unzeroed_get
-arena_mapbits_unzeroed_set
-arena_mapbitsp_get
-arena_mapbitsp_read
-arena_mapbitsp_write
-arena_maxclass
+arena_maxrun
+arena_maybe_purge
+arena_metadata_allocated_add
+arena_metadata_allocated_get
+arena_metadata_allocated_sub
+arena_migrate
 arena_miscelm_get
 arena_miscelm_to_pageind
 arena_miscelm_to_rpages
+arena_nbound
 arena_new
+arena_node_alloc
+arena_node_dalloc
 arena_palloc
 arena_postfork_child
 arena_postfork_parent
@@ -54,6 +80,7 @@ arena_prof_accum_impl
 arena_prof_accum_locked
 arena_prof_promoted
 arena_prof_tctx_get
+arena_prof_tctx_reset
 arena_prof_tctx_set
 arena_ptr_small_binind_get
 arena_purge_all
@@ -61,33 +88,37 @@ arena_quarantine_junk_small
 arena_ralloc
 arena_ralloc_junk_large
 arena_ralloc_no_move
+arena_rd_to_miscelm
 arena_redzone_corruption
 arena_run_regind
 arena_run_to_miscelm
 arena_salloc
+arenas_cache_bypass_cleanup
+arenas_cache_cleanup
 arena_sdalloc
 arena_stats_merge
 arena_tcache_fill_small
-arenas
-arenas_cleanup
-arenas_extend
-arenas_lock
+atomic_add_p
 atomic_add_u
 atomic_add_uint32
 atomic_add_uint64
 atomic_add_z
+atomic_cas_p
+atomic_cas_u
+atomic_cas_uint32
+atomic_cas_uint64
+atomic_cas_z
+atomic_sub_p
 atomic_sub_u
 atomic_sub_uint32
 atomic_sub_uint64
 atomic_sub_z
 base_alloc
 base_boot
-base_calloc
-base_node_alloc
-base_node_dalloc
 base_postfork_child
 base_postfork_parent
 base_prefork
+base_stats_get
 bitmap_full
 bitmap_get
 bitmap_info_init
@@ -97,51 +128,54 @@ bitmap_set
 bitmap_sfu
 bitmap_size
 bitmap_unset
+bootstrap_calloc
+bootstrap_free
+bootstrap_malloc
 bt_init
 buferror
-choose_arena
-choose_arena_hard
-chunk_alloc_arena
 chunk_alloc_base
-chunk_alloc_default
+chunk_alloc_cache
 chunk_alloc_dss
 chunk_alloc_mmap
+chunk_alloc_wrapper
 chunk_boot
-chunk_dalloc_default
+chunk_dalloc_arena
+chunk_dalloc_cache
 chunk_dalloc_mmap
+chunk_dalloc_wrapper
+chunk_deregister
 chunk_dss_boot
 chunk_dss_postfork_child
 chunk_dss_postfork_parent
 chunk_dss_prec_get
 chunk_dss_prec_set
 chunk_dss_prefork
+chunk_hooks_default
+chunk_hooks_get
+chunk_hooks_set
 chunk_in_dss
+chunk_lookup
 chunk_npages
 chunk_postfork_child
 chunk_postfork_parent
 chunk_prefork
-chunk_unmap
-chunks_mtx
-chunks_rtree
+chunk_purge_arena
+chunk_purge_wrapper
+chunk_register
 chunksize
 chunksize_mask
-ckh_bucket_search
+chunks_rtree
 ckh_count
 ckh_delete
-ckh_evict_reloc_insert
 ckh_insert
-ckh_isearch
 ckh_iter
 ckh_new
 ckh_pointer_hash
 ckh_pointer_keycomp
-ckh_rebuild
 ckh_remove
 ckh_search
 ckh_string_hash
 ckh_string_keycomp
-ckh_try_bucket_insert
-ckh_try_insert
 ctl_boot
 ctl_bymib
 ctl_byname
@@ -150,6 +184,23 @@ ctl_postfork_child
 ctl_postfork_parent
 ctl_prefork
 dss_prec_names
+extent_node_achunk_get
+extent_node_achunk_set
+extent_node_addr_get
+extent_node_addr_set
+extent_node_arena_get
+extent_node_arena_set
+extent_node_dirty_insert
+extent_node_dirty_linkage_init
+extent_node_dirty_remove
+extent_node_init
+extent_node_prof_tctx_get
+extent_node_prof_tctx_set
+extent_node_size_get
+extent_node_size_set
+extent_node_zeroed_get
+extent_node_zeroed_set
+extent_tree_ad_empty
 extent_tree_ad_first
 extent_tree_ad_insert
 extent_tree_ad_iter
@@ -166,6 +217,7 @@ extent_tree_ad_reverse_iter
 extent_tree_ad_reverse_iter_recurse
 extent_tree_ad_reverse_iter_start
 extent_tree_ad_search
+extent_tree_szad_empty
 extent_tree_szad_first
 extent_tree_szad_insert
 extent_tree_szad_iter
@@ -193,44 +245,48 @@ hash_rotl_64
 hash_x64_128
 hash_x86_128
 hash_x86_32
-huge_allocated
-huge_boot
+huge_aalloc
 huge_dalloc
 huge_dalloc_junk
 huge_malloc
-huge_ndalloc
-huge_nmalloc
 huge_palloc
-huge_postfork_child
-huge_postfork_parent
-huge_prefork
 huge_prof_tctx_get
+huge_prof_tctx_reset
 huge_prof_tctx_set
 huge_ralloc
 huge_ralloc_no_move
 huge_salloc
+iaalloc
+iallocztm
 icalloc
 icalloct
 idalloc
 idalloct
+idalloctm
 imalloc
 imalloct
+index2size
+index2size_compute
+index2size_lookup
+index2size_tab
 in_valgrind
 ipalloc
 ipalloct
+ipallocztm
 iqalloc
 iralloc
 iralloct
 iralloct_realign
 isalloc
 isdalloct
-isthreaded
 isqalloc
+isthreaded
 ivsalloc
 ixalloc
 jemalloc_postfork_child
 jemalloc_postfork_parent
 jemalloc_prefork
+large_maxclass
 lg_floor
 malloc_cprintf
 malloc_mutex_init
@@ -242,7 +298,8 @@ malloc_mutex_unlock
 malloc_printf
 malloc_snprintf
 malloc_strtoumax
-malloc_tsd_boot
+malloc_tsd_boot0
+malloc_tsd_boot1
 malloc_tsd_cleanup_register
 malloc_tsd_dalloc
 malloc_tsd_malloc
@@ -254,14 +311,15 @@ map_bias
 map_misc_offset
 mb_write
 mutex_boot
-narenas_auto
-narenas_total
+narenas_cache_cleanup
 narenas_total_get
 ncpus
 nhbins
 opt_abort
 opt_dss
 opt_junk
+opt_junk_alloc
+opt_junk_free
 opt_lg_chunk
 opt_lg_dirty_mult
 opt_lg_prof_interval
@@ -275,6 +333,7 @@ opt_prof_final
 opt_prof_gdump
 opt_prof_leak
 opt_prof_prefix
+opt_prof_thread_active_init
 opt_quarantine
 opt_redzone
 opt_stats_print
@@ -283,7 +342,12 @@ opt_utrace
 opt_xmalloc
 opt_zero
 p2rz
+pages_commit
+pages_decommit
+pages_map
 pages_purge
+pages_trim
+pages_unmap
 pow2_ceil
 prof_active_get
 prof_active_get_unlocked
@@ -294,12 +358,15 @@ prof_backtrace
 prof_boot0
 prof_boot1
 prof_boot2
-prof_bt_count
 prof_dump_header
 prof_dump_open
 prof_free
 prof_free_sampled_object
 prof_gdump
+prof_gdump_get
+prof_gdump_get_unlocked
+prof_gdump_set
+prof_gdump_val
 prof_idump
 prof_interval
 prof_lookup
@@ -314,10 +381,12 @@ prof_reset
 prof_sample_accum_update
 prof_sample_threshold_update
 prof_tctx_get
+prof_tctx_reset
 prof_tctx_set
 prof_tdata_cleanup
 prof_tdata_get
 prof_tdata_init
+prof_tdata_reinit
 prof_thread_active_get
 prof_thread_active_init_get
 prof_thread_active_init_set
@@ -326,36 +395,37 @@ prof_thread_name_get
 prof_thread_name_set
 quarantine
 quarantine_alloc_hook
+quarantine_alloc_hook_work
 quarantine_cleanup
-quarantine_init
 register_zone
+rtree_child_read
+rtree_child_read_hard
+rtree_child_tryread
 rtree_delete
 rtree_get
-rtree_get_locked
 rtree_new
-rtree_postfork_child
-rtree_postfork_parent
-rtree_prefork
+rtree_node_valid
 rtree_set
+rtree_start_level
+rtree_subkey
+rtree_subtree_read
+rtree_subtree_read_hard
+rtree_subtree_tryread
+rtree_val_read
+rtree_val_write
 s2u
+s2u_compute
+s2u_lookup
 sa2u
 set_errno
-small_bin2size
-small_bin2size_compute
-small_bin2size_lookup
-small_bin2size_tab
-small_s2u
-small_s2u_compute
-small_s2u_lookup
-small_size2bin
-small_size2bin_compute
-small_size2bin_lookup
-small_size2bin_tab
+size2index
+size2index_compute
+size2index_lookup
+size2index_tab
 stats_cactive
 stats_cactive_add
 stats_cactive_get
 stats_cactive_sub
-stats_chunks
 stats_print
 tcache_alloc_easy
 tcache_alloc_large
@@ -363,6 +433,7 @@ tcache_alloc_small
 tcache_alloc_small_hard
 tcache_arena_associate
 tcache_arena_dissociate
+tcache_arena_reassociate
 tcache_bin_flush_large
 tcache_bin_flush_small
 tcache_bin_info
@@ -380,19 +451,27 @@ tcache_flush
 tcache_get
 tcache_get_hard
 tcache_maxclass
+tcaches
 tcache_salloc
+tcaches_create
+tcaches_destroy
+tcaches_flush
+tcaches_get
 tcache_stats_merge
 thread_allocated_cleanup
 thread_deallocated_cleanup
-tsd_booted
 tsd_arena_get
 tsd_arena_set
 tsd_boot
+tsd_boot0
+tsd_boot1
+tsd_booted
 tsd_cleanup
 tsd_cleanup_wrapper
 tsd_fetch
 tsd_get
-tsd_get_wrapper
+tsd_wrapper_get
+tsd_wrapper_set
 tsd_initialized
 tsd_init_check_recursion
 tsd_init_finish
index c6b1797226e0fb2f58e26897020b13c0f2f99d0c..216d0ef47bda9b630d6cdcb704aa7dad61c4e614 100644 (file)
  *   const uint32_t a, c : See above discussion.
  */
 #define        prng32(r, lg_range, state, a, c) do {                           \
-       assert(lg_range > 0);                                           \
-       assert(lg_range <= 32);                                         \
+       assert((lg_range) > 0);                                         \
+       assert((lg_range) <= 32);                                       \
                                                                        \
        r = (state * (a)) + (c);                                        \
        state = r;                                                      \
-       r >>= (32 - lg_range);                                          \
+       r >>= (32 - (lg_range));                                        \
 } while (false)
 
 /* Same as prng32(), but 64 bits of pseudo-randomness, using uint64_t. */
 #define        prng64(r, lg_range, state, a, c) do {                           \
-       assert(lg_range > 0);                                           \
-       assert(lg_range <= 64);                                         \
+       assert((lg_range) > 0);                                         \
+       assert((lg_range) <= 64);                                       \
                                                                        \
        r = (state * (a)) + (c);                                        \
        state = r;                                                      \
-       r >>= (64 - lg_range);                                          \
+       r >>= (64 - (lg_range));                                        \
 } while (false)
 
 #endif /* JEMALLOC_H_TYPES */
index c8014717ece797d7e864ef9da27b2e08c25a979c..e5198c3e8153a3096ddf4b2b2dd059ee57a5259f 100644 (file)
@@ -89,12 +89,34 @@ struct prof_tctx_s {
        /* Thread data for thread that performed the allocation. */
        prof_tdata_t            *tdata;
 
+       /*
+        * Copy of tdata->thr_{uid,discrim}, necessary because tdata may be
+        * defunct during teardown.
+        */
+       uint64_t                thr_uid;
+       uint64_t                thr_discrim;
+
        /* Profiling counters, protected by tdata->lock. */
        prof_cnt_t              cnts;
 
        /* Associated global context. */
        prof_gctx_t             *gctx;
 
+       /*
+        * UID that distinguishes multiple tctx's created by the same thread,
+        * but coexisting in gctx->tctxs.  There are two ways that such
+        * coexistence can occur:
+        * - A dumper thread can cause a tctx to be retained in the purgatory
+        *   state.
+        * - Although a single "producer" thread must create all tctx's which
+        *   share the same thr_uid, multiple "consumers" can each concurrently
+        *   execute portions of prof_tctx_destroy().  prof_tctx_destroy() only
+        *   gets called once each time cnts.cur{objs,bytes} drop to 0, but this
+        *   threshold can be hit again before the first consumer finishes
+        *   executing prof_tctx_destroy().
+        */
+       uint64_t                tctx_uid;
+
        /* Linkage into gctx's tctxs. */
        rb_node(prof_tctx_t)    tctx_link;
 
@@ -171,6 +193,13 @@ struct prof_tdata_s {
 
        rb_node(prof_tdata_t)   tdata_link;
 
+       /*
+        * Counter used to initialize prof_tctx_t's tctx_uid.  No locking is
+        * necessary when incrementing this field, because only one thread ever
+        * does so.
+        */
+       uint64_t                tctx_uid_next;
+
        /*
         * Hash of (prof_bt_t *)-->(prof_tctx_t *).  Each thread tracks
         * backtraces for which it has non-zero allocation/deallocation counters
@@ -233,6 +262,9 @@ extern char opt_prof_prefix[
 /* Accessed via prof_active_[gs]et{_unlocked,}(). */
 extern bool    prof_active;
 
+/* Accessed via prof_gdump_[gs]et{_unlocked,}(). */
+extern bool    prof_gdump_val;
+
 /*
  * Profile dump interval, measured in bytes allocated.  Each arena triggers a
  * profile dump when it reaches this threshold.  The effect is that the
@@ -279,6 +311,8 @@ bool        prof_thread_active_get(void);
 bool   prof_thread_active_set(bool active);
 bool   prof_thread_active_init_get(void);
 bool   prof_thread_active_init_set(bool active_init);
+bool   prof_gdump_get(void);
+bool   prof_gdump_set(bool active);
 void   prof_boot0(void);
 void   prof_boot1(void);
 bool   prof_boot2(void);
@@ -293,17 +327,22 @@ void      prof_sample_threshold_update(prof_tdata_t *tdata);
 
 #ifndef JEMALLOC_ENABLE_INLINE
 bool   prof_active_get_unlocked(void);
+bool   prof_gdump_get_unlocked(void);
 prof_tdata_t   *prof_tdata_get(tsd_t *tsd, bool create);
 bool   prof_sample_accum_update(tsd_t *tsd, size_t usize, bool commit,
     prof_tdata_t **tdata_out);
-prof_tctx_t    *prof_alloc_prep(tsd_t *tsd, size_t usize, bool update);
+prof_tctx_t    *prof_alloc_prep(tsd_t *tsd, size_t usize, bool prof_active,
+    bool update);
 prof_tctx_t    *prof_tctx_get(const void *ptr);
-void   prof_tctx_set(const void *ptr, prof_tctx_t *tctx);
+void   prof_tctx_set(const void *ptr, size_t usize, prof_tctx_t *tctx);
+void   prof_tctx_reset(const void *ptr, size_t usize, const void *old_ptr,
+    prof_tctx_t *tctx);
 void   prof_malloc_sample_object(const void *ptr, size_t usize,
     prof_tctx_t *tctx);
 void   prof_malloc(const void *ptr, size_t usize, prof_tctx_t *tctx);
 void   prof_realloc(tsd_t *tsd, const void *ptr, size_t usize,
-    prof_tctx_t *tctx, bool updated, size_t old_usize, prof_tctx_t *old_tctx);
+    prof_tctx_t *tctx, bool prof_active, bool updated, const void *old_ptr,
+    size_t old_usize, prof_tctx_t *old_tctx);
 void   prof_free(tsd_t *tsd, const void *ptr, size_t usize);
 #endif
 
@@ -321,6 +360,18 @@ prof_active_get_unlocked(void)
        return (prof_active);
 }
 
+JEMALLOC_ALWAYS_INLINE bool
+prof_gdump_get_unlocked(void)
+{
+
+       /*
+        * No locking is used when reading prof_gdump_val in the fast path, so
+        * there are no guarantees regarding how long it will take for all
+        * threads to notice state changes.
+        */
+       return (prof_gdump_val);
+}
+
 JEMALLOC_ALWAYS_INLINE prof_tdata_t *
 prof_tdata_get(tsd_t *tsd, bool create)
 {
@@ -348,36 +399,32 @@ prof_tdata_get(tsd_t *tsd, bool create)
 JEMALLOC_ALWAYS_INLINE prof_tctx_t *
 prof_tctx_get(const void *ptr)
 {
-       prof_tctx_t *ret;
-       arena_chunk_t *chunk;
 
        cassert(config_prof);
        assert(ptr != NULL);
 
-       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-       if (chunk != ptr) {
-               /* Region. */
-               ret = arena_prof_tctx_get(ptr);
-       } else
-               ret = huge_prof_tctx_get(ptr);
+       return (arena_prof_tctx_get(ptr));
+}
 
-       return (ret);
+JEMALLOC_ALWAYS_INLINE void
+prof_tctx_set(const void *ptr, size_t usize, prof_tctx_t *tctx)
+{
+
+       cassert(config_prof);
+       assert(ptr != NULL);
+
+       arena_prof_tctx_set(ptr, usize, tctx);
 }
 
 JEMALLOC_ALWAYS_INLINE void
-prof_tctx_set(const void *ptr, prof_tctx_t *tctx)
+prof_tctx_reset(const void *ptr, size_t usize, const void *old_ptr,
+    prof_tctx_t *old_tctx)
 {
-       arena_chunk_t *chunk;
 
        cassert(config_prof);
        assert(ptr != NULL);
 
-       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-       if (chunk != ptr) {
-               /* Region. */
-               arena_prof_tctx_set(ptr, tctx);
-       } else
-               huge_prof_tctx_set(ptr, tctx);
+       arena_prof_tctx_reset(ptr, usize, old_ptr, old_tctx);
 }
 
 JEMALLOC_ALWAYS_INLINE bool
@@ -411,7 +458,7 @@ prof_sample_accum_update(tsd_t *tsd, size_t usize, bool update,
 }
 
 JEMALLOC_ALWAYS_INLINE prof_tctx_t *
-prof_alloc_prep(tsd_t *tsd, size_t usize, bool update)
+prof_alloc_prep(tsd_t *tsd, size_t usize, bool prof_active, bool update)
 {
        prof_tctx_t *ret;
        prof_tdata_t *tdata;
@@ -419,8 +466,8 @@ prof_alloc_prep(tsd_t *tsd, size_t usize, bool update)
 
        assert(usize == s2u(usize));
 
-       if (!prof_active_get_unlocked() || likely(prof_sample_accum_update(tsd,
-           usize, update, &tdata)))
+       if (!prof_active || likely(prof_sample_accum_update(tsd, usize, update,
+           &tdata)))
                ret = (prof_tctx_t *)(uintptr_t)1U;
        else {
                bt_init(&bt, tdata->vec);
@@ -442,22 +489,24 @@ prof_malloc(const void *ptr, size_t usize, prof_tctx_t *tctx)
        if (unlikely((uintptr_t)tctx > (uintptr_t)1U))
                prof_malloc_sample_object(ptr, usize, tctx);
        else
-               prof_tctx_set(ptr, (prof_tctx_t *)(uintptr_t)1U);
+               prof_tctx_set(ptr, usize, (prof_tctx_t *)(uintptr_t)1U);
 }
 
 JEMALLOC_ALWAYS_INLINE void
 prof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx,
-    bool updated, size_t old_usize, prof_tctx_t *old_tctx)
+    bool prof_active, bool updated, const void *old_ptr, size_t old_usize,
+    prof_tctx_t *old_tctx)
 {
+       bool sampled, old_sampled;
 
        cassert(config_prof);
        assert(ptr != NULL || (uintptr_t)tctx <= (uintptr_t)1U);
 
-       if (!updated && ptr != NULL) {
+       if (prof_active && !updated && ptr != NULL) {
                assert(usize == isalloc(ptr, true));
                if (prof_sample_accum_update(tsd, usize, true, NULL)) {
                        /*
-                        * Don't sample.  The usize passed to PROF_ALLOC_PREP()
+                        * Don't sample.  The usize passed to prof_alloc_prep()
                         * was larger than what actually got allocated, so a
                         * backtrace was captured for this allocation, even
                         * though its actual usize was insufficient to cross the
@@ -467,12 +516,16 @@ prof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx,
                }
        }
 
-       if (unlikely((uintptr_t)old_tctx > (uintptr_t)1U))
-               prof_free_sampled_object(tsd, old_usize, old_tctx);
-       if (unlikely((uintptr_t)tctx > (uintptr_t)1U))
+       sampled = ((uintptr_t)tctx > (uintptr_t)1U);
+       old_sampled = ((uintptr_t)old_tctx > (uintptr_t)1U);
+
+       if (unlikely(sampled))
                prof_malloc_sample_object(ptr, usize, tctx);
        else
-               prof_tctx_set(ptr, (prof_tctx_t *)(uintptr_t)1U);
+               prof_tctx_reset(ptr, usize, old_ptr, old_tctx);
+
+       if (unlikely(old_sampled))
+               prof_free_sampled_object(tsd, old_usize, old_tctx);
 }
 
 JEMALLOC_ALWAYS_INLINE void
index f70c5f6f3919aaea8338108aab59b01b278ff8f0..1834bb8557ac6c26956a620d3e1b3467cd820ddb 100644 (file)
@@ -1,6 +1,4 @@
-/*
- * List definitions.
- */
+/* List definitions. */
 #define        ql_head(a_type)                                                 \
 struct {                                                               \
        a_type *qlh_first;                                              \
index 602944b9b4fa4281bde94f7ddce35ab9ba62b845..0fbaec25e7c063c0df37db41f713f16e83963073 100644 (file)
@@ -40,8 +40,10 @@ struct {                                                             \
        (a_qr_b)->a_field.qre_prev = t;                                 \
 } while (0)
 
-/* qr_meld() and qr_split() are functionally equivalent, so there's no need to
- * have two copies of the code. */
+/*
+ * qr_meld() and qr_split() are functionally equivalent, so there's no need to
+ * have two copies of the code.
+ */
 #define        qr_split(a_qr_a, a_qr_b, a_field)                               \
        qr_meld((a_qr_a), (a_qr_b), a_field)
 
index 4e9c710ae4ee2c349ffd29f02402042fcd1504fd..ae607399f6d7eef9b43af481bb10c0e05298f3ea 100644 (file)
@@ -29,7 +29,7 @@ struct quarantine_s {
 /******************************************************************************/
 #ifdef JEMALLOC_H_EXTERNS
 
-quarantine_t   *quarantine_init(tsd_t *tsd, size_t lg_maxobjs);
+void   quarantine_alloc_hook_work(tsd_t *tsd);
 void   quarantine(tsd_t *tsd, void *ptr);
 void   quarantine_cleanup(tsd_t *tsd);
 
@@ -50,8 +50,8 @@ quarantine_alloc_hook(void)
        assert(config_fill && opt_quarantine);
 
        tsd = tsd_fetch();
-       if (tsd_quarantine_get(tsd) == NULL && tsd_nominal(tsd))
-               tsd_quarantine_set(tsd, quarantine_init(tsd, LG_MAXOBJS_INIT));
+       if (tsd_quarantine_get(tsd) == NULL)
+               quarantine_alloc_hook_work(tsd);
 }
 #endif
 
index 64fab89c009abe4e592fabd1e5082bdb6e6401a8..2ca8e5933b2888bcfa0b1649157b3821fcdbd308 100644 (file)
@@ -200,7 +200,7 @@ a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start,           \
  *                 int (a_cmp *)(a_type *a_node, a_type *a_other);
  *                                       ^^^^^^
  *                                    or a_key
- *               Interpretation of comparision function return values:
+ *               Interpretation of comparison function return values:
  *                 -1 : a_node <  a_other
  *                  0 : a_node == a_other
  *                  1 : a_node >  a_other
@@ -693,7 +693,7 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) {                        \
                    rbtn_rotate_left(a_type, a_field, pathp->node,      \
                      tnode);                                           \
                    /* Balance restored, but rotation modified        */\
-                   /* subree root, which may actually be the tree    */\
+                   /* subtree root, which may actually be the tree   */\
                    /* root.                                          */\
                    if (pathp == path) {                                \
                        /* Set root. */                                 \
index bc74769f50ed68588850c36366b9ec7df9896f8d..28ae9d1dd2d1a51906614eeab1533e121c17f303 100644 (file)
 /*
  * This radix tree implementation is tailored to the singular purpose of
- * tracking which chunks are currently owned by jemalloc.  This functionality
- * is mandatory for OS X, where jemalloc must be able to respond to object
- * ownership queries.
+ * associating metadata with chunks that are currently owned by jemalloc.
  *
  *******************************************************************************
  */
 #ifdef JEMALLOC_H_TYPES
 
+typedef struct rtree_node_elm_s rtree_node_elm_t;
+typedef struct rtree_level_s rtree_level_t;
 typedef struct rtree_s rtree_t;
 
 /*
- * Size of each radix tree node (must be a power of 2).  This impacts tree
- * depth.
+ * RTREE_BITS_PER_LEVEL must be a power of two that is no larger than the
+ * machine address width.
  */
-#define        RTREE_NODESIZE (1U << 16)
+#define        LG_RTREE_BITS_PER_LEVEL 4
+#define        RTREE_BITS_PER_LEVEL    (ZU(1) << LG_RTREE_BITS_PER_LEVEL)
+#define        RTREE_HEIGHT_MAX                                                \
+    ((ZU(1) << (LG_SIZEOF_PTR+3)) / RTREE_BITS_PER_LEVEL)
 
-typedef void *(rtree_alloc_t)(size_t);
-typedef void (rtree_dalloc_t)(void *);
+/* Used for two-stage lock-free node initialization. */
+#define        RTREE_NODE_INITIALIZING ((rtree_node_elm_t *)0x1)
+
+/*
+ * The node allocation callback function's argument is the number of contiguous
+ * rtree_node_elm_t structures to allocate, and the resulting memory must be
+ * zeroed.
+ */
+typedef rtree_node_elm_t *(rtree_node_alloc_t)(size_t);
+typedef void (rtree_node_dalloc_t)(rtree_node_elm_t *);
 
 #endif /* JEMALLOC_H_TYPES */
 /******************************************************************************/
 #ifdef JEMALLOC_H_STRUCTS
 
+struct rtree_node_elm_s {
+       union {
+               void                    *pun;
+               rtree_node_elm_t        *child;
+               extent_node_t           *val;
+       };
+};
+
+struct rtree_level_s {
+       /*
+        * A non-NULL subtree points to a subtree rooted along the hypothetical
+        * path to the leaf node corresponding to key 0.  Depending on what keys
+        * have been used to store to the tree, an arbitrary combination of
+        * subtree pointers may remain NULL.
+        *
+        * Suppose keys comprise 48 bits, and LG_RTREE_BITS_PER_LEVEL is 4.
+        * This results in a 3-level tree, and the leftmost leaf can be directly
+        * accessed via subtrees[2], the subtree prefixed by 0x0000 (excluding
+        * 0x00000000) can be accessed via subtrees[1], and the remainder of the
+        * tree can be accessed via subtrees[0].
+        *
+        *   levels[0] : [<unused> | 0x0001******** | 0x0002******** | ...]
+        *
+        *   levels[1] : [<unused> | 0x00000001**** | 0x00000002**** | ... ]
+        *
+        *   levels[2] : [val(0x000000000000) | val(0x000000000001) | ...]
+        *
+        * This has practical implications on x64, which currently uses only the
+        * lower 47 bits of virtual address space in userland, thus leaving
+        * subtrees[0] unused and avoiding a level of tree traversal.
+        */
+       union {
+               void                    *subtree_pun;
+               rtree_node_elm_t        *subtree;
+       };
+       /* Number of key bits distinguished by this level. */
+       unsigned                bits;
+       /*
+        * Cumulative number of key bits distinguished by traversing to
+        * corresponding tree level.
+        */
+       unsigned                cumbits;
+};
+
 struct rtree_s {
-       rtree_alloc_t   *alloc;
-       rtree_dalloc_t  *dalloc;
-       malloc_mutex_t  mutex;
-       void            **root;
-       unsigned        height;
-       unsigned        level2bits[1]; /* Dynamically sized. */
+       rtree_node_alloc_t      *alloc;
+       rtree_node_dalloc_t     *dalloc;
+       unsigned                height;
+       /*
+        * Precomputed table used to convert from the number of leading 0 key
+        * bits to which subtree level to start at.
+        */
+       unsigned                start_level[RTREE_HEIGHT_MAX];
+       rtree_level_t           levels[RTREE_HEIGHT_MAX];
 };
 
 #endif /* JEMALLOC_H_STRUCTS */
 /******************************************************************************/
 #ifdef JEMALLOC_H_EXTERNS
 
-rtree_t        *rtree_new(unsigned bits, rtree_alloc_t *alloc, rtree_dalloc_t *dalloc);
+bool rtree_new(rtree_t *rtree, unsigned bits, rtree_node_alloc_t *alloc,
+    rtree_node_dalloc_t *dalloc);
 void   rtree_delete(rtree_t *rtree);
-void   rtree_prefork(rtree_t *rtree);
-void   rtree_postfork_parent(rtree_t *rtree);
-void   rtree_postfork_child(rtree_t *rtree);
+rtree_node_elm_t       *rtree_subtree_read_hard(rtree_t *rtree,
+    unsigned level);
+rtree_node_elm_t       *rtree_child_read_hard(rtree_t *rtree,
+    rtree_node_elm_t *elm, unsigned level);
 
 #endif /* JEMALLOC_H_EXTERNS */
 /******************************************************************************/
 #ifdef JEMALLOC_H_INLINES
 
 #ifndef JEMALLOC_ENABLE_INLINE
-#ifdef JEMALLOC_DEBUG
-uint8_t rtree_get_locked(rtree_t *rtree, uintptr_t key);
-#endif
-uint8_t        rtree_get(rtree_t *rtree, uintptr_t key);
-bool   rtree_set(rtree_t *rtree, uintptr_t key, uint8_t val);
+unsigned       rtree_start_level(rtree_t *rtree, uintptr_t key);
+uintptr_t      rtree_subkey(rtree_t *rtree, uintptr_t key, unsigned level);
+
+bool   rtree_node_valid(rtree_node_elm_t *node);
+rtree_node_elm_t       *rtree_child_tryread(rtree_node_elm_t *elm);
+rtree_node_elm_t       *rtree_child_read(rtree_t *rtree, rtree_node_elm_t *elm,
+    unsigned level);
+extent_node_t  *rtree_val_read(rtree_t *rtree, rtree_node_elm_t *elm,
+    bool dependent);
+void   rtree_val_write(rtree_t *rtree, rtree_node_elm_t *elm,
+    const extent_node_t *val);
+rtree_node_elm_t       *rtree_subtree_tryread(rtree_t *rtree, unsigned level);
+rtree_node_elm_t       *rtree_subtree_read(rtree_t *rtree, unsigned level);
+
+extent_node_t  *rtree_get(rtree_t *rtree, uintptr_t key, bool dependent);
+bool   rtree_set(rtree_t *rtree, uintptr_t key, const extent_node_t *val);
 #endif
 
 #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_RTREE_C_))
-#define        RTREE_GET_GENERATE(f)                                           \
-/* The least significant bits of the key are ignored. */               \
-JEMALLOC_INLINE uint8_t                                                        \
-f(rtree_t *rtree, uintptr_t key)                                       \
-{                                                                      \
-       uint8_t ret;                                                    \
-       uintptr_t subkey;                                               \
-       unsigned i, lshift, height, bits;                               \
-       void **node, **child;                                           \
-                                                                       \
-       RTREE_LOCK(&rtree->mutex);                                      \
-       for (i = lshift = 0, height = rtree->height, node = rtree->root;\
-           i < height - 1;                                             \
-           i++, lshift += bits, node = child) {                        \
-               bits = rtree->level2bits[i];                            \
-               subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR + \
-                   3)) - bits);                                        \
-               child = (void**)node[subkey];                           \
-               if (child == NULL) {                                    \
-                       RTREE_UNLOCK(&rtree->mutex);                    \
-                       return (0);                                     \
-               }                                                       \
-       }                                                               \
-                                                                       \
-       /*                                                              \
-        * node is a leaf, so it contains values rather than node       \
-        * pointers.                                                    \
-        */                                                             \
-       bits = rtree->level2bits[i];                                    \
-       subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) -     \
-           bits);                                                      \
-       {                                                               \
-               uint8_t *leaf = (uint8_t *)node;                        \
-               ret = leaf[subkey];                                     \
-       }                                                               \
-       RTREE_UNLOCK(&rtree->mutex);                                    \
-                                                                       \
-       RTREE_GET_VALIDATE                                              \
-       return (ret);                                                   \
+JEMALLOC_INLINE unsigned
+rtree_start_level(rtree_t *rtree, uintptr_t key)
+{
+       unsigned start_level;
+
+       if (unlikely(key == 0))
+               return (rtree->height - 1);
+
+       start_level = rtree->start_level[lg_floor(key) >>
+           LG_RTREE_BITS_PER_LEVEL];
+       assert(start_level < rtree->height);
+       return (start_level);
 }
 
-#ifdef JEMALLOC_DEBUG
-#  define RTREE_LOCK(l)                malloc_mutex_lock(l)
-#  define RTREE_UNLOCK(l)      malloc_mutex_unlock(l)
-#  define RTREE_GET_VALIDATE
-RTREE_GET_GENERATE(rtree_get_locked)
-#  undef RTREE_LOCK
-#  undef RTREE_UNLOCK
-#  undef RTREE_GET_VALIDATE
-#endif
+JEMALLOC_INLINE uintptr_t
+rtree_subkey(rtree_t *rtree, uintptr_t key, unsigned level)
+{
 
-#define        RTREE_LOCK(l)
-#define        RTREE_UNLOCK(l)
-#ifdef JEMALLOC_DEBUG
-   /*
-    * Suppose that it were possible for a jemalloc-allocated chunk to be
-    * munmap()ped, followed by a different allocator in another thread re-using
-    * overlapping virtual memory, all without invalidating the cached rtree
-    * value.  The result would be a false positive (the rtree would claim that
-    * jemalloc owns memory that it had actually discarded).  This scenario
-    * seems impossible, but the following assertion is a prudent sanity check.
-    */
-#  define RTREE_GET_VALIDATE                                           \
-       assert(rtree_get_locked(rtree, key) == ret);
-#else
-#  define RTREE_GET_VALIDATE
-#endif
-RTREE_GET_GENERATE(rtree_get)
-#undef RTREE_LOCK
-#undef RTREE_UNLOCK
-#undef RTREE_GET_VALIDATE
+       return ((key >> ((ZU(1) << (LG_SIZEOF_PTR+3)) -
+           rtree->levels[level].cumbits)) & ((ZU(1) <<
+           rtree->levels[level].bits) - 1));
+}
 
 JEMALLOC_INLINE bool
-rtree_set(rtree_t *rtree, uintptr_t key, uint8_t val)
+rtree_node_valid(rtree_node_elm_t *node)
+{
+
+       return ((uintptr_t)node > (uintptr_t)RTREE_NODE_INITIALIZING);
+}
+
+JEMALLOC_INLINE rtree_node_elm_t *
+rtree_child_tryread(rtree_node_elm_t *elm)
+{
+       rtree_node_elm_t *child;
+
+       /* Double-checked read (first read may be stale. */
+       child = elm->child;
+       if (!rtree_node_valid(child))
+               child = atomic_read_p(&elm->pun);
+       return (child);
+}
+
+JEMALLOC_INLINE rtree_node_elm_t *
+rtree_child_read(rtree_t *rtree, rtree_node_elm_t *elm, unsigned level)
+{
+       rtree_node_elm_t *child;
+
+       child = rtree_child_tryread(elm);
+       if (unlikely(!rtree_node_valid(child)))
+               child = rtree_child_read_hard(rtree, elm, level);
+       return (child);
+}
+
+JEMALLOC_INLINE extent_node_t *
+rtree_val_read(rtree_t *rtree, rtree_node_elm_t *elm, bool dependent)
+{
+
+       if (dependent) {
+               /*
+                * Reading a val on behalf of a pointer to a valid allocation is
+                * guaranteed to be a clean read even without synchronization,
+                * because the rtree update became visible in memory before the
+                * pointer came into existence.
+                */
+               return (elm->val);
+       } else {
+               /*
+                * An arbitrary read, e.g. on behalf of ivsalloc(), may not be
+                * dependent on a previous rtree write, which means a stale read
+                * could result if synchronization were omitted here.
+                */
+               return (atomic_read_p(&elm->pun));
+       }
+}
+
+JEMALLOC_INLINE void
+rtree_val_write(rtree_t *rtree, rtree_node_elm_t *elm, const extent_node_t *val)
+{
+
+       atomic_write_p(&elm->pun, val);
+}
+
+JEMALLOC_INLINE rtree_node_elm_t *
+rtree_subtree_tryread(rtree_t *rtree, unsigned level)
+{
+       rtree_node_elm_t *subtree;
+
+       /* Double-checked read (first read may be stale. */
+       subtree = rtree->levels[level].subtree;
+       if (!rtree_node_valid(subtree))
+               subtree = atomic_read_p(&rtree->levels[level].subtree_pun);
+       return (subtree);
+}
+
+JEMALLOC_INLINE rtree_node_elm_t *
+rtree_subtree_read(rtree_t *rtree, unsigned level)
+{
+       rtree_node_elm_t *subtree;
+
+       subtree = rtree_subtree_tryread(rtree, level);
+       if (unlikely(!rtree_node_valid(subtree)))
+               subtree = rtree_subtree_read_hard(rtree, level);
+       return (subtree);
+}
+
+JEMALLOC_INLINE extent_node_t *
+rtree_get(rtree_t *rtree, uintptr_t key, bool dependent)
 {
        uintptr_t subkey;
-       unsigned i, lshift, height, bits;
-       void **node, **child;
-
-       malloc_mutex_lock(&rtree->mutex);
-       for (i = lshift = 0, height = rtree->height, node = rtree->root;
-           i < height - 1;
-           i++, lshift += bits, node = child) {
-               bits = rtree->level2bits[i];
-               subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) -
-                   bits);
-               child = (void**)node[subkey];
-               if (child == NULL) {
-                       size_t size = ((i + 1 < height - 1) ? sizeof(void *)
-                           : (sizeof(uint8_t))) << rtree->level2bits[i+1];
-                       child = (void**)rtree->alloc(size);
-                       if (child == NULL) {
-                               malloc_mutex_unlock(&rtree->mutex);
-                               return (true);
-                       }
-                       memset(child, 0, size);
-                       node[subkey] = child;
+       unsigned i, start_level;
+       rtree_node_elm_t *node, *child;
+
+       start_level = rtree_start_level(rtree, key);
+
+       for (i = start_level, node = rtree_subtree_tryread(rtree, start_level);
+           /**/; i++, node = child) {
+               if (!dependent && unlikely(!rtree_node_valid(node)))
+                       return (NULL);
+               subkey = rtree_subkey(rtree, key, i);
+               if (i == rtree->height - 1) {
+                       /*
+                        * node is a leaf, so it contains values rather than
+                        * child pointers.
+                        */
+                       return (rtree_val_read(rtree, &node[subkey],
+                           dependent));
                }
+               assert(i < rtree->height - 1);
+               child = rtree_child_tryread(&node[subkey]);
        }
+       not_reached();
+}
 
-       /* node is a leaf, so it contains values rather than node pointers. */
-       bits = rtree->level2bits[i];
-       subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) - bits);
-       {
-               uint8_t *leaf = (uint8_t *)node;
-               leaf[subkey] = val;
-       }
-       malloc_mutex_unlock(&rtree->mutex);
+JEMALLOC_INLINE bool
+rtree_set(rtree_t *rtree, uintptr_t key, const extent_node_t *val)
+{
+       uintptr_t subkey;
+       unsigned i, start_level;
+       rtree_node_elm_t *node, *child;
 
-       return (false);
+       start_level = rtree_start_level(rtree, key);
+
+       node = rtree_subtree_read(rtree, start_level);
+       if (node == NULL)
+               return (true);
+       for (i = start_level; /**/; i++, node = child) {
+               subkey = rtree_subkey(rtree, key, i);
+               if (i == rtree->height - 1) {
+                       /*
+                        * node is a leaf, so it contains values rather than
+                        * child pointers.
+                        */
+                       rtree_val_write(rtree, &node[subkey], val);
+                       return (false);
+               }
+               assert(i + 1 < rtree->height);
+               child = rtree_child_read(rtree, &node[subkey], i);
+               if (child == NULL)
+                       return (true);
+       }
+       not_reached();
 }
 #endif
 
index 0cfac72dede2a1ea8b6e261e68f1102d7778f7a8..fc82036d35c8ea6456f5a2056a70cd5cb5f3224d 100755 (executable)
@@ -1,4 +1,6 @@
 #!/bin/sh
+#
+# Usage: size_classes.sh <lg_qarr> <lg_tmin> <lg_parr> <lg_g>
 
 # The following limits are chosen such that they cover all supported platforms.
 
@@ -6,19 +8,19 @@
 lg_zarr="2 3"
 
 # Quanta.
-lg_qarr="3 4"
+lg_qarr=$1
 
 # The range of tiny size classes is [2^lg_tmin..2^(lg_q-1)].
-lg_tmin=3
+lg_tmin=$2
 
 # Maximum lookup size.
 lg_kmax=12
 
 # Page sizes.
-lg_parr="12 13 16"
+lg_parr=`echo $3 | tr ',' ' '`
 
 # Size class group size (number of size classes for each size doubling).
-lg_g=2
+lg_g=$4
 
 pow2() {
   e=$1
@@ -61,7 +63,7 @@ size_class() {
     rem="yes"
   fi
 
-  if [ ${lg_size} -lt ${lg_p} ] ; then
+  if [ ${lg_size} -lt $((${lg_p} + ${lg_g})) ] ; then
     bin="yes"
   else
     bin="no"
@@ -159,7 +161,14 @@ size_classes() {
         nbins=$((${index} + 1))
         # Final written value is correct:
         small_maxclass="((((size_t)1) << ${lg_grp}) + (((size_t)${ndelta}) << ${lg_delta}))"
+        if [ ${lg_g} -gt 0 ] ; then
+          lg_large_minclass=$((${lg_grp} + 1))
+        else
+          lg_large_minclass=$((${lg_grp} + 2))
+        fi
       fi
+      # Final written value is correct:
+      huge_maxclass="((((size_t)1) << ${lg_grp}) + (((size_t)${ndelta}) << ${lg_delta}))"
       index=$((${index} + 1))
       ndelta=$((${ndelta} + 1))
     done
@@ -167,14 +176,18 @@ size_classes() {
     lg_delta=$((${lg_delta} + 1))
   done
   echo
+  nsizes=${index}
 
   # Defined upon completion:
   # - ntbins
   # - nlbins
   # - nbins
+  # - nsizes
   # - lg_tiny_maxclass
   # - lookup_maxclass
   # - small_maxclass
+  # - lg_large_minclass
+  # - huge_maxclass
 }
 
 cat <<EOF
@@ -188,7 +201,8 @@ cat <<EOF
  *
  *   LG_SIZE_CLASS_GROUP: Lg of size class count for each size doubling.
  *   SIZE_CLASSES: Complete table of
- *                 SC(index, lg_delta, size, bin, lg_delta_lookup) tuples.
+ *                 SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup)
+ *                 tuples.
  *     index: Size class index.
  *     lg_grp: Lg group base size (no deltas added).
  *     lg_delta: Lg delta to previous size class.
@@ -199,10 +213,12 @@ cat <<EOF
  *   NTBINS: Number of tiny bins.
  *   NLBINS: Number of bins supported by the lookup table.
  *   NBINS: Number of small size class bins.
+ *   NSIZES: Number of size classes.
  *   LG_TINY_MAXCLASS: Lg of maximum tiny size class.
  *   LOOKUP_MAXCLASS: Maximum size class included in lookup table.
  *   SMALL_MAXCLASS: Maximum small size class.
- *   LARGE_MINCLASS: Minimum large size class.
+ *   LG_LARGE_MINCLASS: Lg of minimum large size class.
+ *   HUGE_MAXCLASS: Maximum (huge) size class.
  */
 
 #define        LG_SIZE_CLASS_GROUP     ${lg_g}
@@ -221,9 +237,12 @@ for lg_z in ${lg_zarr} ; do
         echo "#define  NTBINS                  ${ntbins}"
         echo "#define  NLBINS                  ${nlbins}"
         echo "#define  NBINS                   ${nbins}"
+        echo "#define  NSIZES                  ${nsizes}"
         echo "#define  LG_TINY_MAXCLASS        ${lg_tiny_maxclass}"
         echo "#define  LOOKUP_MAXCLASS         ${lookup_maxclass}"
         echo "#define  SMALL_MAXCLASS          ${small_maxclass}"
+        echo "#define  LG_LARGE_MINCLASS       ${lg_large_minclass}"
+        echo "#define  HUGE_MAXCLASS           ${huge_maxclass}"
         echo "#endif"
         echo
       done
@@ -238,7 +257,7 @@ cat <<EOF
 #endif
 #undef SIZE_CLASSES_DEFINED
 /*
- * The small_size2bin lookup table uses uint8_t to encode each bin index, so we
+ * The size2index_tab lookup table uses uint8_t to encode each bin index, so we
  * cannot support more than 256 small size classes.  Further constrain NBINS to
  * 255 since all small size classes, plus a "not small" size class must be
  * stored in 8 bits of arena_chunk_map_bits_t's bits field.
@@ -247,8 +266,6 @@ cat <<EOF
 #  error "Too many small size classes"
 #endif
 
-#define        LARGE_MINCLASS (PAGE_CEILING(SMALL_MAXCLASS+1))
-
 #endif /* JEMALLOC_H_TYPES */
 /******************************************************************************/
 #ifdef JEMALLOC_H_STRUCTS
index ce96476ad75d558125683118a4aca4cc812c94bd..c91dba99dbe26a85368cdb7a35fb51dceca64644 100644 (file)
@@ -4,6 +4,7 @@
 typedef struct tcache_bin_stats_s tcache_bin_stats_t;
 typedef struct malloc_bin_stats_s malloc_bin_stats_t;
 typedef struct malloc_large_stats_s malloc_large_stats_t;
+typedef struct malloc_huge_stats_s malloc_huge_stats_t;
 typedef struct arena_stats_s arena_stats_t;
 typedef struct chunk_stats_s chunk_stats_t;
 
@@ -20,12 +21,6 @@ struct tcache_bin_stats_s {
 };
 
 struct malloc_bin_stats_s {
-       /*
-        * Current number of bytes allocated, including objects currently
-        * cached by tcache.
-        */
-       size_t          allocated;
-
        /*
         * Total number of allocation/deallocation requests served directly by
         * the bin.  Note that tcache may allocate an object, then recycle it
@@ -42,6 +37,12 @@ struct malloc_bin_stats_s {
         */
        uint64_t        nrequests;
 
+       /*
+        * Current number of regions of this size class, including regions
+        * currently cached by tcache.
+        */
+       size_t          curregs;
+
        /* Number of tcache fills from this bin. */
        uint64_t        nfills;
 
@@ -78,10 +79,25 @@ struct malloc_large_stats_s {
         */
        uint64_t        nrequests;
 
-       /* Current number of runs of this size class. */
+       /*
+        * Current number of runs of this size class, including runs currently
+        * cached by tcache.
+        */
        size_t          curruns;
 };
 
+struct malloc_huge_stats_s {
+       /*
+        * Total number of allocation/deallocation requests served directly by
+        * the arena.
+        */
+       uint64_t        nmalloc;
+       uint64_t        ndalloc;
+
+       /* Current number of (multi-)chunk allocations of this size class. */
+       size_t          curhchunks;
+};
+
 struct arena_stats_s {
        /* Number of bytes currently mapped. */
        size_t          mapped;
@@ -95,6 +111,13 @@ struct arena_stats_s {
        uint64_t        nmadvise;
        uint64_t        purged;
 
+       /*
+        * Number of bytes currently mapped purely for metadata purposes, and
+        * number of bytes currently allocated for internal metadata.
+        */
+       size_t          metadata_mapped;
+       size_t          metadata_allocated; /* Protected via atomic_*_z(). */
+
        /* Per-size-category statistics. */
        size_t          allocated_large;
        uint64_t        nmalloc_large;
@@ -104,30 +127,12 @@ struct arena_stats_s {
        size_t          allocated_huge;
        uint64_t        nmalloc_huge;
        uint64_t        ndalloc_huge;
-       uint64_t        nrequests_huge;
 
-       /*
-        * One element for each possible size class, including sizes that
-        * overlap with bin size classes.  This is necessary because ipalloc()
-        * sometimes has to use such large objects in order to assure proper
-        * alignment.
-        */
+       /* One element for each large size class. */
        malloc_large_stats_t    *lstats;
-};
-
-struct chunk_stats_s {
-       /* Number of chunks that were allocated. */
-       uint64_t        nchunks;
 
-       /* High-water mark for number of chunks allocated. */
-       size_t          highchunks;
-
-       /*
-        * Current number of chunks allocated.  This value isn't maintained for
-        * any other purpose, so keep track of it in order to be able to set
-        * highchunks.
-        */
-       size_t          curchunks;
+       /* One element for each huge size class. */
+       malloc_huge_stats_t     *hstats;
 };
 
 #endif /* JEMALLOC_H_STRUCTS */
index 1b1d8d98b9c0a4fbca1b804937ba05f483ad89f7..5079cd2668884d40101588975d5e707d8e7dd56c 100644 (file)
@@ -4,6 +4,7 @@
 typedef struct tcache_bin_info_s tcache_bin_info_t;
 typedef struct tcache_bin_s tcache_bin_t;
 typedef struct tcache_s tcache_t;
+typedef struct tcaches_s tcaches_t;
 
 /*
  * tcache pointers close to NULL are used to encode state information that is
@@ -15,6 +16,11 @@ typedef struct tcache_s tcache_t;
 #define        TCACHE_STATE_PURGATORY          ((tcache_t *)(uintptr_t)3)
 #define        TCACHE_STATE_MAX                TCACHE_STATE_PURGATORY
 
+/*
+ * Absolute minimum number of cache slots for each small bin.
+ */
+#define        TCACHE_NSLOTS_SMALL_MIN         20
+
 /*
  * Absolute maximum number of cache slots for each small bin in the thread
  * cache.  This is an additional constraint beyond that imposed as: twice the
@@ -69,10 +75,9 @@ struct tcache_bin_s {
 
 struct tcache_s {
        ql_elm(tcache_t) link;          /* Used for aggregating stats. */
-       uint64_t        prof_accumbytes;/* Cleared after arena_prof_accum() */
-       arena_t         *arena;         /* This thread's arena. */
+       uint64_t        prof_accumbytes;/* Cleared after arena_prof_accum(). */
        unsigned        ev_cnt;         /* Event count since incremental GC. */
-       unsigned        next_gc_bin;    /* Next bin to GC. */
+       szind_t         next_gc_bin;    /* Next bin to GC. */
        tcache_bin_t    tbins[1];       /* Dynamically sized. */
        /*
         * The pointer stacks associated with tbins follow as a contiguous
@@ -82,6 +87,14 @@ struct tcache_s {
         */
 };
 
+/* Linkage for list of available (previously used) explicit tcache IDs. */
+struct tcaches_s {
+       union {
+               tcache_t        *tcache;
+               tcaches_t       *next;
+       };
+};
+
 #endif /* JEMALLOC_H_STRUCTS */
 /******************************************************************************/
 #ifdef JEMALLOC_H_EXTERNS
@@ -95,26 +108,41 @@ extern tcache_bin_info_t   *tcache_bin_info;
  * Number of tcache bins.  There are NBINS small-object bins, plus 0 or more
  * large-object bins.
  */
-extern size_t                  nhbins;
+extern size_t  nhbins;
 
 /* Maximum cached size class. */
-extern size_t                  tcache_maxclass;
+extern size_t  tcache_maxclass;
+
+/*
+ * Explicit tcaches, managed via the tcache.{create,flush,destroy} mallctls and
+ * usable via the MALLOCX_TCACHE() flag.  The automatic per thread tcaches are
+ * completely disjoint from this data structure.  tcaches starts off as a sparse
+ * array, so it has no physical memory footprint until individual pages are
+ * touched.  This allows the entire array to be allocated the first time an
+ * explicit tcache is created without a disproportionate impact on memory usage.
+ */
+extern tcaches_t       *tcaches;
 
 size_t tcache_salloc(const void *ptr);
-void   tcache_event_hard(tcache_t *tcache);
-void   *tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin,
-    size_t binind);
-void   tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem,
-    tcache_t *tcache);
-void   tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem,
-    tcache_t *tcache);
+void   tcache_event_hard(tsd_t *tsd, tcache_t *tcache);
+void   *tcache_alloc_small_hard(tsd_t *tsd, arena_t *arena, tcache_t *tcache,
+    tcache_bin_t *tbin, szind_t binind);
+void   tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, tcache_bin_t *tbin,
+    szind_t binind, unsigned rem);
+void   tcache_bin_flush_large(tsd_t *tsd, tcache_bin_t *tbin, szind_t binind,
+    unsigned rem, tcache_t *tcache);
 void   tcache_arena_associate(tcache_t *tcache, arena_t *arena);
-void   tcache_arena_dissociate(tcache_t *tcache);
+void   tcache_arena_reassociate(tcache_t *tcache, arena_t *oldarena,
+    arena_t *newarena);
+void   tcache_arena_dissociate(tcache_t *tcache, arena_t *arena);
 tcache_t *tcache_get_hard(tsd_t *tsd);
-tcache_t *tcache_create(arena_t *arena);
+tcache_t *tcache_create(tsd_t *tsd, arena_t *arena);
 void   tcache_cleanup(tsd_t *tsd);
 void   tcache_enabled_cleanup(tsd_t *tsd);
 void   tcache_stats_merge(tcache_t *tcache, arena_t *arena);
+bool   tcaches_create(tsd_t *tsd, unsigned *r_ind);
+void   tcaches_flush(tsd_t *tsd, unsigned ind);
+void   tcaches_destroy(tsd_t *tsd, unsigned ind);
 bool   tcache_boot(void);
 
 #endif /* JEMALLOC_H_EXTERNS */
@@ -122,16 +150,21 @@ bool      tcache_boot(void);
 #ifdef JEMALLOC_H_INLINES
 
 #ifndef JEMALLOC_ENABLE_INLINE
-void   tcache_event(tcache_t *tcache);
+void   tcache_event(tsd_t *tsd, tcache_t *tcache);
 void   tcache_flush(void);
 bool   tcache_enabled_get(void);
 tcache_t *tcache_get(tsd_t *tsd, bool create);
 void   tcache_enabled_set(bool enabled);
 void   *tcache_alloc_easy(tcache_bin_t *tbin);
-void   *tcache_alloc_small(tcache_t *tcache, size_t size, bool zero);
-void   *tcache_alloc_large(tcache_t *tcache, size_t size, bool zero);
-void   tcache_dalloc_small(tcache_t *tcache, void *ptr, size_t binind);
-void   tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size);
+void   *tcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache,
+    size_t size, bool zero);
+void   *tcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache,
+    size_t size, bool zero);
+void   tcache_dalloc_small(tsd_t *tsd, tcache_t *tcache, void *ptr,
+    szind_t binind);
+void   tcache_dalloc_large(tsd_t *tsd, tcache_t *tcache, void *ptr,
+    size_t size);
+tcache_t       *tcaches_get(tsd_t *tsd, unsigned ind);
 #endif
 
 #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_TCACHE_C_))
@@ -201,7 +234,7 @@ tcache_get(tsd_t *tsd, bool create)
 }
 
 JEMALLOC_ALWAYS_INLINE void
-tcache_event(tcache_t *tcache)
+tcache_event(tsd_t *tsd, tcache_t *tcache)
 {
 
        if (TCACHE_GC_INCR == 0)
@@ -210,7 +243,7 @@ tcache_event(tcache_t *tcache)
        tcache->ev_cnt++;
        assert(tcache->ev_cnt <= TCACHE_GC_INCR);
        if (unlikely(tcache->ev_cnt == TCACHE_GC_INCR))
-               tcache_event_hard(tcache);
+               tcache_event_hard(tsd, tcache);
 }
 
 JEMALLOC_ALWAYS_INLINE void *
@@ -230,58 +263,62 @@ tcache_alloc_easy(tcache_bin_t *tbin)
 }
 
 JEMALLOC_ALWAYS_INLINE void *
-tcache_alloc_small(tcache_t *tcache, size_t size, bool zero)
+tcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size,
+    bool zero)
 {
        void *ret;
-       size_t binind;
+       szind_t binind;
+       size_t usize;
        tcache_bin_t *tbin;
 
-       binind = small_size2bin(size);
+       binind = size2index(size);
        assert(binind < NBINS);
        tbin = &tcache->tbins[binind];
-       size = small_bin2size(binind);
+       usize = index2size(binind);
        ret = tcache_alloc_easy(tbin);
        if (unlikely(ret == NULL)) {
-               ret = tcache_alloc_small_hard(tcache, tbin, binind);
+               ret = tcache_alloc_small_hard(tsd, arena, tcache, tbin, binind);
                if (ret == NULL)
                        return (NULL);
        }
-       assert(tcache_salloc(ret) == size);
+       assert(tcache_salloc(ret) == usize);
 
        if (likely(!zero)) {
                if (config_fill) {
-                       if (unlikely(opt_junk)) {
+                       if (unlikely(opt_junk_alloc)) {
                                arena_alloc_junk_small(ret,
                                    &arena_bin_info[binind], false);
                        } else if (unlikely(opt_zero))
-                               memset(ret, 0, size);
+                               memset(ret, 0, usize);
                }
        } else {
-               if (config_fill && unlikely(opt_junk)) {
+               if (config_fill && unlikely(opt_junk_alloc)) {
                        arena_alloc_junk_small(ret, &arena_bin_info[binind],
                            true);
                }
-               memset(ret, 0, size);
+               memset(ret, 0, usize);
        }
 
        if (config_stats)
                tbin->tstats.nrequests++;
        if (config_prof)
-               tcache->prof_accumbytes += size;
-       tcache_event(tcache);
+               tcache->prof_accumbytes += usize;
+       tcache_event(tsd, tcache);
        return (ret);
 }
 
 JEMALLOC_ALWAYS_INLINE void *
-tcache_alloc_large(tcache_t *tcache, size_t size, bool zero)
+tcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size,
+    bool zero)
 {
        void *ret;
-       size_t binind;
+       szind_t binind;
+       size_t usize;
        tcache_bin_t *tbin;
 
-       size = PAGE_CEILING(size);
-       assert(size <= tcache_maxclass);
-       binind = NBINS + (size >> LG_PAGE) - 1;
+       binind = size2index(size);
+       usize = index2size(binind);
+       assert(usize <= tcache_maxclass);
        assert(binind < nhbins);
        tbin = &tcache->tbins[binind];
        ret = tcache_alloc_easy(tbin);
@@ -290,11 +327,11 @@ tcache_alloc_large(tcache_t *tcache, size_t size, bool zero)
                 * Only allocate one large object at a time, because it's quite
                 * expensive to create one and not use it.
                 */
-               ret = arena_malloc_large(tcache->arena, size, zero);
+               ret = arena_malloc_large(arena, usize, zero);
                if (ret == NULL)
                        return (NULL);
        } else {
-               if (config_prof && size == PAGE) {
+               if (config_prof && usize == LARGE_MINCLASS) {
                        arena_chunk_t *chunk =
                            (arena_chunk_t *)CHUNK_ADDR2BASE(ret);
                        size_t pageind = (((uintptr_t)ret - (uintptr_t)chunk) >>
@@ -304,52 +341,52 @@ tcache_alloc_large(tcache_t *tcache, size_t size, bool zero)
                }
                if (likely(!zero)) {
                        if (config_fill) {
-                               if (unlikely(opt_junk))
-                                       memset(ret, 0xa5, size);
+                               if (unlikely(opt_junk_alloc))
+                                       memset(ret, 0xa5, usize);
                                else if (unlikely(opt_zero))
-                                       memset(ret, 0, size);
+                                       memset(ret, 0, usize);
                        }
                } else
-                       memset(ret, 0, size);
+                       memset(ret, 0, usize);
 
                if (config_stats)
                        tbin->tstats.nrequests++;
                if (config_prof)
-                       tcache->prof_accumbytes += size;
+                       tcache->prof_accumbytes += usize;
        }
 
-       tcache_event(tcache);
+       tcache_event(tsd, tcache);
        return (ret);
 }
 
 JEMALLOC_ALWAYS_INLINE void
-tcache_dalloc_small(tcache_t *tcache, void *ptr, size_t binind)
+tcache_dalloc_small(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind)
 {
        tcache_bin_t *tbin;
        tcache_bin_info_t *tbin_info;
 
        assert(tcache_salloc(ptr) <= SMALL_MAXCLASS);
 
-       if (config_fill && unlikely(opt_junk))
+       if (config_fill && unlikely(opt_junk_free))
                arena_dalloc_junk_small(ptr, &arena_bin_info[binind]);
 
        tbin = &tcache->tbins[binind];
        tbin_info = &tcache_bin_info[binind];
        if (unlikely(tbin->ncached == tbin_info->ncached_max)) {
-               tcache_bin_flush_small(tbin, binind, (tbin_info->ncached_max >>
-                   1), tcache);
+               tcache_bin_flush_small(tsd, tcache, tbin, binind,
+                   (tbin_info->ncached_max >> 1));
        }
        assert(tbin->ncached < tbin_info->ncached_max);
        tbin->avail[tbin->ncached] = ptr;
        tbin->ncached++;
 
-       tcache_event(tcache);
+       tcache_event(tsd, tcache);
 }
 
 JEMALLOC_ALWAYS_INLINE void
-tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size)
+tcache_dalloc_large(tsd_t *tsd, tcache_t *tcache, void *ptr, size_t size)
 {
-       size_t binind;
+       szind_t binind;
        tcache_bin_t *tbin;
        tcache_bin_info_t *tbin_info;
 
@@ -357,22 +394,31 @@ tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size)
        assert(tcache_salloc(ptr) > SMALL_MAXCLASS);
        assert(tcache_salloc(ptr) <= tcache_maxclass);
 
-       binind = NBINS + (size >> LG_PAGE) - 1;
+       binind = size2index(size);
 
-       if (config_fill && unlikely(opt_junk))
-               memset(ptr, 0x5a, size);
+       if (config_fill && unlikely(opt_junk_free))
+               arena_dalloc_junk_large(ptr, size);
 
        tbin = &tcache->tbins[binind];
        tbin_info = &tcache_bin_info[binind];
        if (unlikely(tbin->ncached == tbin_info->ncached_max)) {
-               tcache_bin_flush_large(tbin, binind, (tbin_info->ncached_max >>
-                   1), tcache);
+               tcache_bin_flush_large(tsd, tbin, binind,
+                   (tbin_info->ncached_max >> 1), tcache);
        }
        assert(tbin->ncached < tbin_info->ncached_max);
        tbin->avail[tbin->ncached] = ptr;
        tbin->ncached++;
 
-       tcache_event(tcache);
+       tcache_event(tsd, tcache);
+}
+
+JEMALLOC_ALWAYS_INLINE tcache_t *
+tcaches_get(tsd_t *tsd, unsigned ind)
+{
+       tcaches_t *elm = &tcaches[ind];
+       if (unlikely(elm->tcache == NULL))
+               elm->tcache = tcache_create(tsd, arena_choose(tsd, NULL));
+       return (elm->tcache);
 }
 #endif
 
index 25450391cecc7a2c84f6076a86723c282cc3d98d..eed7aa013470d2682a69cfd1b190216674b0b284 100644 (file)
@@ -2,7 +2,7 @@
 #ifdef JEMALLOC_H_TYPES
 
 /* Maximum number of malloc_tsd users with cleanup functions. */
-#define        MALLOC_TSD_CLEANUPS_MAX 8
+#define        MALLOC_TSD_CLEANUPS_MAX 2
 
 typedef bool (*malloc_tsd_cleanup_t)(void);
 
@@ -23,7 +23,7 @@ typedef enum {
 
 /*
  * TLS/TSD-agnostic macro-based implementation of thread-specific data.  There
- * are four macros that support (at least) three use cases: file-private,
+ * are five macros that support (at least) three use cases: file-private,
  * library-private, and library-private inlined.  Following is an example
  * library-private tsd variable:
  *
@@ -33,21 +33,22 @@ typedef enum {
  *           int y;
  *   } example_t;
  *   #define EX_INITIALIZER JEMALLOC_CONCAT({0, 0})
- *   malloc_tsd_protos(, example_, example_t *)
- *   malloc_tsd_externs(example_, example_t *)
+ *   malloc_tsd_types(example_, example_t)
+ *   malloc_tsd_protos(, example_, example_t)
+ *   malloc_tsd_externs(example_, example_t)
  * In example.c:
- *   malloc_tsd_data(, example_, example_t *, EX_INITIALIZER)
- *   malloc_tsd_funcs(, example_, example_t *, EX_INITIALIZER,
+ *   malloc_tsd_data(, example_, example_t, EX_INITIALIZER)
+ *   malloc_tsd_funcs(, example_, example_t, EX_INITIALIZER,
  *       example_tsd_cleanup)
  *
  * The result is a set of generated functions, e.g.:
  *
  *   bool example_tsd_boot(void) {...}
- *   example_t **example_tsd_get() {...}
- *   void example_tsd_set(example_t **val) {...}
+ *   example_t *example_tsd_get() {...}
+ *   void example_tsd_set(example_t *val) {...}
  *
  * Note that all of the functions deal in terms of (a_type *) rather than
- * (a_type)  so that it is possible to support non-pointer types (unlike
+ * (a_type) so that it is possible to support non-pointer types (unlike
  * pthreads TSD).  example_tsd_cleanup() is passed an (a_type *) pointer that is
  * cast to (void *).  This means that the cleanup function needs to cast the
  * function argument to (a_type *), then dereference the resulting pointer to
@@ -70,9 +71,32 @@ typedef enum {
  * non-NULL.
  */
 
+/* malloc_tsd_types(). */
+#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
+#define        malloc_tsd_types(a_name, a_type)
+#elif (defined(JEMALLOC_TLS))
+#define        malloc_tsd_types(a_name, a_type)
+#elif (defined(_WIN32))
+#define        malloc_tsd_types(a_name, a_type)                                \
+typedef struct {                                                       \
+       bool    initialized;                                            \
+       a_type  val;                                                    \
+} a_name##tsd_wrapper_t;
+#else
+#define        malloc_tsd_types(a_name, a_type)                                \
+typedef struct {                                                       \
+       bool    initialized;                                            \
+       a_type  val;                                                    \
+} a_name##tsd_wrapper_t;
+#endif
+
 /* malloc_tsd_protos(). */
 #define        malloc_tsd_protos(a_attr, a_name, a_type)                       \
 a_attr bool                                                            \
+a_name##tsd_boot0(void);                                               \
+a_attr void                                                            \
+a_name##tsd_boot1(void);                                               \
+a_attr bool                                                            \
 a_name##tsd_boot(void);                                                        \
 a_attr a_type *                                                                \
 a_name##tsd_get(void);                                                 \
@@ -93,11 +117,13 @@ extern bool                a_name##tsd_booted;
 #elif (defined(_WIN32))
 #define        malloc_tsd_externs(a_name, a_type)                              \
 extern DWORD           a_name##tsd_tsd;                                \
+extern a_name##tsd_wrapper_t   a_name##tsd_boot_wrapper;               \
 extern bool            a_name##tsd_booted;
 #else
 #define        malloc_tsd_externs(a_name, a_type)                              \
 extern pthread_key_t   a_name##tsd_tsd;                                \
 extern tsd_init_head_t a_name##tsd_init_head;                          \
+extern a_name##tsd_wrapper_t   a_name##tsd_boot_wrapper;               \
 extern bool            a_name##tsd_booted;
 #endif
 
@@ -118,6 +144,10 @@ a_attr bool                a_name##tsd_booted = false;
 #elif (defined(_WIN32))
 #define        malloc_tsd_data(a_attr, a_name, a_type, a_initializer)          \
 a_attr DWORD           a_name##tsd_tsd;                                \
+a_attr a_name##tsd_wrapper_t a_name##tsd_boot_wrapper = {              \
+       false,                                                          \
+       a_initializer                                                   \
+};                                                                     \
 a_attr bool            a_name##tsd_booted = false;
 #else
 #define        malloc_tsd_data(a_attr, a_name, a_type, a_initializer)          \
@@ -126,6 +156,10 @@ a_attr tsd_init_head_t     a_name##tsd_init_head = {                       \
        ql_head_initializer(blocks),                                    \
        MALLOC_MUTEX_INITIALIZER                                        \
 };                                                                     \
+a_attr a_name##tsd_wrapper_t a_name##tsd_boot_wrapper = {              \
+       false,                                                          \
+       a_initializer                                                   \
+};                                                                     \
 a_attr bool            a_name##tsd_booted = false;
 #endif
 
@@ -145,7 +179,7 @@ a_name##tsd_cleanup_wrapper(void)                                   \
        return (a_name##tsd_initialized);                               \
 }                                                                      \
 a_attr bool                                                            \
-a_name##tsd_boot(void)                                                 \
+a_name##tsd_boot0(void)                                                        \
 {                                                                      \
                                                                        \
        if (a_cleanup != malloc_tsd_no_cleanup) {                       \
@@ -155,6 +189,18 @@ a_name##tsd_boot(void)                                                     \
        a_name##tsd_booted = true;                                      \
        return (false);                                                 \
 }                                                                      \
+a_attr void                                                            \
+a_name##tsd_boot1(void)                                                        \
+{                                                                      \
+                                                                       \
+       /* Do nothing. */                                               \
+}                                                                      \
+a_attr bool                                                            \
+a_name##tsd_boot(void)                                                 \
+{                                                                      \
+                                                                       \
+       return (a_name##tsd_boot0());                                   \
+}                                                                      \
 /* Get/set. */                                                         \
 a_attr a_type *                                                                \
 a_name##tsd_get(void)                                                  \
@@ -177,7 +223,7 @@ a_name##tsd_set(a_type *val)                                                \
     a_cleanup)                                                         \
 /* Initialization/cleanup. */                                          \
 a_attr bool                                                            \
-a_name##tsd_boot(void)                                                 \
+a_name##tsd_boot0(void)                                                        \
 {                                                                      \
                                                                        \
        if (a_cleanup != malloc_tsd_no_cleanup) {                       \
@@ -188,6 +234,18 @@ a_name##tsd_boot(void)                                                     \
        a_name##tsd_booted = true;                                      \
        return (false);                                                 \
 }                                                                      \
+a_attr void                                                            \
+a_name##tsd_boot1(void)                                                        \
+{                                                                      \
+                                                                       \
+       /* Do nothing. */                                               \
+}                                                                      \
+a_attr bool                                                            \
+a_name##tsd_boot(void)                                                 \
+{                                                                      \
+                                                                       \
+       return (a_name##tsd_boot0());                                   \
+}                                                                      \
 /* Get/set. */                                                         \
 a_attr a_type *                                                                \
 a_name##tsd_get(void)                                                  \
@@ -215,18 +273,15 @@ a_name##tsd_set(a_type *val)                                              \
 #elif (defined(_WIN32))
 #define        malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,         \
     a_cleanup)                                                         \
-/* Data structure. */                                                  \
-typedef struct {                                                       \
-       bool    initialized;                                            \
-       a_type  val;                                                    \
-} a_name##tsd_wrapper_t;                                               \
 /* Initialization/cleanup. */                                          \
 a_attr bool                                                            \
 a_name##tsd_cleanup_wrapper(void)                                      \
 {                                                                      \
-       a_name##tsd_wrapper_t *wrapper;                                 \
+       DWORD error = GetLastError();                                   \
+       a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *)      \
+           TlsGetValue(a_name##tsd_tsd);                               \
+       SetLastError(error);                                            \
                                                                        \
-       wrapper = (a_name##tsd_wrapper_t *)TlsGetValue(a_name##tsd_tsd);\
        if (wrapper == NULL)                                            \
                return (false);                                         \
        if (a_cleanup != malloc_tsd_no_cleanup &&                       \
@@ -241,26 +296,23 @@ a_name##tsd_cleanup_wrapper(void)                                 \
        malloc_tsd_dalloc(wrapper);                                     \
        return (false);                                                 \
 }                                                                      \
-a_attr bool                                                            \
-a_name##tsd_boot(void)                                                 \
+a_attr void                                                            \
+a_name##tsd_wrapper_set(a_name##tsd_wrapper_t *wrapper)                        \
 {                                                                      \
                                                                        \
-       a_name##tsd_tsd = TlsAlloc();                                   \
-       if (a_name##tsd_tsd == TLS_OUT_OF_INDEXES)                      \
-               return (true);                                          \
-       if (a_cleanup != malloc_tsd_no_cleanup) {                       \
-               malloc_tsd_cleanup_register(                            \
-                   &a_name##tsd_cleanup_wrapper);                      \
+       if (!TlsSetValue(a_name##tsd_tsd, (void *)wrapper)) {           \
+               malloc_write("<jemalloc>: Error setting"                \
+                   " TSD for "#a_name"\n");                            \
+               abort();                                                \
        }                                                               \
-       a_name##tsd_booted = true;                                      \
-       return (false);                                                 \
 }                                                                      \
-/* Get/set. */                                                         \
 a_attr a_name##tsd_wrapper_t *                                         \
-a_name##tsd_get_wrapper(void)                                          \
+a_name##tsd_wrapper_get(void)                                          \
 {                                                                      \
+       DWORD error = GetLastError();                                   \
        a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *)      \
            TlsGetValue(a_name##tsd_tsd);                               \
+       SetLastError(error);                                            \
                                                                        \
        if (unlikely(wrapper == NULL)) {                                \
                wrapper = (a_name##tsd_wrapper_t *)                     \
@@ -273,21 +325,57 @@ a_name##tsd_get_wrapper(void)                                             \
                        wrapper->initialized = false;                   \
                        wrapper->val = a_initializer;                   \
                }                                                       \
-               if (!TlsSetValue(a_name##tsd_tsd, (void *)wrapper)) {   \
-                       malloc_write("<jemalloc>: Error setting"        \
-                           " TSD for "#a_name"\n");                    \
-                       abort();                                        \
-               }                                                       \
+               a_name##tsd_wrapper_set(wrapper);                       \
        }                                                               \
        return (wrapper);                                               \
 }                                                                      \
+a_attr bool                                                            \
+a_name##tsd_boot0(void)                                                        \
+{                                                                      \
+                                                                       \
+       a_name##tsd_tsd = TlsAlloc();                                   \
+       if (a_name##tsd_tsd == TLS_OUT_OF_INDEXES)                      \
+               return (true);                                          \
+       if (a_cleanup != malloc_tsd_no_cleanup) {                       \
+               malloc_tsd_cleanup_register(                            \
+                   &a_name##tsd_cleanup_wrapper);                      \
+       }                                                               \
+       a_name##tsd_wrapper_set(&a_name##tsd_boot_wrapper);             \
+       a_name##tsd_booted = true;                                      \
+       return (false);                                                 \
+}                                                                      \
+a_attr void                                                            \
+a_name##tsd_boot1(void)                                                        \
+{                                                                      \
+       a_name##tsd_wrapper_t *wrapper;                                 \
+       wrapper = (a_name##tsd_wrapper_t *)                             \
+           malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t));           \
+       if (wrapper == NULL) {                                          \
+               malloc_write("<jemalloc>: Error allocating"             \
+                   " TSD for "#a_name"\n");                            \
+               abort();                                                \
+       }                                                               \
+       memcpy(wrapper, &a_name##tsd_boot_wrapper,                      \
+           sizeof(a_name##tsd_wrapper_t));                             \
+       a_name##tsd_wrapper_set(wrapper);                               \
+}                                                                      \
+a_attr bool                                                            \
+a_name##tsd_boot(void)                                                 \
+{                                                                      \
+                                                                       \
+       if (a_name##tsd_boot0())                                        \
+               return (true);                                          \
+       a_name##tsd_boot1();                                            \
+       return (false);                                                 \
+}                                                                      \
+/* Get/set. */                                                         \
 a_attr a_type *                                                                \
 a_name##tsd_get(void)                                                  \
 {                                                                      \
        a_name##tsd_wrapper_t *wrapper;                                 \
                                                                        \
        assert(a_name##tsd_booted);                                     \
-       wrapper = a_name##tsd_get_wrapper();                            \
+       wrapper = a_name##tsd_wrapper_get();                            \
        return (&wrapper->val);                                         \
 }                                                                      \
 a_attr void                                                            \
@@ -296,7 +384,7 @@ a_name##tsd_set(a_type *val)                                                \
        a_name##tsd_wrapper_t *wrapper;                                 \
                                                                        \
        assert(a_name##tsd_booted);                                     \
-       wrapper = a_name##tsd_get_wrapper();                            \
+       wrapper = a_name##tsd_wrapper_get();                            \
        wrapper->val = *(val);                                          \
        if (a_cleanup != malloc_tsd_no_cleanup)                         \
                wrapper->initialized = true;                            \
@@ -304,11 +392,6 @@ a_name##tsd_set(a_type *val)                                               \
 #else
 #define        malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer,         \
     a_cleanup)                                                         \
-/* Data structure. */                                                  \
-typedef struct {                                                       \
-       bool    initialized;                                            \
-       a_type  val;                                                    \
-} a_name##tsd_wrapper_t;                                               \
 /* Initialization/cleanup. */                                          \
 a_attr void                                                            \
 a_name##tsd_cleanup_wrapper(void *arg)                                 \
@@ -333,19 +416,19 @@ a_name##tsd_cleanup_wrapper(void *arg)                                    \
        }                                                               \
        malloc_tsd_dalloc(wrapper);                                     \
 }                                                                      \
-a_attr bool                                                            \
-a_name##tsd_boot(void)                                                 \
+a_attr void                                                            \
+a_name##tsd_wrapper_set(a_name##tsd_wrapper_t *wrapper)                        \
 {                                                                      \
                                                                        \
-       if (pthread_key_create(&a_name##tsd_tsd,                        \
-           a_name##tsd_cleanup_wrapper) != 0)                          \
-               return (true);                                          \
-       a_name##tsd_booted = true;                                      \
-       return (false);                                                 \
+       if (pthread_setspecific(a_name##tsd_tsd,                        \
+           (void *)wrapper)) {                                         \
+               malloc_write("<jemalloc>: Error setting"                \
+                   " TSD for "#a_name"\n");                            \
+               abort();                                                \
+       }                                                               \
 }                                                                      \
-/* Get/set. */                                                         \
 a_attr a_name##tsd_wrapper_t *                                         \
-a_name##tsd_get_wrapper(void)                                          \
+a_name##tsd_wrapper_get(void)                                          \
 {                                                                      \
        a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *)      \
            pthread_getspecific(a_name##tsd_tsd);                       \
@@ -367,23 +450,54 @@ a_name##tsd_get_wrapper(void)                                             \
                        wrapper->initialized = false;                   \
                        wrapper->val = a_initializer;                   \
                }                                                       \
-               if (pthread_setspecific(a_name##tsd_tsd,                \
-                   (void *)wrapper)) {                                 \
-                       malloc_write("<jemalloc>: Error setting"        \
-                           " TSD for "#a_name"\n");                    \
-                       abort();                                        \
-               }                                                       \
+               a_name##tsd_wrapper_set(wrapper);                       \
                tsd_init_finish(&a_name##tsd_init_head, &block);        \
        }                                                               \
        return (wrapper);                                               \
 }                                                                      \
+a_attr bool                                                            \
+a_name##tsd_boot0(void)                                                        \
+{                                                                      \
+                                                                       \
+       if (pthread_key_create(&a_name##tsd_tsd,                        \
+           a_name##tsd_cleanup_wrapper) != 0)                          \
+               return (true);                                          \
+       a_name##tsd_wrapper_set(&a_name##tsd_boot_wrapper);             \
+       a_name##tsd_booted = true;                                      \
+       return (false);                                                 \
+}                                                                      \
+a_attr void                                                            \
+a_name##tsd_boot1(void)                                                        \
+{                                                                      \
+       a_name##tsd_wrapper_t *wrapper;                                 \
+       wrapper = (a_name##tsd_wrapper_t *)                             \
+           malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t));           \
+       if (wrapper == NULL) {                                          \
+               malloc_write("<jemalloc>: Error allocating"             \
+                   " TSD for "#a_name"\n");                            \
+               abort();                                                \
+       }                                                               \
+       memcpy(wrapper, &a_name##tsd_boot_wrapper,                      \
+           sizeof(a_name##tsd_wrapper_t));                             \
+       a_name##tsd_wrapper_set(wrapper);                               \
+}                                                                      \
+a_attr bool                                                            \
+a_name##tsd_boot(void)                                                 \
+{                                                                      \
+                                                                       \
+       if (a_name##tsd_boot0())                                        \
+               return (true);                                          \
+       a_name##tsd_boot1();                                            \
+       return (false);                                                 \
+}                                                                      \
+/* Get/set. */                                                         \
 a_attr a_type *                                                                \
 a_name##tsd_get(void)                                                  \
 {                                                                      \
        a_name##tsd_wrapper_t *wrapper;                                 \
                                                                        \
        assert(a_name##tsd_booted);                                     \
-       wrapper = a_name##tsd_get_wrapper();                            \
+       wrapper = a_name##tsd_wrapper_get();                            \
        return (&wrapper->val);                                         \
 }                                                                      \
 a_attr void                                                            \
@@ -392,7 +506,7 @@ a_name##tsd_set(a_type *val)                                                \
        a_name##tsd_wrapper_t *wrapper;                                 \
                                                                        \
        assert(a_name##tsd_booted);                                     \
-       wrapper = a_name##tsd_get_wrapper();                            \
+       wrapper = a_name##tsd_wrapper_get();                            \
        wrapper->val = *(val);                                          \
        if (a_cleanup != malloc_tsd_no_cleanup)                         \
                wrapper->initialized = true;                            \
@@ -423,6 +537,9 @@ struct tsd_init_head_s {
     O(thread_deallocated,      uint64_t)                               \
     O(prof_tdata,              prof_tdata_t *)                         \
     O(arena,                   arena_t *)                              \
+    O(arenas_cache,            arena_t **)                             \
+    O(narenas_cache,           unsigned)                               \
+    O(arenas_cache_bypass,     bool)                                   \
     O(tcache_enabled,          tcache_enabled_t)                       \
     O(quarantine,              quarantine_t *)                         \
 
@@ -433,6 +550,9 @@ struct tsd_init_head_s {
     0,                                                                 \
     NULL,                                                              \
     NULL,                                                              \
+    NULL,                                                              \
+    0,                                                                 \
+    false,                                                             \
     tcache_enabled_default,                                            \
     NULL                                                               \
 }
@@ -447,6 +567,8 @@ MALLOC_TSD
 
 static const tsd_t tsd_initializer = TSD_INITIALIZER;
 
+malloc_tsd_types(, tsd_t)
+
 #endif /* JEMALLOC_H_STRUCTS */
 /******************************************************************************/
 #ifdef JEMALLOC_H_EXTERNS
@@ -455,7 +577,8 @@ void        *malloc_tsd_malloc(size_t size);
 void   malloc_tsd_dalloc(void *wrapper);
 void   malloc_tsd_no_cleanup(void *arg);
 void   malloc_tsd_cleanup_register(bool (*f)(void));
-bool   malloc_tsd_boot(void);
+bool   malloc_tsd_boot0(void);
+void   malloc_tsd_boot1(void);
 #if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \
     !defined(_WIN32))
 void   *tsd_init_check_recursion(tsd_init_head_t *head,
index 5af6832949757ebae3aea2fdc5968d9622946bbb..b2ea740fdc7628d1de2302fd581b403bed7257f8 100644 (file)
@@ -1,6 +1,36 @@
 /******************************************************************************/
 #ifdef JEMALLOC_H_TYPES
 
+#ifdef _WIN32
+#  ifdef _WIN64
+#    define FMT64_PREFIX "ll"
+#    define FMTPTR_PREFIX "ll"
+#  else
+#    define FMT64_PREFIX "ll"
+#    define FMTPTR_PREFIX ""
+#  endif
+#  define FMTd32 "d"
+#  define FMTu32 "u"
+#  define FMTx32 "x"
+#  define FMTd64 FMT64_PREFIX "d"
+#  define FMTu64 FMT64_PREFIX "u"
+#  define FMTx64 FMT64_PREFIX "x"
+#  define FMTdPTR FMTPTR_PREFIX "d"
+#  define FMTuPTR FMTPTR_PREFIX "u"
+#  define FMTxPTR FMTPTR_PREFIX "x"
+#else
+#  include <inttypes.h>
+#  define FMTd32 PRId32
+#  define FMTu32 PRIu32
+#  define FMTx32 PRIx32
+#  define FMTd64 PRId64
+#  define FMTu64 PRIu64
+#  define FMTx64 PRIx64
+#  define FMTdPTR PRIdPTR
+#  define FMTuPTR PRIuPTR
+#  define FMTxPTR PRIxPTR
+#endif
+
 /* Size of stack-allocated buffer passed to buferror(). */
 #define        BUFERROR_BUF            64
 
  * uninitialized.
  */
 #ifdef JEMALLOC_CC_SILENCE
-#  define JEMALLOC_CC_SILENCE_INIT(v) = v
+#      define JEMALLOC_CC_SILENCE_INIT(v) = v
 #else
-#  define JEMALLOC_CC_SILENCE_INIT(v)
+#      define JEMALLOC_CC_SILENCE_INIT(v)
+#endif
+
+#define        JEMALLOC_GNUC_PREREQ(major, minor)                              \
+    (!defined(__clang__) &&                                            \
+    (__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))))
+#ifndef __has_builtin
+#  define __has_builtin(builtin) (0)
 #endif
+#define        JEMALLOC_CLANG_HAS_BUILTIN(builtin)                             \
+    (defined(__clang__) && __has_builtin(builtin))
 
 #ifdef __GNUC__
-#define likely(x) __builtin_expect(!!(x), 1)
-#define unlikely(x) __builtin_expect(!!(x), 0)
+#      define likely(x)   __builtin_expect(!!(x), 1)
+#      define unlikely(x) __builtin_expect(!!(x), 0)
+#  if JEMALLOC_GNUC_PREREQ(4, 6) ||                                    \
+      JEMALLOC_CLANG_HAS_BUILTIN(__builtin_unreachable)
+#      define unreachable() __builtin_unreachable()
+#  else
+#      define unreachable()
+#  endif
 #else
-#define likely(x) !!(x)
-#define unlikely(x) !!(x)
+#      define likely(x)   !!(x)
+#      define unlikely(x) !!(x)
+#      define unreachable()
 #endif
 
 /*
                    __FILE__, __LINE__);                                \
                abort();                                                \
        }                                                               \
+       unreachable();                                                  \
 } while (0)
 #endif
 
@@ -104,13 +151,12 @@ void      malloc_write(const char *s);
 int    malloc_vsnprintf(char *str, size_t size, const char *format,
     va_list ap);
 int    malloc_snprintf(char *str, size_t size, const char *format, ...)
-    JEMALLOC_ATTR(format(printf, 3, 4));
+    JEMALLOC_FORMAT_PRINTF(3, 4);
 void   malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
     const char *format, va_list ap);
 void malloc_cprintf(void (*write)(void *, const char *), void *cbopaque,
-    const char *format, ...) JEMALLOC_ATTR(format(printf, 3, 4));
-void   malloc_printf(const char *format, ...)
-    JEMALLOC_ATTR(format(printf, 1, 2));
+    const char *format, ...) JEMALLOC_FORMAT_PRINTF(3, 4);
+void   malloc_printf(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);
 
 #endif /* JEMALLOC_H_EXTERNS */
 /******************************************************************************/
@@ -127,7 +173,7 @@ int get_errno(void);
 
 #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_UTIL_C_))
 
-/* Sanity check: */
+/* Sanity check. */
 #if !defined(JEMALLOC_INTERNAL_FFSL) || !defined(JEMALLOC_INTERNAL_FFS)
 #  error Both JEMALLOC_INTERNAL_FFSL && JEMALLOC_INTERNAL_FFS should have been defined by configure
 #endif
@@ -136,14 +182,14 @@ JEMALLOC_ALWAYS_INLINE int
 jemalloc_ffsl(long bitmap)
 {
 
-        return (JEMALLOC_INTERNAL_FFSL(bitmap));
+       return (JEMALLOC_INTERNAL_FFSL(bitmap));
 }
 
 JEMALLOC_ALWAYS_INLINE int
 jemalloc_ffs(int bitmap)
 {
 
-        return (JEMALLOC_INTERNAL_FFS(bitmap));
+       return (JEMALLOC_INTERNAL_FFS(bitmap));
 }
 
 /* Compute the smallest power of 2 that is >= x. */
@@ -170,6 +216,8 @@ lg_floor(size_t x)
 {
        size_t ret;
 
+       assert(x != 0);
+
        asm ("bsr %1, %0"
            : "=r"(ret) // Outputs.
            : "r"(x)    // Inputs.
@@ -180,22 +228,26 @@ lg_floor(size_t x)
 JEMALLOC_INLINE size_t
 lg_floor(size_t x)
 {
-    unsigned long ret;
+       unsigned long ret;
+
+       assert(x != 0);
 
 #if (LG_SIZEOF_PTR == 3)
-    _BitScanReverse64(&ret, x);
+       _BitScanReverse64(&ret, x);
 #elif (LG_SIZEOF_PTR == 2)
-    _BitScanReverse(&ret, x);
+       _BitScanReverse(&ret, x);
 #else
 #  error "Unsupported type sizes for lg_floor()"
 #endif
-    return (ret);
+       return (ret);
 }
 #elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ))
 JEMALLOC_INLINE size_t
 lg_floor(size_t x)
 {
 
+       assert(x != 0);
+
 #if (LG_SIZEOF_PTR == LG_SIZEOF_INT)
        return (((8 << LG_SIZEOF_PTR) - 1) - __builtin_clz(x));
 #elif (LG_SIZEOF_PTR == LG_SIZEOF_LONG)
@@ -209,6 +261,8 @@ JEMALLOC_INLINE size_t
 lg_floor(size_t x)
 {
 
+       assert(x != 0);
+
        x |= (x >> 1);
        x |= (x >> 2);
        x |= (x >> 4);
@@ -231,7 +285,7 @@ lg_floor(size_t x)
 }
 #endif
 
-/* Sets error code */
+/* Set error code. */
 JEMALLOC_INLINE void
 set_errno(int errnum)
 {
@@ -243,7 +297,7 @@ set_errno(int errnum)
 #endif
 }
 
-/* Get last error code */
+/* Get last error code. */
 JEMALLOC_INLINE int
 get_errno(void)
 {
index 7e1c8be18f76f3e5ab08517c568653a06db5a295..c085814f204046d532b8162ead4ef536f747ed4e 100755 (executable)
@@ -22,7 +22,7 @@ done
 
 cat <<EOF
 #ifdef __cplusplus
-};
+}
 #endif
 #endif /* JEMALLOC_H_ */
 EOF
index ce6c6987c5b07d4fbea4b3f62b4d8c5e31d78cf0..ab13c3758cab42dc861f9a6387a41215ede58e06 100644 (file)
@@ -1,6 +1,15 @@
 /* Defined if __attribute__((...)) syntax is supported. */
 #undef JEMALLOC_HAVE_ATTR
 
+/* Defined if alloc_size attribute is supported. */
+#undef JEMALLOC_HAVE_ATTR_ALLOC_SIZE
+
+/* Defined if format(gnu_printf, ...) attribute is supported. */
+#undef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF
+
+/* Defined if format(printf, ...) attribute is supported. */
+#undef JEMALLOC_HAVE_ATTR_FORMAT_PRINTF
+
 /*
  * Define overrides for non-standard allocator-related functions if they are
  * present on the system.
  */
 #undef JEMALLOC_USABLE_SIZE_CONST
 
+/*
+ * If defined, specify throw() for the public function prototypes when compiling
+ * with C++.  The only justification for this is to match the prototypes that
+ * glibc defines.
+ */
+#undef JEMALLOC_USE_CXX_THROW
+
 /* sizeof(void *) == 2^LG_SIZEOF_PTR. */
 #undef LG_SIZEOF_PTR
index 99f12611d6bfad64815379d4d186feb41eb60461..7f64d9ff9a37bd8f31c1d4ab5cf45894a03e50e4 100644 (file)
         ((a < (size_t)INT_MAX) ? ffs(a)-1 : ffs(a>>32)+31)
 #  endif
 #  define MALLOCX_ZERO ((int)0x40)
-/* Bias arena index bits so that 0 encodes "MALLOCX_ARENA() unspecified". */
-#  define MALLOCX_ARENA(a)     ((int)(((a)+1) << 8))
+/*
+ * Bias tcache index bits so that 0 encodes "automatic tcache management", and 1
+ * encodes MALLOCX_TCACHE_NONE.
+ */
+#  define MALLOCX_TCACHE(tc)   ((int)(((tc)+2) << 8))
+#  define MALLOCX_TCACHE_NONE  MALLOCX_TCACHE(-1)
+/*
+ * Bias arena index bits so that 0 encodes "use an automatically chosen arena".
+ */
+#  define MALLOCX_ARENA(a)     ((int)(((a)+1) << 20))
+
+#if defined(__cplusplus) && defined(JEMALLOC_USE_CXX_THROW)
+#  define JEMALLOC_CXX_THROW throw()
+#else
+#  define JEMALLOC_CXX_THROW
+#endif
 
 #ifdef JEMALLOC_HAVE_ATTR
 #  define JEMALLOC_ATTR(s) __attribute__((s))
-#  define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility("default"))
 #  define JEMALLOC_ALIGNED(s) JEMALLOC_ATTR(aligned(s))
-#  define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s))
+#  ifdef JEMALLOC_HAVE_ATTR_ALLOC_SIZE
+#    define JEMALLOC_ALLOC_SIZE(s) JEMALLOC_ATTR(alloc_size(s))
+#    define JEMALLOC_ALLOC_SIZE2(s1, s2) JEMALLOC_ATTR(alloc_size(s1, s2))
+#  else
+#    define JEMALLOC_ALLOC_SIZE(s)
+#    define JEMALLOC_ALLOC_SIZE2(s1, s2)
+#  endif
+#  ifndef JEMALLOC_EXPORT
+#    define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility("default"))
+#  endif
+#  ifdef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF
+#    define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(gnu_printf, s, i))
+#  elif defined(JEMALLOC_HAVE_ATTR_FORMAT_PRINTF)
+#    define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(printf, s, i))
+#  else
+#    define JEMALLOC_FORMAT_PRINTF(s, i)
+#  endif
 #  define JEMALLOC_NOINLINE JEMALLOC_ATTR(noinline)
+#  define JEMALLOC_NOTHROW JEMALLOC_ATTR(nothrow)
+#  define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s))
+#  define JEMALLOC_RESTRICT_RETURN
+#  define JEMALLOC_ALLOCATOR
 #elif _MSC_VER
 #  define JEMALLOC_ATTR(s)
-#  ifdef DLLEXPORT
-#    define JEMALLOC_EXPORT __declspec(dllexport)
+#  define JEMALLOC_ALIGNED(s) __declspec(align(s))
+#  define JEMALLOC_ALLOC_SIZE(s)
+#  define JEMALLOC_ALLOC_SIZE2(s1, s2)
+#  ifndef JEMALLOC_EXPORT
+#    ifdef DLLEXPORT
+#      define JEMALLOC_EXPORT __declspec(dllexport)
+#    else
+#      define JEMALLOC_EXPORT __declspec(dllimport)
+#    endif
+#  endif
+#  define JEMALLOC_FORMAT_PRINTF(s, i)
+#  define JEMALLOC_NOINLINE __declspec(noinline)
+#  ifdef __cplusplus
+#    define JEMALLOC_NOTHROW __declspec(nothrow)
 #  else
-#    define JEMALLOC_EXPORT __declspec(dllimport)
+#    define JEMALLOC_NOTHROW
 #  endif
-#  define JEMALLOC_ALIGNED(s) __declspec(align(s))
 #  define JEMALLOC_SECTION(s) __declspec(allocate(s))
-#  define JEMALLOC_NOINLINE __declspec(noinline)
+#  define JEMALLOC_RESTRICT_RETURN __declspec(restrict)
+#  if _MSC_VER >= 1900 && !defined(__EDG__)
+#    define JEMALLOC_ALLOCATOR __declspec(allocator)
+#  else
+#    define JEMALLOC_ALLOCATOR
+#  endif
 #else
 #  define JEMALLOC_ATTR(s)
-#  define JEMALLOC_EXPORT
 #  define JEMALLOC_ALIGNED(s)
-#  define JEMALLOC_SECTION(s)
+#  define JEMALLOC_ALLOC_SIZE(s)
+#  define JEMALLOC_ALLOC_SIZE2(s1, s2)
+#  define JEMALLOC_EXPORT
+#  define JEMALLOC_FORMAT_PRINTF(s, i)
 #  define JEMALLOC_NOINLINE
+#  define JEMALLOC_NOTHROW
+#  define JEMALLOC_SECTION(s)
+#  define JEMALLOC_RESTRICT_RETURN
+#  define JEMALLOC_ALLOCATOR
 #endif
index f81adc14a7e70464f9b89f04593f338685db2c07..a78414b196fc4063f90ba71d4c3703fcdb7bee18 100644 (file)
@@ -7,44 +7,60 @@ extern JEMALLOC_EXPORT const char     *@je_@malloc_conf;
 extern JEMALLOC_EXPORT void            (*@je_@malloc_message)(void *cbopaque,
     const char *s);
 
-JEMALLOC_EXPORT void   *@je_@malloc(size_t size) JEMALLOC_ATTR(malloc);
-JEMALLOC_EXPORT void   *@je_@calloc(size_t num, size_t size)
-    JEMALLOC_ATTR(malloc);
-JEMALLOC_EXPORT int    @je_@posix_memalign(void **memptr, size_t alignment,
-    size_t size) JEMALLOC_ATTR(nonnull(1));
-JEMALLOC_EXPORT void   *@je_@aligned_alloc(size_t alignment, size_t size)
-    JEMALLOC_ATTR(malloc);
-JEMALLOC_EXPORT void   *@je_@realloc(void *ptr, size_t size);
-JEMALLOC_EXPORT void   @je_@free(void *ptr);
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+    void JEMALLOC_NOTHROW      *@je_@malloc(size_t size)
+    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+    void JEMALLOC_NOTHROW      *@je_@calloc(size_t num, size_t size)
+    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2);
+JEMALLOC_EXPORT int JEMALLOC_NOTHROW   @je_@posix_memalign(void **memptr,
+    size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(nonnull(1));
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+    void JEMALLOC_NOTHROW      *@je_@aligned_alloc(size_t alignment,
+    size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc)
+    JEMALLOC_ALLOC_SIZE(2);
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+    void JEMALLOC_NOTHROW      *@je_@realloc(void *ptr, size_t size)
+    JEMALLOC_CXX_THROW JEMALLOC_ALLOC_SIZE(2);
+JEMALLOC_EXPORT void JEMALLOC_NOTHROW  @je_@free(void *ptr)
+    JEMALLOC_CXX_THROW;
 
-JEMALLOC_EXPORT void   *@je_@mallocx(size_t size, int flags)
-    JEMALLOC_ATTR(malloc);
-JEMALLOC_EXPORT void   *@je_@rallocx(void *ptr, size_t size, int flags);
-JEMALLOC_EXPORT size_t @je_@xallocx(void *ptr, size_t size, size_t extra,
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+    void JEMALLOC_NOTHROW      *@je_@mallocx(size_t size, int flags)
+    JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+    void JEMALLOC_NOTHROW      *@je_@rallocx(void *ptr, size_t size,
+    int flags) JEMALLOC_ALLOC_SIZE(2);
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW        @je_@xallocx(void *ptr, size_t size,
+    size_t extra, int flags);
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW        @je_@sallocx(const void *ptr,
+    int flags) JEMALLOC_ATTR(pure);
+JEMALLOC_EXPORT void JEMALLOC_NOTHROW  @je_@dallocx(void *ptr, int flags);
+JEMALLOC_EXPORT void JEMALLOC_NOTHROW  @je_@sdallocx(void *ptr, size_t size,
     int flags);
-JEMALLOC_EXPORT size_t @je_@sallocx(const void *ptr, int flags)
-    JEMALLOC_ATTR(pure);
-JEMALLOC_EXPORT void   @je_@dallocx(void *ptr, int flags);
-JEMALLOC_EXPORT void   @je_@sdallocx(void *ptr, size_t size, int flags);
-JEMALLOC_EXPORT size_t @je_@nallocx(size_t size, int flags)
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW        @je_@nallocx(size_t size, int flags)
     JEMALLOC_ATTR(pure);
 
-JEMALLOC_EXPORT int    @je_@mallctl(const char *name, void *oldp,
-    size_t *oldlenp, void *newp, size_t newlen);
-JEMALLOC_EXPORT int    @je_@mallctlnametomib(const char *name, size_t *mibp,
-    size_t *miblenp);
-JEMALLOC_EXPORT int    @je_@mallctlbymib(const size_t *mib, size_t miblen,
+JEMALLOC_EXPORT int JEMALLOC_NOTHROW   @je_@mallctl(const char *name,
     void *oldp, size_t *oldlenp, void *newp, size_t newlen);
-JEMALLOC_EXPORT void   @je_@malloc_stats_print(void (*write_cb)(void *,
-    const char *), void *@je_@cbopaque, const char *opts);
-JEMALLOC_EXPORT size_t @je_@malloc_usable_size(
-    JEMALLOC_USABLE_SIZE_CONST void *ptr);
+JEMALLOC_EXPORT int JEMALLOC_NOTHROW   @je_@mallctlnametomib(const char *name,
+    size_t *mibp, size_t *miblenp);
+JEMALLOC_EXPORT int JEMALLOC_NOTHROW   @je_@mallctlbymib(const size_t *mib,
+    size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
+JEMALLOC_EXPORT void JEMALLOC_NOTHROW  @je_@malloc_stats_print(
+    void (*write_cb)(void *, const char *), void *@je_@cbopaque,
+    const char *opts);
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW        @je_@malloc_usable_size(
+    JEMALLOC_USABLE_SIZE_CONST void *ptr) JEMALLOC_CXX_THROW;
 
 #ifdef JEMALLOC_OVERRIDE_MEMALIGN
-JEMALLOC_EXPORT void * @je_@memalign(size_t alignment, size_t size)
-    JEMALLOC_ATTR(malloc);
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+    void JEMALLOC_NOTHROW      *@je_@memalign(size_t alignment, size_t size)
+    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc);
 #endif
 
 #ifdef JEMALLOC_OVERRIDE_VALLOC
-JEMALLOC_EXPORT void * @je_@valloc(size_t size) JEMALLOC_ATTR(malloc);
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+    void JEMALLOC_NOTHROW      *@je_@valloc(size_t size) JEMALLOC_CXX_THROW
+    JEMALLOC_ATTR(malloc);
 #endif
index 8092f1b155e6ed58c8afd0f67fdab8e7416c8d40..fa7b350adcda9884f3d6b315323c8edf7e7bd086 100644 (file)
@@ -1,2 +1,57 @@
-typedef void *(chunk_alloc_t)(void *, size_t, size_t, bool *, unsigned);
-typedef bool (chunk_dalloc_t)(void *, size_t, unsigned);
+/*
+ * void *
+ * chunk_alloc(void *new_addr, size_t size, size_t alignment, bool *zero,
+ *     bool *commit, unsigned arena_ind);
+ */
+typedef void *(chunk_alloc_t)(void *, size_t, size_t, bool *, bool *, unsigned);
+
+/*
+ * bool
+ * chunk_dalloc(void *chunk, size_t size, bool committed, unsigned arena_ind);
+ */
+typedef bool (chunk_dalloc_t)(void *, size_t, bool, unsigned);
+
+/*
+ * bool
+ * chunk_commit(void *chunk, size_t size, size_t offset, size_t length,
+ *     unsigned arena_ind);
+ */
+typedef bool (chunk_commit_t)(void *, size_t, size_t, size_t, unsigned);
+
+/*
+ * bool
+ * chunk_decommit(void *chunk, size_t size, size_t offset, size_t length,
+ *     unsigned arena_ind);
+ */
+typedef bool (chunk_decommit_t)(void *, size_t, size_t, size_t, unsigned);
+
+/*
+ * bool
+ * chunk_purge(void *chunk, size_t size, size_t offset, size_t length,
+ *     unsigned arena_ind);
+ */
+typedef bool (chunk_purge_t)(void *, size_t, size_t, size_t, unsigned);
+
+/*
+ * bool
+ * chunk_split(void *chunk, size_t size, size_t size_a, size_t size_b,
+ *     bool committed, unsigned arena_ind);
+ */
+typedef bool (chunk_split_t)(void *, size_t, size_t, size_t, bool, unsigned);
+
+/*
+ * bool
+ * chunk_merge(void *chunk_a, size_t size_a, void *chunk_b, size_t size_b,
+ *     bool committed, unsigned arena_ind);
+ */
+typedef bool (chunk_merge_t)(void *, size_t, void *, size_t, bool, unsigned);
+
+typedef struct {
+       chunk_alloc_t           *alloc;
+       chunk_dalloc_t          *dalloc;
+       chunk_commit_t          *commit;
+       chunk_decommit_t        *decommit;
+       chunk_purge_t           *purge;
+       chunk_split_t           *split;
+       chunk_merge_t           *merge;
+} chunk_hooks_t;
diff --git a/src/jemalloc/include/msvc_compat/C99/inttypes.h b/src/jemalloc/include/msvc_compat/C99/inttypes.h
deleted file mode 100644 (file)
index a4e6b75..0000000
+++ /dev/null
@@ -1,313 +0,0 @@
-// ISO C9x  compliant inttypes.h for Microsoft Visual Studio
-// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 
-// 
-//  Copyright (c) 2006 Alexander Chemeris
-// 
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-// 
-//   1. Redistributions of source code must retain the above copyright notice,
-//      this list of conditions and the following disclaimer.
-// 
-//   2. Redistributions in binary form must reproduce the above copyright
-//      notice, this list of conditions and the following disclaimer in the
-//      documentation and/or other materials provided with the distribution.
-// 
-//   3. The name of the author may be used to endorse or promote products
-//      derived from this software without specific prior written permission.
-// 
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// 
-///////////////////////////////////////////////////////////////////////////////
-
-#ifndef _MSC_VER // [
-#error "Use this header only with Microsoft Visual C++ compilers!"
-#endif // _MSC_VER ]
-
-#ifndef _MSC_INTTYPES_H_ // [
-#define _MSC_INTTYPES_H_
-
-#if _MSC_VER > 1000
-#pragma once
-#endif
-
-#include "stdint.h"
-
-// 7.8 Format conversion of integer types
-
-typedef struct {
-   intmax_t quot;
-   intmax_t rem;
-} imaxdiv_t;
-
-// 7.8.1 Macros for format specifiers
-
-#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [   See footnote 185 at page 198
-
-#ifdef _WIN64
-#  define __PRI64_PREFIX        "l"
-#  define __PRIPTR_PREFIX       "l"
-#else
-#  define __PRI64_PREFIX        "ll"
-#  define __PRIPTR_PREFIX
-#endif
-
-// The fprintf macros for signed integers are:
-#define PRId8       "d"
-#define PRIi8       "i"
-#define PRIdLEAST8  "d"
-#define PRIiLEAST8  "i"
-#define PRIdFAST8   "d"
-#define PRIiFAST8   "i"
-
-#define PRId16       "hd"
-#define PRIi16       "hi"
-#define PRIdLEAST16  "hd"
-#define PRIiLEAST16  "hi"
-#define PRIdFAST16   "hd"
-#define PRIiFAST16   "hi"
-
-#define PRId32       "d"
-#define PRIi32       "i"
-#define PRIdLEAST32  "d"
-#define PRIiLEAST32  "i"
-#define PRIdFAST32   "d"
-#define PRIiFAST32   "i"
-
-#define PRId64       __PRI64_PREFIX "d"
-#define PRIi64       __PRI64_PREFIX "i"
-#define PRIdLEAST64  __PRI64_PREFIX "d"
-#define PRIiLEAST64  __PRI64_PREFIX "i"
-#define PRIdFAST64   __PRI64_PREFIX "d"
-#define PRIiFAST64   __PRI64_PREFIX "i"
-
-#define PRIdMAX     __PRI64_PREFIX "d"
-#define PRIiMAX     __PRI64_PREFIX "i"
-
-#define PRIdPTR     __PRIPTR_PREFIX "d"
-#define PRIiPTR     __PRIPTR_PREFIX "i"
-
-// The fprintf macros for unsigned integers are:
-#define PRIo8       "o"
-#define PRIu8       "u"
-#define PRIx8       "x"
-#define PRIX8       "X"
-#define PRIoLEAST8  "o"
-#define PRIuLEAST8  "u"
-#define PRIxLEAST8  "x"
-#define PRIXLEAST8  "X"
-#define PRIoFAST8   "o"
-#define PRIuFAST8   "u"
-#define PRIxFAST8   "x"
-#define PRIXFAST8   "X"
-
-#define PRIo16       "ho"
-#define PRIu16       "hu"
-#define PRIx16       "hx"
-#define PRIX16       "hX"
-#define PRIoLEAST16  "ho"
-#define PRIuLEAST16  "hu"
-#define PRIxLEAST16  "hx"
-#define PRIXLEAST16  "hX"
-#define PRIoFAST16   "ho"
-#define PRIuFAST16   "hu"
-#define PRIxFAST16   "hx"
-#define PRIXFAST16   "hX"
-
-#define PRIo32       "o"
-#define PRIu32       "u"
-#define PRIx32       "x"
-#define PRIX32       "X"
-#define PRIoLEAST32  "o"
-#define PRIuLEAST32  "u"
-#define PRIxLEAST32  "x"
-#define PRIXLEAST32  "X"
-#define PRIoFAST32   "o"
-#define PRIuFAST32   "u"
-#define PRIxFAST32   "x"
-#define PRIXFAST32   "X"
-
-#define PRIo64       __PRI64_PREFIX "o"
-#define PRIu64       __PRI64_PREFIX "u"
-#define PRIx64       __PRI64_PREFIX "x"
-#define PRIX64       __PRI64_PREFIX "X"
-#define PRIoLEAST64  __PRI64_PREFIX "o"
-#define PRIuLEAST64  __PRI64_PREFIX "u"
-#define PRIxLEAST64  __PRI64_PREFIX "x"
-#define PRIXLEAST64  __PRI64_PREFIX "X"
-#define PRIoFAST64   __PRI64_PREFIX "o"
-#define PRIuFAST64   __PRI64_PREFIX "u"
-#define PRIxFAST64   __PRI64_PREFIX "x"
-#define PRIXFAST64   __PRI64_PREFIX "X"
-
-#define PRIoMAX     __PRI64_PREFIX "o"
-#define PRIuMAX     __PRI64_PREFIX "u"
-#define PRIxMAX     __PRI64_PREFIX "x"
-#define PRIXMAX     __PRI64_PREFIX "X"
-
-#define PRIoPTR     __PRIPTR_PREFIX "o"
-#define PRIuPTR     __PRIPTR_PREFIX "u"
-#define PRIxPTR     __PRIPTR_PREFIX "x"
-#define PRIXPTR     __PRIPTR_PREFIX "X"
-
-// The fscanf macros for signed integers are:
-#define SCNd8       "d"
-#define SCNi8       "i"
-#define SCNdLEAST8  "d"
-#define SCNiLEAST8  "i"
-#define SCNdFAST8   "d"
-#define SCNiFAST8   "i"
-
-#define SCNd16       "hd"
-#define SCNi16       "hi"
-#define SCNdLEAST16  "hd"
-#define SCNiLEAST16  "hi"
-#define SCNdFAST16   "hd"
-#define SCNiFAST16   "hi"
-
-#define SCNd32       "ld"
-#define SCNi32       "li"
-#define SCNdLEAST32  "ld"
-#define SCNiLEAST32  "li"
-#define SCNdFAST32   "ld"
-#define SCNiFAST32   "li"
-
-#define SCNd64       "I64d"
-#define SCNi64       "I64i"
-#define SCNdLEAST64  "I64d"
-#define SCNiLEAST64  "I64i"
-#define SCNdFAST64   "I64d"
-#define SCNiFAST64   "I64i"
-
-#define SCNdMAX     "I64d"
-#define SCNiMAX     "I64i"
-
-#ifdef _WIN64 // [
-#  define SCNdPTR     "I64d"
-#  define SCNiPTR     "I64i"
-#else  // _WIN64 ][
-#  define SCNdPTR     "ld"
-#  define SCNiPTR     "li"
-#endif  // _WIN64 ]
-
-// The fscanf macros for unsigned integers are:
-#define SCNo8       "o"
-#define SCNu8       "u"
-#define SCNx8       "x"
-#define SCNX8       "X"
-#define SCNoLEAST8  "o"
-#define SCNuLEAST8  "u"
-#define SCNxLEAST8  "x"
-#define SCNXLEAST8  "X"
-#define SCNoFAST8   "o"
-#define SCNuFAST8   "u"
-#define SCNxFAST8   "x"
-#define SCNXFAST8   "X"
-
-#define SCNo16       "ho"
-#define SCNu16       "hu"
-#define SCNx16       "hx"
-#define SCNX16       "hX"
-#define SCNoLEAST16  "ho"
-#define SCNuLEAST16  "hu"
-#define SCNxLEAST16  "hx"
-#define SCNXLEAST16  "hX"
-#define SCNoFAST16   "ho"
-#define SCNuFAST16   "hu"
-#define SCNxFAST16   "hx"
-#define SCNXFAST16   "hX"
-
-#define SCNo32       "lo"
-#define SCNu32       "lu"
-#define SCNx32       "lx"
-#define SCNX32       "lX"
-#define SCNoLEAST32  "lo"
-#define SCNuLEAST32  "lu"
-#define SCNxLEAST32  "lx"
-#define SCNXLEAST32  "lX"
-#define SCNoFAST32   "lo"
-#define SCNuFAST32   "lu"
-#define SCNxFAST32   "lx"
-#define SCNXFAST32   "lX"
-
-#define SCNo64       "I64o"
-#define SCNu64       "I64u"
-#define SCNx64       "I64x"
-#define SCNX64       "I64X"
-#define SCNoLEAST64  "I64o"
-#define SCNuLEAST64  "I64u"
-#define SCNxLEAST64  "I64x"
-#define SCNXLEAST64  "I64X"
-#define SCNoFAST64   "I64o"
-#define SCNuFAST64   "I64u"
-#define SCNxFAST64   "I64x"
-#define SCNXFAST64   "I64X"
-
-#define SCNoMAX     "I64o"
-#define SCNuMAX     "I64u"
-#define SCNxMAX     "I64x"
-#define SCNXMAX     "I64X"
-
-#ifdef _WIN64 // [
-#  define SCNoPTR     "I64o"
-#  define SCNuPTR     "I64u"
-#  define SCNxPTR     "I64x"
-#  define SCNXPTR     "I64X"
-#else  // _WIN64 ][
-#  define SCNoPTR     "lo"
-#  define SCNuPTR     "lu"
-#  define SCNxPTR     "lx"
-#  define SCNXPTR     "lX"
-#endif  // _WIN64 ]
-
-#endif // __STDC_FORMAT_MACROS ]
-
-// 7.8.2 Functions for greatest-width integer types
-
-// 7.8.2.1 The imaxabs function
-#define imaxabs _abs64
-
-// 7.8.2.2 The imaxdiv function
-
-// This is modified version of div() function from Microsoft's div.c found
-// in %MSVC.NET%\crt\src\div.c
-#ifdef STATIC_IMAXDIV // [
-static
-#else // STATIC_IMAXDIV ][
-_inline
-#endif // STATIC_IMAXDIV ]
-imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
-{
-   imaxdiv_t result;
-
-   result.quot = numer / denom;
-   result.rem = numer % denom;
-
-   if (numer < 0 && result.rem > 0) {
-      // did division wrong; must fix up
-      ++result.quot;
-      result.rem -= denom;
-   }
-
-   return result;
-}
-
-// 7.8.2.3 The strtoimax and strtoumax functions
-#define strtoimax _strtoi64
-#define strtoumax _strtoui64
-
-// 7.8.2.4 The wcstoimax and wcstoumax functions
-#define wcstoimax _wcstoi64
-#define wcstoumax _wcstoui64
-
-
-#endif // _MSC_INTTYPES_H_ ]
index c84975b6b8e1d682133614705f077f863ffef0a1..f01ffdd18a502da0e4df9bb9a72f531acd319643 100644 (file)
@@ -3,8 +3,9 @@
 
 /* MSVC doesn't define ffs/ffsl. This dummy strings.h header is provided
  * for both */
-#include <intrin.h>
-#pragma intrinsic(_BitScanForward)
+#ifdef _MSC_VER
+#  include <intrin.h>
+#  pragma intrinsic(_BitScanForward)
 static __forceinline int ffsl(long x)
 {
        unsigned long i;
@@ -20,4 +21,9 @@ static __forceinline int ffs(int x)
        return (ffsl(x));
 }
 
+#else
+#  define ffsl(x) __builtin_ffsl(x)
+#  define ffs(x) __builtin_ffs(x)
 #endif
+
+#endif /* strings_h */
diff --git a/src/jemalloc/include/msvc_compat/windows_extra.h b/src/jemalloc/include/msvc_compat/windows_extra.h
new file mode 100644 (file)
index 0000000..0c5e323
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef MSVC_COMPAT_WINDOWS_EXTRA_H
+#define        MSVC_COMPAT_WINDOWS_EXTRA_H
+
+#ifndef ENOENT
+#  define ENOENT ERROR_PATH_NOT_FOUND
+#endif
+#ifndef EINVAL
+#  define EINVAL ERROR_BAD_ARGUMENTS
+#endif
+#ifndef EAGAIN
+#  define EAGAIN ERROR_OUTOFMEMORY
+#endif
+#ifndef EPERM
+#  define EPERM  ERROR_WRITE_FAULT
+#endif
+#ifndef EFAULT
+#  define EFAULT ERROR_INVALID_ADDRESS
+#endif
+#ifndef ENOMEM
+#  define ENOMEM ERROR_NOT_ENOUGH_MEMORY
+#endif
+#ifndef ERANGE
+#  define ERANGE ERROR_INVALID_DATA
+#endif
+
+#endif /* MSVC_COMPAT_WINDOWS_EXTRA_H */
index af3f945d42f7f77c99b880e20bad332af05546a1..1a3ad9b34d9200d36ab8bb03c434542f6a8469b3 100644 (file)
@@ -2,10 +2,11 @@ prefix=@prefix@
 exec_prefix=@exec_prefix@
 libdir=@libdir@
 includedir=@includedir@
+install_suffix=@install_suffix@
 
 Name: jemalloc
 Description: A general purpose malloc(3) implementation that emphasizes fragmentation avoidance and scalable concurrency support.
 URL: http://www.canonware.com/jemalloc
 Version: @jemalloc_version@
 Cflags: -I${includedir}
-Libs: -L${libdir} -ljemalloc
+Libs: -L${libdir} -ljemalloc${install_suffix}
index b7300a92413a8aa7099bb9981c98e21d889a297c..43733cc157274ce0e40d86be1f9cd2fbc4cae4bf 100644 (file)
@@ -5,44 +5,17 @@
 /* Data. */
 
 ssize_t                opt_lg_dirty_mult = LG_DIRTY_MULT_DEFAULT;
+static ssize_t lg_dirty_mult_default;
 arena_bin_info_t       arena_bin_info[NBINS];
 
-JEMALLOC_ALIGNED(CACHELINE)
-const uint32_t small_bin2size_tab[NBINS] = {
-#define        B2S_bin_yes(size) \
-       size,
-#define        B2S_bin_no(size)
-#define        SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup) \
-       B2S_bin_##bin((ZU(1)<<lg_grp) + (ZU(ndelta)<<lg_delta))
-       SIZE_CLASSES
-#undef B2S_bin_yes
-#undef B2S_bin_no
-#undef SC
-};
-
-JEMALLOC_ALIGNED(CACHELINE)
-const uint8_t  small_size2bin_tab[] = {
-#define        S2B_3(i)        i,
-#define        S2B_4(i)        S2B_3(i) S2B_3(i)
-#define        S2B_5(i)        S2B_4(i) S2B_4(i)
-#define        S2B_6(i)        S2B_5(i) S2B_5(i)
-#define        S2B_7(i)        S2B_6(i) S2B_6(i)
-#define        S2B_8(i)        S2B_7(i) S2B_7(i)
-#define        S2B_9(i)        S2B_8(i) S2B_8(i)
-#define        S2B_no(i)
-#define        SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup) \
-       S2B_##lg_delta_lookup(index)
-       SIZE_CLASSES
-#undef S2B_3
-#undef S2B_4
-#undef S2B_5
-#undef S2B_6
-#undef S2B_7
-#undef S2B_8
-#undef S2B_9
-#undef S2B_no
-#undef SC
-};
+size_t         map_bias;
+size_t         map_misc_offset;
+size_t         arena_maxrun; /* Max run size for arenas. */
+size_t         large_maxclass; /* Max large size class. */
+static size_t  small_maxrun; /* Max run size used for small size classes. */
+static bool    *small_run_tab; /* Valid small run page multiples. */
+unsigned       nlclasses; /* Number of large size classes. */
+unsigned       nhclasses; /* Number of huge size classes. */
 
 /******************************************************************************/
 /*
@@ -52,7 +25,7 @@ const uint8_t small_size2bin_tab[] = {
 
 static void    arena_purge(arena_t *arena, bool all);
 static void    arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty,
-    bool cleaned);
+    bool cleaned, bool decommitted);
 static void    arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk,
     arena_run_t *run, arena_bin_t *bin);
 static void    arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk,
@@ -60,16 +33,49 @@ static void arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk,
 
 /******************************************************************************/
 
+#define        CHUNK_MAP_KEY           ((uintptr_t)0x1U)
+
+JEMALLOC_INLINE_C arena_chunk_map_misc_t *
+arena_miscelm_key_create(size_t size)
+{
+
+       return ((arena_chunk_map_misc_t *)(arena_mapbits_size_encode(size) |
+           CHUNK_MAP_KEY));
+}
+
+JEMALLOC_INLINE_C bool
+arena_miscelm_is_key(const arena_chunk_map_misc_t *miscelm)
+{
+
+       return (((uintptr_t)miscelm & CHUNK_MAP_KEY) != 0);
+}
+
+#undef CHUNK_MAP_KEY
+
 JEMALLOC_INLINE_C size_t
-arena_miscelm_to_bits(arena_chunk_map_misc_t *miscelm)
+arena_miscelm_key_size_get(const arena_chunk_map_misc_t *miscelm)
 {
-       arena_chunk_t *chunk = CHUNK_ADDR2BASE(miscelm);
-       size_t pageind = arena_miscelm_to_pageind(miscelm);
 
-       return arena_mapbits_get(chunk, pageind);
+       assert(arena_miscelm_is_key(miscelm));
+
+       return (arena_mapbits_size_decode((uintptr_t)miscelm));
+}
+
+JEMALLOC_INLINE_C size_t
+arena_miscelm_size_get(arena_chunk_map_misc_t *miscelm)
+{
+       arena_chunk_t *chunk;
+       size_t pageind, mapbits;
+
+       assert(!arena_miscelm_is_key(miscelm));
+
+       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(miscelm);
+       pageind = arena_miscelm_to_pageind(miscelm);
+       mapbits = arena_mapbits_get(chunk, pageind);
+       return (arena_mapbits_size_decode(mapbits));
 }
 
-static inline int
+JEMALLOC_INLINE_C int
 arena_run_comp(arena_chunk_map_misc_t *a, arena_chunk_map_misc_t *b)
 {
        uintptr_t a_miscelm = (uintptr_t)a;
@@ -85,25 +91,103 @@ arena_run_comp(arena_chunk_map_misc_t *a, arena_chunk_map_misc_t *b)
 rb_gen(static UNUSED, arena_run_tree_, arena_run_tree_t, arena_chunk_map_misc_t,
     rb_link, arena_run_comp)
 
-static inline int
+static size_t
+run_quantize(size_t size)
+{
+       size_t qsize;
+
+       assert(size != 0);
+       assert(size == PAGE_CEILING(size));
+
+       /* Don't change sizes that are valid small run sizes. */
+       if (size <= small_maxrun && small_run_tab[size >> LG_PAGE])
+               return (size);
+
+       /*
+        * Round down to the nearest run size that can actually be requested
+        * during normal large allocation.  Add large_pad so that cache index
+        * randomization can offset the allocation from the page boundary.
+        */
+       qsize = index2size(size2index(size - large_pad + 1) - 1) + large_pad;
+       if (qsize <= SMALL_MAXCLASS + large_pad)
+               return (run_quantize(size - large_pad));
+       assert(qsize <= size);
+       return (qsize);
+}
+
+static size_t
+run_quantize_next(size_t size)
+{
+       size_t large_run_size_next;
+
+       assert(size != 0);
+       assert(size == PAGE_CEILING(size));
+
+       /*
+        * Return the next quantized size greater than the input size.
+        * Quantized sizes comprise the union of run sizes that back small
+        * region runs, and run sizes that back large regions with no explicit
+        * alignment constraints.
+        */
+
+       if (size > SMALL_MAXCLASS) {
+               large_run_size_next = PAGE_CEILING(index2size(size2index(size -
+                   large_pad) + 1) + large_pad);
+       } else
+               large_run_size_next = SIZE_T_MAX;
+       if (size >= small_maxrun)
+               return (large_run_size_next);
+
+       while (true) {
+               size += PAGE;
+               assert(size <= small_maxrun);
+               if (small_run_tab[size >> LG_PAGE]) {
+                       if (large_run_size_next < size)
+                               return (large_run_size_next);
+                       return (size);
+               }
+       }
+}
+
+static size_t
+run_quantize_first(size_t size)
+{
+       size_t qsize = run_quantize(size);
+
+       if (qsize < size) {
+               /*
+                * Skip a quantization that may have an adequately large run,
+                * because under-sized runs may be mixed in.  This only happens
+                * when an unusual size is requested, i.e. for aligned
+                * allocation, and is just one of several places where linear
+                * search would potentially find sufficiently aligned available
+                * memory somewhere lower.
+                */
+               qsize = run_quantize_next(size);
+       }
+       return (qsize);
+}
+
+JEMALLOC_INLINE_C int
 arena_avail_comp(arena_chunk_map_misc_t *a, arena_chunk_map_misc_t *b)
 {
        int ret;
-       size_t a_size;
-       size_t b_size = arena_miscelm_to_bits(b) & ~PAGE_MASK;
        uintptr_t a_miscelm = (uintptr_t)a;
-       uintptr_t b_miscelm = (uintptr_t)b;
-
-       if (a_miscelm & CHUNK_MAP_KEY)
-               a_size = a_miscelm & ~PAGE_MASK;
-       else
-               a_size = arena_miscelm_to_bits(a) & ~PAGE_MASK;
+       size_t a_qsize = run_quantize(arena_miscelm_is_key(a) ?
+           arena_miscelm_key_size_get(a) : arena_miscelm_size_get(a));
+       size_t b_qsize = run_quantize(arena_miscelm_size_get(b));
 
-       ret = (a_size > b_size) - (a_size < b_size);
+       /*
+        * Compare based on quantized size rather than size, in order to sort
+        * equally useful runs only by address.
+        */
+       ret = (a_qsize > b_qsize) - (a_qsize < b_qsize);
        if (ret == 0) {
-               if (!(a_miscelm & CHUNK_MAP_KEY))
+               if (!arena_miscelm_is_key(a)) {
+                       uintptr_t b_miscelm = (uintptr_t)b;
+
                        ret = (a_miscelm > b_miscelm) - (a_miscelm < b_miscelm);
-               else {
+               else {
                        /*
                         * Treat keys as if they are lower than anything else.
                         */
@@ -141,35 +225,70 @@ arena_avail_remove(arena_t *arena, arena_chunk_t *chunk, size_t pageind,
 }
 
 static void
-arena_dirty_insert(arena_t *arena, arena_chunk_t *chunk, size_t pageind,
+arena_run_dirty_insert(arena_t *arena, arena_chunk_t *chunk, size_t pageind,
     size_t npages)
 {
        arena_chunk_map_misc_t *miscelm = arena_miscelm_get(chunk, pageind);
+
        assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >>
            LG_PAGE));
        assert(arena_mapbits_dirty_get(chunk, pageind) == CHUNK_MAP_DIRTY);
        assert(arena_mapbits_dirty_get(chunk, pageind+npages-1) ==
            CHUNK_MAP_DIRTY);
-       ql_elm_new(miscelm, dr_link);
-       ql_tail_insert(&arena->runs_dirty, miscelm, dr_link);
+
+       qr_new(&miscelm->rd, rd_link);
+       qr_meld(&arena->runs_dirty, &miscelm->rd, rd_link);
        arena->ndirty += npages;
 }
 
 static void
-arena_dirty_remove(arena_t *arena, arena_chunk_t *chunk, size_t pageind,
+arena_run_dirty_remove(arena_t *arena, arena_chunk_t *chunk, size_t pageind,
     size_t npages)
 {
        arena_chunk_map_misc_t *miscelm = arena_miscelm_get(chunk, pageind);
+
        assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >>
            LG_PAGE));
        assert(arena_mapbits_dirty_get(chunk, pageind) == CHUNK_MAP_DIRTY);
        assert(arena_mapbits_dirty_get(chunk, pageind+npages-1) ==
            CHUNK_MAP_DIRTY);
-       ql_remove(&arena->runs_dirty, miscelm, dr_link);
+
+       qr_remove(&miscelm->rd, rd_link);
+       assert(arena->ndirty >= npages);
        arena->ndirty -= npages;
 }
 
-static inline void *
+static size_t
+arena_chunk_dirty_npages(const extent_node_t *node)
+{
+
+       return (extent_node_size_get(node) >> LG_PAGE);
+}
+
+void
+arena_chunk_cache_maybe_insert(arena_t *arena, extent_node_t *node, bool cache)
+{
+
+       if (cache) {
+               extent_node_dirty_linkage_init(node);
+               extent_node_dirty_insert(node, &arena->runs_dirty,
+                   &arena->chunks_cache);
+               arena->ndirty += arena_chunk_dirty_npages(node);
+       }
+}
+
+void
+arena_chunk_cache_maybe_remove(arena_t *arena, extent_node_t *node, bool dirty)
+{
+
+       if (dirty) {
+               extent_node_dirty_remove(node);
+               assert(arena->ndirty >= arena_chunk_dirty_npages(node));
+               arena->ndirty -= arena_chunk_dirty_npages(node);
+       }
+}
+
+JEMALLOC_INLINE_C void *
 arena_run_reg_alloc(arena_run_t *run, arena_bin_info_t *bin_info)
 {
        void *ret;
@@ -186,19 +305,16 @@ arena_run_reg_alloc(arena_run_t *run, arena_bin_info_t *bin_info)
        ret = (void *)((uintptr_t)rpages + (uintptr_t)bin_info->reg0_offset +
            (uintptr_t)(bin_info->reg_interval * regind));
        run->nfree--;
-       if (regind == run->nextind)
-               run->nextind++;
-       assert(regind < run->nextind);
        return (ret);
 }
 
-static inline void
+JEMALLOC_INLINE_C void
 arena_run_reg_dalloc(arena_run_t *run, void *ptr)
 {
        arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
        size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
        size_t mapbits = arena_mapbits_get(chunk, pageind);
-       size_t binind = arena_ptr_small_binind_get(ptr, mapbits);
+       szind_t binind = arena_ptr_small_binind_get(ptr, mapbits);
        arena_bin_info_t *bin_info = &arena_bin_info[binind];
        unsigned regind = arena_run_regind(run, bin_info, ptr);
 
@@ -218,7 +334,7 @@ arena_run_reg_dalloc(arena_run_t *run, void *ptr)
        run->nfree++;
 }
 
-static inline void
+JEMALLOC_INLINE_C void
 arena_run_zero(arena_chunk_t *chunk, size_t run_ind, size_t npages)
 {
 
@@ -228,7 +344,7 @@ arena_run_zero(arena_chunk_t *chunk, size_t run_ind, size_t npages)
            (npages << LG_PAGE));
 }
 
-static inline void
+JEMALLOC_INLINE_C void
 arena_run_page_mark_zeroed(arena_chunk_t *chunk, size_t run_ind)
 {
 
@@ -236,7 +352,7 @@ arena_run_page_mark_zeroed(arena_chunk_t *chunk, size_t run_ind)
            << LG_PAGE)), PAGE);
 }
 
-static inline void
+JEMALLOC_INLINE_C void
 arena_run_page_validate_zeroed(arena_chunk_t *chunk, size_t run_ind)
 {
        size_t i;
@@ -262,10 +378,12 @@ arena_cactive_update(arena_t *arena, size_t add_pages, size_t sub_pages)
 
 static void
 arena_run_split_remove(arena_t *arena, arena_chunk_t *chunk, size_t run_ind,
-    size_t flag_dirty, size_t need_pages)
+    size_t flag_dirty, size_t flag_decommitted, size_t need_pages)
 {
        size_t total_pages, rem_pages;
 
+       assert(flag_dirty == 0 || flag_decommitted == 0);
+
        total_pages = arena_mapbits_unallocated_size_get(chunk, run_ind) >>
            LG_PAGE;
        assert(arena_mapbits_dirty_get(chunk, run_ind+total_pages-1) ==
@@ -275,61 +393,73 @@ arena_run_split_remove(arena_t *arena, arena_chunk_t *chunk, size_t run_ind,
 
        arena_avail_remove(arena, chunk, run_ind, total_pages);
        if (flag_dirty != 0)
-               arena_dirty_remove(arena, chunk, run_ind, total_pages);
+               arena_run_dirty_remove(arena, chunk, run_ind, total_pages);
        arena_cactive_update(arena, need_pages, 0);
        arena->nactive += need_pages;
 
        /* Keep track of trailing unused pages for later use. */
        if (rem_pages > 0) {
+               size_t flags = flag_dirty | flag_decommitted;
+               size_t flag_unzeroed_mask = (flags == 0) ?  CHUNK_MAP_UNZEROED :
+                   0;
+
+               arena_mapbits_unallocated_set(chunk, run_ind+need_pages,
+                   (rem_pages << LG_PAGE), flags |
+                   (arena_mapbits_unzeroed_get(chunk, run_ind+need_pages) &
+                   flag_unzeroed_mask));
+               arena_mapbits_unallocated_set(chunk, run_ind+total_pages-1,
+                   (rem_pages << LG_PAGE), flags |
+                   (arena_mapbits_unzeroed_get(chunk, run_ind+total_pages-1) &
+                   flag_unzeroed_mask));
                if (flag_dirty != 0) {
-                       arena_mapbits_unallocated_set(chunk,
-                           run_ind+need_pages, (rem_pages << LG_PAGE),
-                           flag_dirty);
-                       arena_mapbits_unallocated_set(chunk,
-                           run_ind+total_pages-1, (rem_pages << LG_PAGE),
-                           flag_dirty);
-                       arena_dirty_insert(arena, chunk, run_ind+need_pages,
+                       arena_run_dirty_insert(arena, chunk, run_ind+need_pages,
                            rem_pages);
-               } else {
-                       arena_mapbits_unallocated_set(chunk, run_ind+need_pages,
-                           (rem_pages << LG_PAGE),
-                           arena_mapbits_unzeroed_get(chunk,
-                           run_ind+need_pages));
-                       arena_mapbits_unallocated_set(chunk,
-                           run_ind+total_pages-1, (rem_pages << LG_PAGE),
-                           arena_mapbits_unzeroed_get(chunk,
-                           run_ind+total_pages-1));
                }
                arena_avail_insert(arena, chunk, run_ind+need_pages, rem_pages);
        }
 }
 
-static void
+static bool
 arena_run_split_large_helper(arena_t *arena, arena_run_t *run, size_t size,
     bool remove, bool zero)
 {
        arena_chunk_t *chunk;
        arena_chunk_map_misc_t *miscelm;
-       size_t flag_dirty, run_ind, need_pages, i;
+       size_t flag_dirty, flag_decommitted, run_ind, need_pages;
+       size_t flag_unzeroed_mask;
 
        chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
        miscelm = arena_run_to_miscelm(run);
        run_ind = arena_miscelm_to_pageind(miscelm);
        flag_dirty = arena_mapbits_dirty_get(chunk, run_ind);
+       flag_decommitted = arena_mapbits_decommitted_get(chunk, run_ind);
        need_pages = (size >> LG_PAGE);
        assert(need_pages > 0);
 
+       if (flag_decommitted != 0 && arena->chunk_hooks.commit(chunk, chunksize,
+           run_ind << LG_PAGE, size, arena->ind))
+               return (true);
+
        if (remove) {
                arena_run_split_remove(arena, chunk, run_ind, flag_dirty,
-                   need_pages);
+                   flag_decommitted, need_pages);
        }
 
        if (zero) {
-               if (flag_dirty == 0) {
+               if (flag_decommitted != 0) {
+                       /* The run is untouched, and therefore zeroed. */
+                       JEMALLOC_VALGRIND_MAKE_MEM_DEFINED((void
+                           *)((uintptr_t)chunk + (run_ind << LG_PAGE)),
+                           (need_pages << LG_PAGE));
+               } else if (flag_dirty != 0) {
+                       /* The run is dirty, so all pages must be zeroed. */
+                       arena_run_zero(chunk, run_ind, need_pages);
+               } else {
                        /*
                         * The run is clean, so some pages may be zeroed (i.e.
                         * never before touched).
                         */
+                       size_t i;
                        for (i = 0; i < need_pages; i++) {
                                if (arena_mapbits_unzeroed_get(chunk, run_ind+i)
                                    != 0)
@@ -342,9 +472,6 @@ arena_run_split_large_helper(arena_t *arena, arena_run_t *run, size_t size,
                                            run_ind+i);
                                }
                        }
-               } else {
-                       /* The run is dirty, so all pages must be zeroed. */
-                       arena_run_zero(chunk, run_ind, need_pages);
                }
        } else {
                JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk +
@@ -355,31 +482,37 @@ arena_run_split_large_helper(arena_t *arena, arena_run_t *run, size_t size,
         * Set the last element first, in case the run only contains one page
         * (i.e. both statements set the same element).
         */
-       arena_mapbits_large_set(chunk, run_ind+need_pages-1, 0, flag_dirty);
-       arena_mapbits_large_set(chunk, run_ind, size, flag_dirty);
+       flag_unzeroed_mask = (flag_dirty | flag_decommitted) == 0 ?
+           CHUNK_MAP_UNZEROED : 0;
+       arena_mapbits_large_set(chunk, run_ind+need_pages-1, 0, flag_dirty |
+           (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk,
+           run_ind+need_pages-1)));
+       arena_mapbits_large_set(chunk, run_ind, size, flag_dirty |
+           (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk, run_ind)));
+       return (false);
 }
 
-static void
+static bool
 arena_run_split_large(arena_t *arena, arena_run_t *run, size_t size, bool zero)
 {
 
-       arena_run_split_large_helper(arena, run, size, true, zero);
+       return (arena_run_split_large_helper(arena, run, size, true, zero));
 }
 
-static void
+static bool
 arena_run_init_large(arena_t *arena, arena_run_t *run, size_t size, bool zero)
 {
 
-       arena_run_split_large_helper(arena, run, size, false, zero);
+       return (arena_run_split_large_helper(arena, run, size, false, zero));
 }
 
-static void
+static bool
 arena_run_split_small(arena_t *arena, arena_run_t *run, size_t size,
-    size_t binind)
+    szind_t binind)
 {
        arena_chunk_t *chunk;
        arena_chunk_map_misc_t *miscelm;
-       size_t flag_dirty, run_ind, need_pages, i;
+       size_t flag_dirty, flag_decommitted, run_ind, need_pages, i;
 
        assert(binind != BININD_INVALID);
 
@@ -387,33 +520,28 @@ arena_run_split_small(arena_t *arena, arena_run_t *run, size_t size,
        miscelm = arena_run_to_miscelm(run);
        run_ind = arena_miscelm_to_pageind(miscelm);
        flag_dirty = arena_mapbits_dirty_get(chunk, run_ind);
+       flag_decommitted = arena_mapbits_decommitted_get(chunk, run_ind);
        need_pages = (size >> LG_PAGE);
        assert(need_pages > 0);
 
-       arena_run_split_remove(arena, chunk, run_ind, flag_dirty, need_pages);
+       if (flag_decommitted != 0 && arena->chunk_hooks.commit(chunk, chunksize,
+           run_ind << LG_PAGE, size, arena->ind))
+               return (true);
 
-       /*
-        * Propagate the dirty and unzeroed flags to the allocated small run,
-        * so that arena_dalloc_bin_run() has the ability to conditionally trim
-        * clean pages.
-        */
-       arena_mapbits_small_set(chunk, run_ind, 0, binind, flag_dirty);
-       if (config_debug && flag_dirty == 0 && arena_mapbits_unzeroed_get(chunk,
-           run_ind) == 0)
-               arena_run_page_validate_zeroed(chunk, run_ind);
-       for (i = 1; i < need_pages - 1; i++) {
-               arena_mapbits_small_set(chunk, run_ind+i, i, binind, 0);
-               if (config_debug && flag_dirty == 0 &&
-                   arena_mapbits_unzeroed_get(chunk, run_ind+i) == 0)
+       arena_run_split_remove(arena, chunk, run_ind, flag_dirty,
+           flag_decommitted, need_pages);
+
+       for (i = 0; i < need_pages; i++) {
+               size_t flag_unzeroed = arena_mapbits_unzeroed_get(chunk,
+                   run_ind+i);
+               arena_mapbits_small_set(chunk, run_ind+i, i, binind,
+                   flag_unzeroed);
+               if (config_debug && flag_dirty == 0 && flag_unzeroed == 0)
                        arena_run_page_validate_zeroed(chunk, run_ind+i);
        }
-       arena_mapbits_small_set(chunk, run_ind+need_pages-1, need_pages-1,
-           binind, flag_dirty);
-       if (config_debug && flag_dirty == 0 && arena_mapbits_unzeroed_get(chunk,
-           run_ind+need_pages-1) == 0)
-               arena_run_page_validate_zeroed(chunk, run_ind+need_pages-1);
        JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk +
            (run_ind << LG_PAGE)), (need_pages << LG_PAGE));
+       return (false);
 }
 
 static arena_chunk_t *
@@ -429,97 +557,117 @@ arena_chunk_init_spare(arena_t *arena)
        assert(arena_mapbits_allocated_get(chunk, map_bias) == 0);
        assert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0);
        assert(arena_mapbits_unallocated_size_get(chunk, map_bias) ==
-           arena_maxclass);
+           arena_maxrun);
        assert(arena_mapbits_unallocated_size_get(chunk, chunk_npages-1) ==
-           arena_maxclass);
+           arena_maxrun);
        assert(arena_mapbits_dirty_get(chunk, map_bias) ==
            arena_mapbits_dirty_get(chunk, chunk_npages-1));
 
        return (chunk);
 }
 
+static bool
+arena_chunk_register(arena_t *arena, arena_chunk_t *chunk, bool zero)
+{
+
+       /*
+        * The extent node notion of "committed" doesn't directly apply to
+        * arena chunks.  Arbitrarily mark them as committed.  The commit state
+        * of runs is tracked individually, and upon chunk deallocation the
+        * entire chunk is in a consistent commit state.
+        */
+       extent_node_init(&chunk->node, arena, chunk, chunksize, zero, true);
+       extent_node_achunk_set(&chunk->node, true);
+       return (chunk_register(chunk, &chunk->node));
+}
+
 static arena_chunk_t *
-arena_chunk_alloc_internal(arena_t *arena, size_t size, size_t alignment,
-    bool *zero)
+arena_chunk_alloc_internal_hard(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    bool *zero, bool *commit)
 {
        arena_chunk_t *chunk;
-       chunk_alloc_t *chunk_alloc;
-       chunk_dalloc_t *chunk_dalloc;
 
-       chunk_alloc = arena->chunk_alloc;
-       chunk_dalloc = arena->chunk_dalloc;
        malloc_mutex_unlock(&arena->lock);
-       chunk = (arena_chunk_t *)chunk_alloc_arena(chunk_alloc, chunk_dalloc,
-           arena->ind, NULL, size, alignment, zero);
-       malloc_mutex_lock(&arena->lock);
-       if (config_stats && chunk != NULL)
-               arena->stats.mapped += chunksize;
 
+       chunk = (arena_chunk_t *)chunk_alloc_wrapper(arena, chunk_hooks, NULL,
+           chunksize, chunksize, zero, commit);
+       if (chunk != NULL && !*commit) {
+               /* Commit header. */
+               if (chunk_hooks->commit(chunk, chunksize, 0, map_bias <<
+                   LG_PAGE, arena->ind)) {
+                       chunk_dalloc_wrapper(arena, chunk_hooks,
+                           (void *)chunk, chunksize, *commit);
+                       chunk = NULL;
+               }
+       }
+       if (chunk != NULL && arena_chunk_register(arena, chunk, *zero)) {
+               if (!*commit) {
+                       /* Undo commit of header. */
+                       chunk_hooks->decommit(chunk, chunksize, 0, map_bias <<
+                           LG_PAGE, arena->ind);
+               }
+               chunk_dalloc_wrapper(arena, chunk_hooks, (void *)chunk,
+                   chunksize, *commit);
+               chunk = NULL;
+       }
+
+       malloc_mutex_lock(&arena->lock);
        return (chunk);
 }
 
-void *
-arena_chunk_alloc_huge(arena_t *arena, void *new_addr, size_t size,
-    size_t alignment, bool *zero)
+static arena_chunk_t *
+arena_chunk_alloc_internal(arena_t *arena, bool *zero, bool *commit)
 {
-       void *ret;
-       chunk_alloc_t *chunk_alloc;
-       chunk_dalloc_t *chunk_dalloc;
+       arena_chunk_t *chunk;
+       chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER;
 
-       malloc_mutex_lock(&arena->lock);
-       chunk_alloc = arena->chunk_alloc;
-       chunk_dalloc = arena->chunk_dalloc;
-       if (config_stats) {
-               /* Optimistically update stats prior to unlocking. */
-               arena->stats.mapped += size;
-               arena->stats.allocated_huge += size;
-               arena->stats.nmalloc_huge++;
-               arena->stats.nrequests_huge++;
+       chunk = chunk_alloc_cache(arena, &chunk_hooks, NULL, chunksize,
+           chunksize, zero, true);
+       if (chunk != NULL) {
+               if (arena_chunk_register(arena, chunk, *zero)) {
+                       chunk_dalloc_cache(arena, &chunk_hooks, chunk,
+                           chunksize, true);
+                       return (NULL);
+               }
+               *commit = true;
+       }
+       if (chunk == NULL) {
+               chunk = arena_chunk_alloc_internal_hard(arena, &chunk_hooks,
+                   zero, commit);
        }
-       arena->nactive += (size >> LG_PAGE);
-       malloc_mutex_unlock(&arena->lock);
 
-       ret = chunk_alloc_arena(chunk_alloc, chunk_dalloc, arena->ind,
-           new_addr, size, alignment, zero);
-       if (config_stats) {
-               if (ret != NULL)
-                       stats_cactive_add(size);
-               else {
-                       /* Revert optimistic stats updates. */
-                       malloc_mutex_lock(&arena->lock);
-                       arena->stats.mapped -= size;
-                       arena->stats.allocated_huge -= size;
-                       arena->stats.nmalloc_huge--;
-                       malloc_mutex_unlock(&arena->lock);
-               }
+       if (config_stats && chunk != NULL) {
+               arena->stats.mapped += chunksize;
+               arena->stats.metadata_mapped += (map_bias << LG_PAGE);
        }
 
-       return (ret);
+       return (chunk);
 }
 
 static arena_chunk_t *
 arena_chunk_init_hard(arena_t *arena)
 {
        arena_chunk_t *chunk;
-       bool zero;
-       size_t unzeroed, i;
+       bool zero, commit;
+       size_t flag_unzeroed, flag_decommitted, i;
 
        assert(arena->spare == NULL);
 
        zero = false;
-       chunk = arena_chunk_alloc_internal(arena, chunksize, chunksize, &zero);
+       commit = false;
+       chunk = arena_chunk_alloc_internal(arena, &zero, &commit);
        if (chunk == NULL)
                return (NULL);
 
-       chunk->arena = arena;
-
        /*
         * Initialize the map to contain one maximal free untouched run.  Mark
-        * the pages as zeroed iff chunk_alloc() returned a zeroed chunk.
+        * the pages as zeroed if chunk_alloc() returned a zeroed or decommitted
+        * chunk.
         */
-       unzeroed = zero ? 0 : CHUNK_MAP_UNZEROED;
-       arena_mapbits_unallocated_set(chunk, map_bias, arena_maxclass,
-           unzeroed);
+       flag_unzeroed = (zero || !commit) ? 0 : CHUNK_MAP_UNZEROED;
+       flag_decommitted = commit ? 0 : CHUNK_MAP_DECOMMITTED;
+       arena_mapbits_unallocated_set(chunk, map_bias, arena_maxrun,
+           flag_unzeroed | flag_decommitted);
        /*
         * There is no need to initialize the internal page map entries unless
         * the chunk is not zeroed.
@@ -531,7 +679,7 @@ arena_chunk_init_hard(arena_t *arena)
                    chunk_npages-1) - (uintptr_t)arena_bitselm_get(chunk,
                    map_bias+1)));
                for (i = map_bias+1; i < chunk_npages-1; i++)
-                       arena_mapbits_unzeroed_set(chunk, i, unzeroed);
+                       arena_mapbits_internal_set(chunk, i, flag_unzeroed);
        } else {
                JEMALLOC_VALGRIND_MAKE_MEM_DEFINED((void
                    *)arena_bitselm_get(chunk, map_bias+1), (size_t)((uintptr_t)
@@ -540,12 +688,12 @@ arena_chunk_init_hard(arena_t *arena)
                if (config_debug) {
                        for (i = map_bias+1; i < chunk_npages-1; i++) {
                                assert(arena_mapbits_unzeroed_get(chunk, i) ==
-                                   unzeroed);
+                                   flag_unzeroed);
                        }
                }
        }
-       arena_mapbits_unallocated_set(chunk, chunk_npages-1, arena_maxclass,
-           unzeroed);
+       arena_mapbits_unallocated_set(chunk, chunk_npages-1, arena_maxrun,
+           flag_unzeroed);
 
        return (chunk);
 }
@@ -569,37 +717,6 @@ arena_chunk_alloc(arena_t *arena)
        return (chunk);
 }
 
-static void
-arena_chunk_dalloc_internal(arena_t *arena, arena_chunk_t *chunk)
-{
-       chunk_dalloc_t *chunk_dalloc;
-
-       chunk_dalloc = arena->chunk_dalloc;
-       malloc_mutex_unlock(&arena->lock);
-       chunk_dalloc((void *)chunk, chunksize, arena->ind);
-       malloc_mutex_lock(&arena->lock);
-       if (config_stats)
-               arena->stats.mapped -= chunksize;
-}
-
-void
-arena_chunk_dalloc_huge(arena_t *arena, void *chunk, size_t size)
-{
-       chunk_dalloc_t *chunk_dalloc;
-
-       malloc_mutex_lock(&arena->lock);
-       chunk_dalloc = arena->chunk_dalloc;
-       if (config_stats) {
-               arena->stats.mapped -= size;
-               arena->stats.allocated_huge -= size;
-               arena->stats.ndalloc_huge++;
-               stats_cactive_sub(size);
-       }
-       arena->nactive -= (size >> LG_PAGE);
-       malloc_mutex_unlock(&arena->lock);
-       chunk_dalloc(chunk, size, arena->ind);
-}
-
 static void
 arena_chunk_dalloc(arena_t *arena, arena_chunk_t *chunk)
 {
@@ -607,11 +724,13 @@ arena_chunk_dalloc(arena_t *arena, arena_chunk_t *chunk)
        assert(arena_mapbits_allocated_get(chunk, map_bias) == 0);
        assert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0);
        assert(arena_mapbits_unallocated_size_get(chunk, map_bias) ==
-           arena_maxclass);
+           arena_maxrun);
        assert(arena_mapbits_unallocated_size_get(chunk, chunk_npages-1) ==
-           arena_maxclass);
+           arena_maxrun);
        assert(arena_mapbits_dirty_get(chunk, map_bias) ==
            arena_mapbits_dirty_get(chunk, chunk_npages-1));
+       assert(arena_mapbits_decommitted_get(chunk, map_bias) ==
+           arena_mapbits_decommitted_get(chunk, chunk_npages-1));
 
        /*
         * Remove run from the runs_avail tree, so that the arena does not use
@@ -621,32 +740,355 @@ arena_chunk_dalloc(arena_t *arena, arena_chunk_t *chunk)
 
        if (arena->spare != NULL) {
                arena_chunk_t *spare = arena->spare;
+               chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER;
+               bool committed;
 
                arena->spare = chunk;
                if (arena_mapbits_dirty_get(spare, map_bias) != 0) {
-                       arena_dirty_remove(arena, spare, map_bias,
+                       arena_run_dirty_remove(arena, spare, map_bias,
                            chunk_npages-map_bias);
                }
-               arena_chunk_dalloc_internal(arena, spare);
+
+               chunk_deregister(spare, &spare->node);
+
+               committed = (arena_mapbits_decommitted_get(spare, map_bias) ==
+                   0);
+               if (!committed) {
+                       /*
+                        * Decommit the header.  Mark the chunk as decommitted
+                        * even if header decommit fails, since treating a
+                        * partially committed chunk as committed has a high
+                        * potential for causing later access of decommitted
+                        * memory.
+                        */
+                       chunk_hooks = chunk_hooks_get(arena);
+                       chunk_hooks.decommit(spare, chunksize, 0, map_bias <<
+                           LG_PAGE, arena->ind);
+               }
+
+               chunk_dalloc_cache(arena, &chunk_hooks, (void *)spare,
+                   chunksize, committed);
+
+               if (config_stats) {
+                       arena->stats.mapped -= chunksize;
+                       arena->stats.metadata_mapped -= (map_bias << LG_PAGE);
+               }
        } else
                arena->spare = chunk;
 }
 
-static arena_run_t *
-arena_run_alloc_large_helper(arena_t *arena, size_t size, bool zero)
+static void
+arena_huge_malloc_stats_update(arena_t *arena, size_t usize)
 {
-       arena_chunk_map_misc_t *miscelm;
-       arena_chunk_map_misc_t *key;
+       szind_t index = size2index(usize) - nlclasses - NBINS;
 
-       key = (arena_chunk_map_misc_t *)(size | CHUNK_MAP_KEY);
-       miscelm = arena_avail_tree_nsearch(&arena->runs_avail, key);
-       if (miscelm != NULL) {
-               arena_run_t *run = &miscelm->run;
-               arena_run_split_large(arena, &miscelm->run, size, zero);
-               return (run);
+       cassert(config_stats);
+
+       arena->stats.nmalloc_huge++;
+       arena->stats.allocated_huge += usize;
+       arena->stats.hstats[index].nmalloc++;
+       arena->stats.hstats[index].curhchunks++;
+}
+
+static void
+arena_huge_malloc_stats_update_undo(arena_t *arena, size_t usize)
+{
+       szind_t index = size2index(usize) - nlclasses - NBINS;
+
+       cassert(config_stats);
+
+       arena->stats.nmalloc_huge--;
+       arena->stats.allocated_huge -= usize;
+       arena->stats.hstats[index].nmalloc--;
+       arena->stats.hstats[index].curhchunks--;
+}
+
+static void
+arena_huge_dalloc_stats_update(arena_t *arena, size_t usize)
+{
+       szind_t index = size2index(usize) - nlclasses - NBINS;
+
+       cassert(config_stats);
+
+       arena->stats.ndalloc_huge++;
+       arena->stats.allocated_huge -= usize;
+       arena->stats.hstats[index].ndalloc++;
+       arena->stats.hstats[index].curhchunks--;
+}
+
+static void
+arena_huge_dalloc_stats_update_undo(arena_t *arena, size_t usize)
+{
+       szind_t index = size2index(usize) - nlclasses - NBINS;
+
+       cassert(config_stats);
+
+       arena->stats.ndalloc_huge--;
+       arena->stats.allocated_huge += usize;
+       arena->stats.hstats[index].ndalloc--;
+       arena->stats.hstats[index].curhchunks++;
+}
+
+static void
+arena_huge_ralloc_stats_update(arena_t *arena, size_t oldsize, size_t usize)
+{
+
+       arena_huge_dalloc_stats_update(arena, oldsize);
+       arena_huge_malloc_stats_update(arena, usize);
+}
+
+static void
+arena_huge_ralloc_stats_update_undo(arena_t *arena, size_t oldsize,
+    size_t usize)
+{
+
+       arena_huge_dalloc_stats_update_undo(arena, oldsize);
+       arena_huge_malloc_stats_update_undo(arena, usize);
+}
+
+extent_node_t *
+arena_node_alloc(arena_t *arena)
+{
+       extent_node_t *node;
+
+       malloc_mutex_lock(&arena->node_cache_mtx);
+       node = ql_last(&arena->node_cache, ql_link);
+       if (node == NULL) {
+               malloc_mutex_unlock(&arena->node_cache_mtx);
+               return (base_alloc(sizeof(extent_node_t)));
+       }
+       ql_tail_remove(&arena->node_cache, extent_node_t, ql_link);
+       malloc_mutex_unlock(&arena->node_cache_mtx);
+       return (node);
+}
+
+void
+arena_node_dalloc(arena_t *arena, extent_node_t *node)
+{
+
+       malloc_mutex_lock(&arena->node_cache_mtx);
+       ql_elm_new(node, ql_link);
+       ql_tail_insert(&arena->node_cache, node, ql_link);
+       malloc_mutex_unlock(&arena->node_cache_mtx);
+}
+
+static void *
+arena_chunk_alloc_huge_hard(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    size_t usize, size_t alignment, bool *zero, size_t csize)
+{
+       void *ret;
+       bool commit = true;
+
+       ret = chunk_alloc_wrapper(arena, chunk_hooks, NULL, csize, alignment,
+           zero, &commit);
+       if (ret == NULL) {
+               /* Revert optimistic stats updates. */
+               malloc_mutex_lock(&arena->lock);
+               if (config_stats) {
+                       arena_huge_malloc_stats_update_undo(arena, usize);
+                       arena->stats.mapped -= usize;
+               }
+               arena->nactive -= (usize >> LG_PAGE);
+               malloc_mutex_unlock(&arena->lock);
        }
 
-       return (NULL);
+       return (ret);
+}
+
+void *
+arena_chunk_alloc_huge(arena_t *arena, size_t usize, size_t alignment,
+    bool *zero)
+{
+       void *ret;
+       chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER;
+       size_t csize = CHUNK_CEILING(usize);
+
+       malloc_mutex_lock(&arena->lock);
+
+       /* Optimistically update stats. */
+       if (config_stats) {
+               arena_huge_malloc_stats_update(arena, usize);
+               arena->stats.mapped += usize;
+       }
+       arena->nactive += (usize >> LG_PAGE);
+
+       ret = chunk_alloc_cache(arena, &chunk_hooks, NULL, csize, alignment,
+           zero, true);
+       malloc_mutex_unlock(&arena->lock);
+       if (ret == NULL) {
+               ret = arena_chunk_alloc_huge_hard(arena, &chunk_hooks, usize,
+                   alignment, zero, csize);
+       }
+
+       if (config_stats && ret != NULL)
+               stats_cactive_add(usize);
+       return (ret);
+}
+
+void
+arena_chunk_dalloc_huge(arena_t *arena, void *chunk, size_t usize)
+{
+       chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER;
+       size_t csize;
+
+       csize = CHUNK_CEILING(usize);
+       malloc_mutex_lock(&arena->lock);
+       if (config_stats) {
+               arena_huge_dalloc_stats_update(arena, usize);
+               arena->stats.mapped -= usize;
+               stats_cactive_sub(usize);
+       }
+       arena->nactive -= (usize >> LG_PAGE);
+
+       chunk_dalloc_cache(arena, &chunk_hooks, chunk, csize, true);
+       malloc_mutex_unlock(&arena->lock);
+}
+
+void
+arena_chunk_ralloc_huge_similar(arena_t *arena, void *chunk, size_t oldsize,
+    size_t usize)
+{
+
+       assert(CHUNK_CEILING(oldsize) == CHUNK_CEILING(usize));
+       assert(oldsize != usize);
+
+       malloc_mutex_lock(&arena->lock);
+       if (config_stats)
+               arena_huge_ralloc_stats_update(arena, oldsize, usize);
+       if (oldsize < usize) {
+               size_t udiff = usize - oldsize;
+               arena->nactive += udiff >> LG_PAGE;
+               if (config_stats)
+                       stats_cactive_add(udiff);
+       } else {
+               size_t udiff = oldsize - usize;
+               arena->nactive -= udiff >> LG_PAGE;
+               if (config_stats)
+                       stats_cactive_sub(udiff);
+       }
+       malloc_mutex_unlock(&arena->lock);
+}
+
+void
+arena_chunk_ralloc_huge_shrink(arena_t *arena, void *chunk, size_t oldsize,
+    size_t usize)
+{
+       size_t udiff = oldsize - usize;
+       size_t cdiff = CHUNK_CEILING(oldsize) - CHUNK_CEILING(usize);
+
+       malloc_mutex_lock(&arena->lock);
+       if (config_stats) {
+               arena_huge_ralloc_stats_update(arena, oldsize, usize);
+               if (cdiff != 0) {
+                       arena->stats.mapped -= cdiff;
+                       stats_cactive_sub(udiff);
+               }
+       }
+       arena->nactive -= udiff >> LG_PAGE;
+
+       if (cdiff != 0) {
+               chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER;
+               void *nchunk = (void *)((uintptr_t)chunk +
+                   CHUNK_CEILING(usize));
+
+               chunk_dalloc_cache(arena, &chunk_hooks, nchunk, cdiff, true);
+       }
+       malloc_mutex_unlock(&arena->lock);
+}
+
+static bool
+arena_chunk_ralloc_huge_expand_hard(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    void *chunk, size_t oldsize, size_t usize, bool *zero, void *nchunk,
+    size_t udiff, size_t cdiff)
+{
+       bool err;
+       bool commit = true;
+
+       err = (chunk_alloc_wrapper(arena, chunk_hooks, nchunk, cdiff, chunksize,
+           zero, &commit) == NULL);
+       if (err) {
+               /* Revert optimistic stats updates. */
+               malloc_mutex_lock(&arena->lock);
+               if (config_stats) {
+                       arena_huge_ralloc_stats_update_undo(arena, oldsize,
+                           usize);
+                       arena->stats.mapped -= cdiff;
+               }
+               arena->nactive -= (udiff >> LG_PAGE);
+               malloc_mutex_unlock(&arena->lock);
+       } else if (chunk_hooks->merge(chunk, CHUNK_CEILING(oldsize), nchunk,
+           cdiff, true, arena->ind)) {
+               chunk_dalloc_arena(arena, chunk_hooks, nchunk, cdiff, *zero,
+                   true);
+               err = true;
+       }
+       return (err);
+}
+
+bool
+arena_chunk_ralloc_huge_expand(arena_t *arena, void *chunk, size_t oldsize,
+    size_t usize, bool *zero)
+{
+       bool err;
+       chunk_hooks_t chunk_hooks = chunk_hooks_get(arena);
+       void *nchunk = (void *)((uintptr_t)chunk + CHUNK_CEILING(oldsize));
+       size_t udiff = usize - oldsize;
+       size_t cdiff = CHUNK_CEILING(usize) - CHUNK_CEILING(oldsize);
+
+       malloc_mutex_lock(&arena->lock);
+
+       /* Optimistically update stats. */
+       if (config_stats) {
+               arena_huge_ralloc_stats_update(arena, oldsize, usize);
+               arena->stats.mapped += cdiff;
+       }
+       arena->nactive += (udiff >> LG_PAGE);
+
+       err = (chunk_alloc_cache(arena, &arena->chunk_hooks, nchunk, cdiff,
+           chunksize, zero, true) == NULL);
+       malloc_mutex_unlock(&arena->lock);
+       if (err) {
+               err = arena_chunk_ralloc_huge_expand_hard(arena, &chunk_hooks,
+                   chunk, oldsize, usize, zero, nchunk, udiff,
+                   cdiff);
+       } else if (chunk_hooks.merge(chunk, CHUNK_CEILING(oldsize), nchunk,
+           cdiff, true, arena->ind)) {
+               chunk_dalloc_arena(arena, &chunk_hooks, nchunk, cdiff, *zero,
+                   true);
+               err = true;
+       }
+
+       if (config_stats && !err)
+               stats_cactive_add(udiff);
+       return (err);
+}
+
+/*
+ * Do first-best-fit run selection, i.e. select the lowest run that best fits.
+ * Run sizes are quantized, so not all candidate runs are necessarily exactly
+ * the same size.
+ */
+static arena_run_t *
+arena_run_first_best_fit(arena_t *arena, size_t size)
+{
+       size_t search_size = run_quantize_first(size);
+       arena_chunk_map_misc_t *key = arena_miscelm_key_create(search_size);
+       arena_chunk_map_misc_t *miscelm =
+           arena_avail_tree_nsearch(&arena->runs_avail, key);
+       if (miscelm == NULL)
+               return (NULL);
+       return (&miscelm->run);
+}
+
+static arena_run_t *
+arena_run_alloc_large_helper(arena_t *arena, size_t size, bool zero)
+{
+       arena_run_t *run = arena_run_first_best_fit(arena, s2u(size));
+       if (run != NULL) {
+               if (arena_run_split_large(arena, run, size, zero))
+                       run = NULL;
+       }
+       return (run);
 }
 
 static arena_run_t *
@@ -655,8 +1097,8 @@ arena_run_alloc_large(arena_t *arena, size_t size, bool zero)
        arena_chunk_t *chunk;
        arena_run_t *run;
 
-       assert(size <= arena_maxclass);
-       assert((size & PAGE_MASK) == 0);
+       assert(size <= arena_maxrun);
+       assert(size == PAGE_CEILING(size));
 
        /* Search the arena's chunks for the lowest best fit. */
        run = arena_run_alloc_large_helper(arena, size, zero);
@@ -669,7 +1111,8 @@ arena_run_alloc_large(arena_t *arena, size_t size, bool zero)
        chunk = arena_chunk_alloc(arena);
        if (chunk != NULL) {
                run = &arena_miscelm_get(chunk, map_bias)->run;
-               arena_run_split_large(arena, run, size, zero);
+               if (arena_run_split_large(arena, run, size, zero))
+                       run = NULL;
                return (run);
        }
 
@@ -682,31 +1125,24 @@ arena_run_alloc_large(arena_t *arena, size_t size, bool zero)
 }
 
 static arena_run_t *
-arena_run_alloc_small_helper(arena_t *arena, size_t size, size_t binind)
+arena_run_alloc_small_helper(arena_t *arena, size_t size, szind_t binind)
 {
-       arena_run_t *run;
-       arena_chunk_map_misc_t *miscelm;
-       arena_chunk_map_misc_t *key;
-
-       key = (arena_chunk_map_misc_t *)(size | CHUNK_MAP_KEY);
-       miscelm = arena_avail_tree_nsearch(&arena->runs_avail, key);
-       if (miscelm != NULL) {
-               run = &miscelm->run;
-               arena_run_split_small(arena, run, size, binind);
-               return (run);
+       arena_run_t *run = arena_run_first_best_fit(arena, size);
+       if (run != NULL) {
+               if (arena_run_split_small(arena, run, size, binind))
+                       run = NULL;
        }
-
-       return (NULL);
+       return (run);
 }
 
 static arena_run_t *
-arena_run_alloc_small(arena_t *arena, size_t size, size_t binind)
+arena_run_alloc_small(arena_t *arena, size_t size, szind_t binind)
 {
        arena_chunk_t *chunk;
        arena_run_t *run;
 
-       assert(size <= arena_maxclass);
-       assert((size & PAGE_MASK) == 0);
+       assert(size <= arena_maxrun);
+       assert(size == PAGE_CEILING(size));
        assert(binind != BININD_INVALID);
 
        /* Search the arena's chunks for the lowest best fit. */
@@ -720,7 +1156,8 @@ arena_run_alloc_small(arena_t *arena, size_t size, size_t binind)
        chunk = arena_chunk_alloc(arena);
        if (chunk != NULL) {
                run = &arena_miscelm_get(chunk, map_bias)->run;
-               arena_run_split_small(arena, run, size, binind);
+               if (arena_run_split_small(arena, run, size, binind))
+                       run = NULL;
                return (run);
        }
 
@@ -732,45 +1169,101 @@ arena_run_alloc_small(arena_t *arena, size_t size, size_t binind)
        return (arena_run_alloc_small_helper(arena, size, binind));
 }
 
-static inline void
+static bool
+arena_lg_dirty_mult_valid(ssize_t lg_dirty_mult)
+{
+
+       return (lg_dirty_mult >= -1 && lg_dirty_mult < (ssize_t)(sizeof(size_t)
+           << 3));
+}
+
+ssize_t
+arena_lg_dirty_mult_get(arena_t *arena)
+{
+       ssize_t lg_dirty_mult;
+
+       malloc_mutex_lock(&arena->lock);
+       lg_dirty_mult = arena->lg_dirty_mult;
+       malloc_mutex_unlock(&arena->lock);
+
+       return (lg_dirty_mult);
+}
+
+bool
+arena_lg_dirty_mult_set(arena_t *arena, ssize_t lg_dirty_mult)
+{
+
+       if (!arena_lg_dirty_mult_valid(lg_dirty_mult))
+               return (true);
+
+       malloc_mutex_lock(&arena->lock);
+       arena->lg_dirty_mult = lg_dirty_mult;
+       arena_maybe_purge(arena);
+       malloc_mutex_unlock(&arena->lock);
+
+       return (false);
+}
+
+void
 arena_maybe_purge(arena_t *arena)
 {
-       size_t threshold;
 
        /* Don't purge if the option is disabled. */
-       if (opt_lg_dirty_mult < 0)
+       if (arena->lg_dirty_mult < 0)
+               return;
+       /* Don't recursively purge. */
+       if (arena->purging)
                return;
-       threshold = (arena->nactive >> opt_lg_dirty_mult);
        /*
-        * Don't purge unless the number of purgeable pages exceeds the
-        * threshold.
+        * Iterate, since preventing recursive purging could otherwise leave too
+        * many dirty pages.
         */
-       if (arena->ndirty <= threshold)
-               return;
-
-       arena_purge(arena, false);
+       while (true) {
+               size_t threshold = (arena->nactive >> arena->lg_dirty_mult);
+               if (threshold < chunk_npages)
+                       threshold = chunk_npages;
+               /*
+                * Don't purge unless the number of purgeable pages exceeds the
+                * threshold.
+                */
+               if (arena->ndirty <= threshold)
+                       return;
+               arena_purge(arena, false);
+       }
 }
 
 static size_t
 arena_dirty_count(arena_t *arena)
 {
        size_t ndirty = 0;
-       arena_chunk_map_misc_t *miscelm;
-       arena_chunk_t *chunk;
-       size_t pageind, npages;
-
-       ql_foreach(miscelm, &arena->runs_dirty, dr_link) {
-               chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(miscelm);
-               pageind = arena_miscelm_to_pageind(miscelm);
-               assert(arena_mapbits_allocated_get(chunk, pageind) == 0);
-               assert(arena_mapbits_large_get(chunk, pageind) == 0);
-               assert(arena_mapbits_dirty_get(chunk, pageind) != 0);
-               npages = arena_mapbits_unallocated_size_get(chunk, pageind) >>
-                   LG_PAGE;
+       arena_runs_dirty_link_t *rdelm;
+       extent_node_t *chunkselm;
+
+       for (rdelm = qr_next(&arena->runs_dirty, rd_link),
+           chunkselm = qr_next(&arena->chunks_cache, cc_link);
+           rdelm != &arena->runs_dirty; rdelm = qr_next(rdelm, rd_link)) {
+               size_t npages;
+
+               if (rdelm == &chunkselm->rd) {
+                       npages = extent_node_size_get(chunkselm) >> LG_PAGE;
+                       chunkselm = qr_next(chunkselm, cc_link);
+               } else {
+                       arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(
+                           rdelm);
+                       arena_chunk_map_misc_t *miscelm =
+                           arena_rd_to_miscelm(rdelm);
+                       size_t pageind = arena_miscelm_to_pageind(miscelm);
+                       assert(arena_mapbits_allocated_get(chunk, pageind) ==
+                           0);
+                       assert(arena_mapbits_large_get(chunk, pageind) == 0);
+                       assert(arena_mapbits_dirty_get(chunk, pageind) != 0);
+                       npages = arena_mapbits_unallocated_size_get(chunk,
+                           pageind) >> LG_PAGE;
+               }
                ndirty += npages;
        }
 
-       return (ndirty);
+       return (ndirty);
 }
 
 static size_t
@@ -783,7 +1276,8 @@ arena_compute_npurge(arena_t *arena, bool all)
         * purge.
         */
        if (!all) {
-               size_t threshold = (arena->nactive >> opt_lg_dirty_mult);
+               size_t threshold = (arena->nactive >> arena->lg_dirty_mult);
+               threshold = threshold < chunk_npages ? chunk_npages : threshold;
 
                npurge = arena->ndirty - threshold;
        } else
@@ -793,42 +1287,78 @@ arena_compute_npurge(arena_t *arena, bool all)
 }
 
 static size_t
-arena_stash_dirty(arena_t *arena, bool all, size_t npurge,
-    arena_chunk_miscelms_t *miscelms)
+arena_stash_dirty(arena_t *arena, chunk_hooks_t *chunk_hooks, bool all,
+    size_t npurge, arena_runs_dirty_link_t *purge_runs_sentinel,
+    extent_node_t *purge_chunks_sentinel)
 {
-       arena_chunk_map_misc_t *miscelm;
+       arena_runs_dirty_link_t *rdelm, *rdelm_next;
+       extent_node_t *chunkselm;
        size_t nstashed = 0;
 
-       /* Add at least npurge pages to purge_list. */
-       for (miscelm = ql_first(&arena->runs_dirty); miscelm != NULL;
-           miscelm = ql_first(&arena->runs_dirty)) {
-               arena_chunk_t *chunk =
-                   (arena_chunk_t *)CHUNK_ADDR2BASE(miscelm);
-               size_t pageind = arena_miscelm_to_pageind(miscelm);
-               size_t run_size = arena_mapbits_unallocated_size_get(chunk,
-                   pageind);
-               size_t npages = run_size >> LG_PAGE;
-               arena_run_t *run = &miscelm->run;
-
-               assert(pageind + npages <= chunk_npages);
-               assert(arena_mapbits_dirty_get(chunk, pageind) ==
-                   arena_mapbits_dirty_get(chunk, pageind+npages-1));
+       /* Stash at least npurge pages. */
+       for (rdelm = qr_next(&arena->runs_dirty, rd_link),
+           chunkselm = qr_next(&arena->chunks_cache, cc_link);
+           rdelm != &arena->runs_dirty; rdelm = rdelm_next) {
+               size_t npages;
+               rdelm_next = qr_next(rdelm, rd_link);
 
-               /*
-                * If purging the spare chunk's run, make it available prior to
-                * allocation.
-                */
-               if (chunk == arena->spare)
-                       arena_chunk_alloc(arena);
+               if (rdelm == &chunkselm->rd) {
+                       extent_node_t *chunkselm_next;
+                       bool zero;
+                       UNUSED void *chunk;
+
+                       chunkselm_next = qr_next(chunkselm, cc_link);
+                       /*
+                        * Allocate.  chunkselm remains valid due to the
+                        * dalloc_node=false argument to chunk_alloc_cache().
+                        */
+                       zero = false;
+                       chunk = chunk_alloc_cache(arena, chunk_hooks,
+                           extent_node_addr_get(chunkselm),
+                           extent_node_size_get(chunkselm), chunksize, &zero,
+                           false);
+                       assert(chunk == extent_node_addr_get(chunkselm));
+                       assert(zero == extent_node_zeroed_get(chunkselm));
+                       extent_node_dirty_insert(chunkselm, purge_runs_sentinel,
+                           purge_chunks_sentinel);
+                       npages = extent_node_size_get(chunkselm) >> LG_PAGE;
+                       chunkselm = chunkselm_next;
+               } else {
+                       arena_chunk_t *chunk =
+                           (arena_chunk_t *)CHUNK_ADDR2BASE(rdelm);
+                       arena_chunk_map_misc_t *miscelm =
+                           arena_rd_to_miscelm(rdelm);
+                       size_t pageind = arena_miscelm_to_pageind(miscelm);
+                       arena_run_t *run = &miscelm->run;
+                       size_t run_size =
+                           arena_mapbits_unallocated_size_get(chunk, pageind);
 
-               /* Temporarily allocate the free dirty run. */
-               arena_run_split_large(arena, run, run_size, false);
-               /* Append to purge_list for later processing. */
-               ql_elm_new(miscelm, dr_link);
-               ql_tail_insert(miscelms, miscelm, dr_link);
+                       npages = run_size >> LG_PAGE;
 
-               nstashed += npages;
+                       assert(pageind + npages <= chunk_npages);
+                       assert(arena_mapbits_dirty_get(chunk, pageind) ==
+                           arena_mapbits_dirty_get(chunk, pageind+npages-1));
+
+                       /*
+                        * If purging the spare chunk's run, make it available
+                        * prior to allocation.
+                        */
+                       if (chunk == arena->spare)
+                               arena_chunk_alloc(arena);
+
+                       /* Temporarily allocate the free dirty run. */
+                       arena_run_split_large(arena, run, run_size, false);
+                       /* Stash. */
+                       if (false)
+                               qr_new(rdelm, rd_link); /* Redundant. */
+                       else {
+                               assert(qr_next(rdelm, rd_link) == rdelm);
+                               assert(qr_prev(rdelm, rd_link) == rdelm);
+                       }
+                       qr_meld(purge_runs_sentinel, rdelm, rd_link);
+               }
 
+               nstashed += npages;
                if (!all && nstashed >= npurge)
                        break;
        }
@@ -837,52 +1367,88 @@ arena_stash_dirty(arena_t *arena, bool all, size_t npurge,
 }
 
 static size_t
-arena_purge_stashed(arena_t *arena, arena_chunk_miscelms_t *miscelms)
+arena_purge_stashed(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    arena_runs_dirty_link_t *purge_runs_sentinel,
+    extent_node_t *purge_chunks_sentinel)
 {
        size_t npurged, nmadvise;
-       arena_chunk_map_misc_t *miscelm;
+       arena_runs_dirty_link_t *rdelm;
+       extent_node_t *chunkselm;
 
        if (config_stats)
                nmadvise = 0;
        npurged = 0;
 
        malloc_mutex_unlock(&arena->lock);
+       for (rdelm = qr_next(purge_runs_sentinel, rd_link),
+           chunkselm = qr_next(purge_chunks_sentinel, cc_link);
+           rdelm != purge_runs_sentinel; rdelm = qr_next(rdelm, rd_link)) {
+               size_t npages;
 
-       ql_foreach(miscelm, miscelms, dr_link) {
-               arena_chunk_t *chunk;
-               size_t pageind, run_size, npages, flag_unzeroed, i;
-               bool unzeroed;
-
-               chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(miscelm);
-               pageind = arena_miscelm_to_pageind(miscelm);
-               run_size = arena_mapbits_large_size_get(chunk, pageind);
-               npages = run_size >> LG_PAGE;
-
-               assert(pageind + npages <= chunk_npages);
-               unzeroed = pages_purge((void *)((uintptr_t)chunk + (pageind <<
-                   LG_PAGE)), run_size);
-               flag_unzeroed = unzeroed ? CHUNK_MAP_UNZEROED : 0;
+               if (rdelm == &chunkselm->rd) {
+                       /*
+                        * Don't actually purge the chunk here because 1)
+                        * chunkselm is embedded in the chunk and must remain
+                        * valid, and 2) we deallocate the chunk in
+                        * arena_unstash_purged(), where it is destroyed,
+                        * decommitted, or purged, depending on chunk
+                        * deallocation policy.
+                        */
+                       size_t size = extent_node_size_get(chunkselm);
+                       npages = size >> LG_PAGE;
+                       chunkselm = qr_next(chunkselm, cc_link);
+               } else {
+                       size_t pageind, run_size, flag_unzeroed, flags, i;
+                       bool decommitted;
+                       arena_chunk_t *chunk =
+                           (arena_chunk_t *)CHUNK_ADDR2BASE(rdelm);
+                       arena_chunk_map_misc_t *miscelm =
+                           arena_rd_to_miscelm(rdelm);
+                       pageind = arena_miscelm_to_pageind(miscelm);
+                       run_size = arena_mapbits_large_size_get(chunk, pageind);
+                       npages = run_size >> LG_PAGE;
+
+                       assert(pageind + npages <= chunk_npages);
+                       assert(!arena_mapbits_decommitted_get(chunk, pageind));
+                       assert(!arena_mapbits_decommitted_get(chunk,
+                           pageind+npages-1));
+                       decommitted = !chunk_hooks->decommit(chunk, chunksize,
+                           pageind << LG_PAGE, npages << LG_PAGE, arena->ind);
+                       if (decommitted) {
+                               flag_unzeroed = 0;
+                               flags = CHUNK_MAP_DECOMMITTED;
+                       } else {
+                               flag_unzeroed = chunk_purge_wrapper(arena,
+                                   chunk_hooks, chunk, chunksize, pageind <<
+                                   LG_PAGE, run_size) ? CHUNK_MAP_UNZEROED : 0;
+                               flags = flag_unzeroed;
+                       }
+                       arena_mapbits_large_set(chunk, pageind+npages-1, 0,
+                           flags);
+                       arena_mapbits_large_set(chunk, pageind, run_size,
+                           flags);
 
-               /*
-                * Set the unzeroed flag for all pages, now that pages_purge()
-                * has returned whether the pages were zeroed as a side effect
-                * of purging.  This chunk map modification is safe even though
-                * the arena mutex isn't currently owned by this thread,
-                * because the run is marked as allocated, thus protecting it
-                * from being modified by any other thread.  As long as these
-                * writes don't perturb the first and last elements'
-                * CHUNK_MAP_ALLOCATED bits, behavior is well defined.
-                */
-               for (i = 0; i < npages; i++) {
-                       arena_mapbits_unzeroed_set(chunk, pageind+i,
-                           flag_unzeroed);
+                       /*
+                        * Set the unzeroed flag for internal pages, now that
+                        * chunk_purge_wrapper() has returned whether the pages
+                        * were zeroed as a side effect of purging.  This chunk
+                        * map modification is safe even though the arena mutex
+                        * isn't currently owned by this thread, because the run
+                        * is marked as allocated, thus protecting it from being
+                        * modified by any other thread.  As long as these
+                        * writes don't perturb the first and last elements'
+                        * CHUNK_MAP_ALLOCATED bits, behavior is well defined.
+                        */
+                       for (i = 1; i < npages-1; i++) {
+                               arena_mapbits_internal_set(chunk, pageind+i,
+                                   flag_unzeroed);
+                       }
                }
 
                npurged += npages;
                if (config_stats)
                        nmadvise++;
        }
-
        malloc_mutex_lock(&arena->lock);
 
        if (config_stats) {
@@ -894,41 +1460,82 @@ arena_purge_stashed(arena_t *arena, arena_chunk_miscelms_t *miscelms)
 }
 
 static void
-arena_unstash_purged(arena_t *arena, arena_chunk_miscelms_t *miscelms)
-{
-       arena_chunk_map_misc_t *miscelm;
-
-       /* Deallocate runs. */
-       for (miscelm = ql_first(miscelms); miscelm != NULL;
-           miscelm = ql_first(miscelms)) {
-               arena_run_t *run = &miscelm->run;
-               ql_remove(miscelms, miscelm, dr_link);
-               arena_run_dalloc(arena, run, false, true);
+arena_unstash_purged(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    arena_runs_dirty_link_t *purge_runs_sentinel,
+    extent_node_t *purge_chunks_sentinel)
+{
+       arena_runs_dirty_link_t *rdelm, *rdelm_next;
+       extent_node_t *chunkselm;
+
+       /* Deallocate chunks/runs. */
+       for (rdelm = qr_next(purge_runs_sentinel, rd_link),
+           chunkselm = qr_next(purge_chunks_sentinel, cc_link);
+           rdelm != purge_runs_sentinel; rdelm = rdelm_next) {
+               rdelm_next = qr_next(rdelm, rd_link);
+               if (rdelm == &chunkselm->rd) {
+                       extent_node_t *chunkselm_next = qr_next(chunkselm,
+                           cc_link);
+                       void *addr = extent_node_addr_get(chunkselm);
+                       size_t size = extent_node_size_get(chunkselm);
+                       bool zeroed = extent_node_zeroed_get(chunkselm);
+                       bool committed = extent_node_committed_get(chunkselm);
+                       extent_node_dirty_remove(chunkselm);
+                       arena_node_dalloc(arena, chunkselm);
+                       chunkselm = chunkselm_next;
+                       chunk_dalloc_arena(arena, chunk_hooks, addr, size,
+                           zeroed, committed);
+               } else {
+                       arena_chunk_t *chunk =
+                           (arena_chunk_t *)CHUNK_ADDR2BASE(rdelm);
+                       arena_chunk_map_misc_t *miscelm =
+                           arena_rd_to_miscelm(rdelm);
+                       size_t pageind = arena_miscelm_to_pageind(miscelm);
+                       bool decommitted = (arena_mapbits_decommitted_get(chunk,
+                           pageind) != 0);
+                       arena_run_t *run = &miscelm->run;
+                       qr_remove(rdelm, rd_link);
+                       arena_run_dalloc(arena, run, false, true, decommitted);
+               }
        }
 }
 
-void
+static void
 arena_purge(arena_t *arena, bool all)
 {
+       chunk_hooks_t chunk_hooks = chunk_hooks_get(arena);
        size_t npurge, npurgeable, npurged;
-       arena_chunk_miscelms_t purge_list;
+       arena_runs_dirty_link_t purge_runs_sentinel;
+       extent_node_t purge_chunks_sentinel;
 
-       if (config_debug) {
+       arena->purging = true;
+
+       /*
+        * Calls to arena_dirty_count() are disabled even for debug builds
+        * because overhead grows nonlinearly as memory usage increases.
+        */
+       if (false && config_debug) {
                size_t ndirty = arena_dirty_count(arena);
                assert(ndirty == arena->ndirty);
        }
-       assert((arena->nactive >> opt_lg_dirty_mult) < arena->ndirty || all);
+       assert((arena->nactive >> arena->lg_dirty_mult) < arena->ndirty || all);
 
        if (config_stats)
                arena->stats.npurge++;
 
        npurge = arena_compute_npurge(arena, all);
-       ql_new(&purge_list);
-       npurgeable = arena_stash_dirty(arena, all, npurge, &purge_list);
+       qr_new(&purge_runs_sentinel, rd_link);
+       extent_node_dirty_linkage_init(&purge_chunks_sentinel);
+
+       npurgeable = arena_stash_dirty(arena, &chunk_hooks, all, npurge,
+           &purge_runs_sentinel, &purge_chunks_sentinel);
        assert(npurgeable >= npurge);
-       npurged = arena_purge_stashed(arena, &purge_list);
+       npurged = arena_purge_stashed(arena, &chunk_hooks, &purge_runs_sentinel,
+           &purge_chunks_sentinel);
        assert(npurged == npurgeable);
-       arena_unstash_purged(arena, &purge_list);
+       arena_unstash_purged(arena, &chunk_hooks, &purge_runs_sentinel,
+           &purge_chunks_sentinel);
+
+       arena->purging = false;
 }
 
 void
@@ -942,7 +1549,8 @@ arena_purge_all(arena_t *arena)
 
 static void
 arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size,
-    size_t *p_run_ind, size_t *p_run_pages, size_t flag_dirty)
+    size_t *p_run_ind, size_t *p_run_pages, size_t flag_dirty,
+    size_t flag_decommitted)
 {
        size_t size = *p_size;
        size_t run_ind = *p_run_ind;
@@ -951,7 +1559,9 @@ arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size,
        /* Try to coalesce forward. */
        if (run_ind + run_pages < chunk_npages &&
            arena_mapbits_allocated_get(chunk, run_ind+run_pages) == 0 &&
-           arena_mapbits_dirty_get(chunk, run_ind+run_pages) == flag_dirty) {
+           arena_mapbits_dirty_get(chunk, run_ind+run_pages) == flag_dirty &&
+           arena_mapbits_decommitted_get(chunk, run_ind+run_pages) ==
+           flag_decommitted) {
                size_t nrun_size = arena_mapbits_unallocated_size_get(chunk,
                    run_ind+run_pages);
                size_t nrun_pages = nrun_size >> LG_PAGE;
@@ -964,11 +1574,16 @@ arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size,
                    run_ind+run_pages+nrun_pages-1) == nrun_size);
                assert(arena_mapbits_dirty_get(chunk,
                    run_ind+run_pages+nrun_pages-1) == flag_dirty);
+               assert(arena_mapbits_decommitted_get(chunk,
+                   run_ind+run_pages+nrun_pages-1) == flag_decommitted);
                arena_avail_remove(arena, chunk, run_ind+run_pages, nrun_pages);
 
-               /* If the successor is dirty, remove it from runs_dirty. */
+               /*
+                * If the successor is dirty, remove it from the set of dirty
+                * pages.
+                */
                if (flag_dirty != 0) {
-                       arena_dirty_remove(arena, chunk, run_ind+run_pages,
+                       arena_run_dirty_remove(arena, chunk, run_ind+run_pages,
                            nrun_pages);
                }
 
@@ -983,7 +1598,8 @@ arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size,
        /* Try to coalesce backward. */
        if (run_ind > map_bias && arena_mapbits_allocated_get(chunk,
            run_ind-1) == 0 && arena_mapbits_dirty_get(chunk, run_ind-1) ==
-           flag_dirty) {
+           flag_dirty && arena_mapbits_decommitted_get(chunk, run_ind-1) ==
+           flag_decommitted) {
                size_t prun_size = arena_mapbits_unallocated_size_get(chunk,
                    run_ind-1);
                size_t prun_pages = prun_size >> LG_PAGE;
@@ -997,11 +1613,18 @@ arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size,
                assert(arena_mapbits_unallocated_size_get(chunk, run_ind) ==
                    prun_size);
                assert(arena_mapbits_dirty_get(chunk, run_ind) == flag_dirty);
+               assert(arena_mapbits_decommitted_get(chunk, run_ind) ==
+                   flag_decommitted);
                arena_avail_remove(arena, chunk, run_ind, prun_pages);
 
-               /* If the predecessor is dirty, remove it from runs_dirty. */
-               if (flag_dirty != 0)
-                       arena_dirty_remove(arena, chunk, run_ind, prun_pages);
+               /*
+                * If the predecessor is dirty, remove it from the set of dirty
+                * pages.
+                */
+               if (flag_dirty != 0) {
+                       arena_run_dirty_remove(arena, chunk, run_ind,
+                           prun_pages);
+               }
 
                size += prun_size;
                run_pages += prun_pages;
@@ -1016,28 +1639,53 @@ arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size,
        *p_run_pages = run_pages;
 }
 
-static void
-arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned)
+static size_t
+arena_run_size_get(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
+    size_t run_ind)
 {
-       arena_chunk_t *chunk;
-       arena_chunk_map_misc_t *miscelm;
-       size_t size, run_ind, run_pages, flag_dirty;
+       size_t size;
 
-       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
-       miscelm = arena_run_to_miscelm(run);
-       run_ind = arena_miscelm_to_pageind(miscelm);
        assert(run_ind >= map_bias);
        assert(run_ind < chunk_npages);
+
        if (arena_mapbits_large_get(chunk, run_ind) != 0) {
                size = arena_mapbits_large_size_get(chunk, run_ind);
-               assert(size == PAGE ||
-                   arena_mapbits_large_size_get(chunk,
+               assert(size == PAGE || arena_mapbits_large_size_get(chunk,
                    run_ind+(size>>LG_PAGE)-1) == 0);
        } else {
-               size_t binind = arena_bin_index(arena, run->bin);
-               arena_bin_info_t *bin_info = &arena_bin_info[binind];
+               arena_bin_info_t *bin_info = &arena_bin_info[run->binind];
                size = bin_info->run_size;
        }
+
+       return (size);
+}
+
+static bool
+arena_run_decommit(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run)
+{
+       arena_chunk_map_misc_t *miscelm = arena_run_to_miscelm(run);
+       size_t run_ind = arena_miscelm_to_pageind(miscelm);
+       size_t offset = run_ind << LG_PAGE;
+       size_t length = arena_run_size_get(arena, chunk, run, run_ind);
+
+       return (arena->chunk_hooks.decommit(chunk, chunksize, offset, length,
+           arena->ind));
+}
+
+static void
+arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned,
+    bool decommitted)
+{
+       arena_chunk_t *chunk;
+       arena_chunk_map_misc_t *miscelm;
+       size_t size, run_ind, run_pages, flag_dirty, flag_decommitted;
+
+       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
+       miscelm = arena_run_to_miscelm(run);
+       run_ind = arena_miscelm_to_pageind(miscelm);
+       assert(run_ind >= map_bias);
+       assert(run_ind < chunk_npages);
+       size = arena_run_size_get(arena, chunk, run, run_ind);
        run_pages = (size >> LG_PAGE);
        arena_cactive_update(arena, 0, run_pages);
        arena->nactive -= run_pages;
@@ -1049,16 +1697,18 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned)
         */
        assert(arena_mapbits_dirty_get(chunk, run_ind) ==
            arena_mapbits_dirty_get(chunk, run_ind+run_pages-1));
-       if (!cleaned && arena_mapbits_dirty_get(chunk, run_ind) != 0)
+       if (!cleaned && !decommitted && arena_mapbits_dirty_get(chunk, run_ind)
+           != 0)
                dirty = true;
        flag_dirty = dirty ? CHUNK_MAP_DIRTY : 0;
+       flag_decommitted = decommitted ? CHUNK_MAP_DECOMMITTED : 0;
 
        /* Mark pages as unallocated in the chunk map. */
-       if (dirty) {
-               arena_mapbits_unallocated_set(chunk, run_ind, size,
-                   CHUNK_MAP_DIRTY);
+       if (dirty || decommitted) {
+               size_t flags = flag_dirty | flag_decommitted;
+               arena_mapbits_unallocated_set(chunk, run_ind, size, flags);
                arena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size,
-                   CHUNK_MAP_DIRTY);
+                   flags);
        } else {
                arena_mapbits_unallocated_set(chunk, run_ind, size,
                    arena_mapbits_unzeroed_get(chunk, run_ind));
@@ -1066,22 +1716,25 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned)
                    arena_mapbits_unzeroed_get(chunk, run_ind+run_pages-1));
        }
 
-       arena_run_coalesce(arena, chunk, &size, &run_ind, &run_pages, flag_dirty);
+       arena_run_coalesce(arena, chunk, &size, &run_ind, &run_pages,
+           flag_dirty, flag_decommitted);
 
        /* Insert into runs_avail, now that coalescing is complete. */
        assert(arena_mapbits_unallocated_size_get(chunk, run_ind) ==
            arena_mapbits_unallocated_size_get(chunk, run_ind+run_pages-1));
        assert(arena_mapbits_dirty_get(chunk, run_ind) ==
            arena_mapbits_dirty_get(chunk, run_ind+run_pages-1));
+       assert(arena_mapbits_decommitted_get(chunk, run_ind) ==
+           arena_mapbits_decommitted_get(chunk, run_ind+run_pages-1));
        arena_avail_insert(arena, chunk, run_ind, run_pages);
 
        if (dirty)
-               arena_dirty_insert(arena, chunk, run_ind, run_pages);
+               arena_run_dirty_insert(arena, chunk, run_ind, run_pages);
 
        /* Deallocate chunk if it is now completely unused. */
-       if (size == arena_maxclass) {
+       if (size == arena_maxrun) {
                assert(run_ind == map_bias);
-               assert(run_pages == (arena_maxclass >> LG_PAGE));
+               assert(run_pages == (arena_maxrun >> LG_PAGE));
                arena_chunk_dalloc(arena, chunk);
        }
 
@@ -1096,6 +1749,15 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned)
                arena_maybe_purge(arena);
 }
 
+static void
+arena_run_dalloc_decommit(arena_t *arena, arena_chunk_t *chunk,
+    arena_run_t *run)
+{
+       bool committed = arena_run_decommit(arena, chunk, run);
+
+       arena_run_dalloc(arena, run, committed, false, !committed);
+}
+
 static void
 arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
     size_t oldsize, size_t newsize)
@@ -1104,6 +1766,9 @@ arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
        size_t pageind = arena_miscelm_to_pageind(miscelm);
        size_t head_npages = (oldsize - newsize) >> LG_PAGE;
        size_t flag_dirty = arena_mapbits_dirty_get(chunk, pageind);
+       size_t flag_decommitted = arena_mapbits_decommitted_get(chunk, pageind);
+       size_t flag_unzeroed_mask = (flag_dirty | flag_decommitted) == 0 ?
+           CHUNK_MAP_UNZEROED : 0;
 
        assert(oldsize > newsize);
 
@@ -1113,8 +1778,11 @@ arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
         * run first, in case of single-page runs.
         */
        assert(arena_mapbits_large_size_get(chunk, pageind) == oldsize);
-       arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty);
-       arena_mapbits_large_set(chunk, pageind, oldsize-newsize, flag_dirty);
+       arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty |
+           (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk,
+           pageind+head_npages-1)));
+       arena_mapbits_large_set(chunk, pageind, oldsize-newsize, flag_dirty |
+           (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk, pageind)));
 
        if (config_debug) {
                UNUSED size_t tail_npages = newsize >> LG_PAGE;
@@ -1124,9 +1792,10 @@ arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
                    pageind+head_npages+tail_npages-1) == flag_dirty);
        }
        arena_mapbits_large_set(chunk, pageind+head_npages, newsize,
-           flag_dirty);
+           flag_dirty | (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk,
+           pageind+head_npages)));
 
-       arena_run_dalloc(arena, run, false, false);
+       arena_run_dalloc(arena, run, false, false, (flag_decommitted != 0));
 }
 
 static void
@@ -1137,6 +1806,9 @@ arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
        size_t pageind = arena_miscelm_to_pageind(miscelm);
        size_t head_npages = newsize >> LG_PAGE;
        size_t flag_dirty = arena_mapbits_dirty_get(chunk, pageind);
+       size_t flag_decommitted = arena_mapbits_decommitted_get(chunk, pageind);
+       size_t flag_unzeroed_mask = (flag_dirty | flag_decommitted) == 0 ?
+           CHUNK_MAP_UNZEROED : 0;
        arena_chunk_map_misc_t *tail_miscelm;
        arena_run_t *tail_run;
 
@@ -1148,8 +1820,11 @@ arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
         * run first, in case of single-page runs.
         */
        assert(arena_mapbits_large_size_get(chunk, pageind) == oldsize);
-       arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty);
-       arena_mapbits_large_set(chunk, pageind, newsize, flag_dirty);
+       arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty |
+           (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk,
+           pageind+head_npages-1)));
+       arena_mapbits_large_set(chunk, pageind, newsize, flag_dirty |
+           (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk, pageind)));
 
        if (config_debug) {
                UNUSED size_t tail_npages = (oldsize - newsize) >> LG_PAGE;
@@ -1159,11 +1834,13 @@ arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
                    pageind+head_npages+tail_npages-1) == flag_dirty);
        }
        arena_mapbits_large_set(chunk, pageind+head_npages, oldsize-newsize,
-           flag_dirty);
+           flag_dirty | (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk,
+           pageind+head_npages)));
 
        tail_miscelm = arena_miscelm_get(chunk, pageind + head_npages);
        tail_run = &tail_miscelm->run;
-       arena_run_dalloc(arena, tail_run, dirty, false);
+       arena_run_dalloc(arena, tail_run, dirty, false, (flag_decommitted !=
+           0));
 }
 
 static arena_run_t *
@@ -1212,7 +1889,7 @@ static arena_run_t *
 arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin)
 {
        arena_run_t *run;
-       size_t binind;
+       szind_t binind;
        arena_bin_info_t *bin_info;
 
        /* Look for a usable run. */
@@ -1231,8 +1908,7 @@ arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin)
        run = arena_run_alloc_small(arena, bin_info->run_size, binind);
        if (run != NULL) {
                /* Initialize run internals. */
-               run->bin = bin;
-               run->nextind = 0;
+               run->binind = binind;
                run->nfree = bin_info->nregs;
                bitmap_init(run->bitmap, &bin_info->bitmap_info);
        }
@@ -1263,8 +1939,7 @@ arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin)
 static void *
 arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin)
 {
-       void *ret;
-       size_t binind;
+       szind_t binind;
        arena_bin_info_t *bin_info;
        arena_run_t *run;
 
@@ -1277,6 +1952,7 @@ arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin)
                 * Another thread updated runcur while this one ran without the
                 * bin lock in arena_bin_nonfull_run_get().
                 */
+               void *ret;
                assert(bin->runcur->nfree > 0);
                ret = arena_run_reg_alloc(bin->runcur, bin_info);
                if (run != NULL) {
@@ -1310,13 +1986,11 @@ arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin)
 }
 
 void
-arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind,
+arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, szind_t binind,
     uint64_t prof_accumbytes)
 {
        unsigned i, nfill;
        arena_bin_t *bin;
-       arena_run_t *run;
-       void *ptr;
 
        assert(tbin->ncached == 0);
 
@@ -1326,6 +2000,8 @@ arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind,
        malloc_mutex_lock(&bin->lock);
        for (i = 0, nfill = (tcache_bin_info[binind].ncached_max >>
            tbin->lg_fill_div); i < nfill; i++) {
+               arena_run_t *run;
+               void *ptr;
                if ((run = bin->runcur) != NULL && run->nfree > 0)
                        ptr = arena_run_reg_alloc(run, &arena_bin_info[binind]);
                else
@@ -1343,7 +2019,7 @@ arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind,
                        }
                        break;
                }
-               if (config_fill && unlikely(opt_junk)) {
+               if (config_fill && unlikely(opt_junk_alloc)) {
                        arena_alloc_junk_small(ptr, &arena_bin_info[binind],
                            true);
                }
@@ -1351,9 +2027,9 @@ arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind,
                tbin->avail[nfill - 1 - i] = ptr;
        }
        if (config_stats) {
-               bin->stats.allocated += i * arena_bin_info[binind].reg_size;
                bin->stats.nmalloc += i;
                bin->stats.nrequests += tbin->tstats.nrequests;
+               bin->stats.curregs += i;
                bin->stats.nfills++;
                tbin->tstats.nrequests = 0;
        }
@@ -1400,29 +2076,35 @@ arena_redzone_corruption_t *arena_redzone_corruption =
 static void
 arena_redzones_validate(void *ptr, arena_bin_info_t *bin_info, bool reset)
 {
-       size_t size = bin_info->reg_size;
-       size_t redzone_size = bin_info->redzone_size;
-       size_t i;
        bool error = false;
 
-       for (i = 1; i <= redzone_size; i++) {
-               uint8_t *byte = (uint8_t *)((uintptr_t)ptr - i);
-               if (*byte != 0xa5) {
-                       error = true;
-                       arena_redzone_corruption(ptr, size, false, i, *byte);
-                       if (reset)
-                               *byte = 0xa5;
+       if (opt_junk_alloc) {
+               size_t size = bin_info->reg_size;
+               size_t redzone_size = bin_info->redzone_size;
+               size_t i;
+
+               for (i = 1; i <= redzone_size; i++) {
+                       uint8_t *byte = (uint8_t *)((uintptr_t)ptr - i);
+                       if (*byte != 0xa5) {
+                               error = true;
+                               arena_redzone_corruption(ptr, size, false, i,
+                                   *byte);
+                               if (reset)
+                                       *byte = 0xa5;
+                       }
                }
-       }
-       for (i = 0; i < redzone_size; i++) {
-               uint8_t *byte = (uint8_t *)((uintptr_t)ptr + size + i);
-               if (*byte != 0xa5) {
-                       error = true;
-                       arena_redzone_corruption(ptr, size, true, i, *byte);
-                       if (reset)
-                               *byte = 0xa5;
+               for (i = 0; i < redzone_size; i++) {
+                       uint8_t *byte = (uint8_t *)((uintptr_t)ptr + size + i);
+                       if (*byte != 0xa5) {
+                               error = true;
+                               arena_redzone_corruption(ptr, size, true, i,
+                                   *byte);
+                               if (reset)
+                                       *byte = 0xa5;
+                       }
                }
        }
+
        if (opt_abort && error)
                abort();
 }
@@ -1450,14 +2132,14 @@ arena_dalloc_junk_small_t *arena_dalloc_junk_small =
 void
 arena_quarantine_junk_small(void *ptr, size_t usize)
 {
-       size_t binind;
+       szind_t binind;
        arena_bin_info_t *bin_info;
        cassert(config_fill);
-       assert(opt_junk);
+       assert(opt_junk_free);
        assert(opt_quarantine);
        assert(usize <= SMALL_MAXCLASS);
 
-       binind = small_size2bin(usize);
+       binind = size2index(usize);
        bin_info = &arena_bin_info[binind];
        arena_redzones_validate(ptr, bin_info, true);
 }
@@ -1468,12 +2150,12 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero)
        void *ret;
        arena_bin_t *bin;
        arena_run_t *run;
-       size_t binind;
+       szind_t binind;
 
-       binind = small_size2bin(size);
+       binind = size2index(size);
        assert(binind < NBINS);
        bin = &arena->bins[binind];
-       size = small_bin2size(binind);
+       size = index2size(binind);
 
        malloc_mutex_lock(&bin->lock);
        if ((run = bin->runcur) != NULL && run->nfree > 0)
@@ -1487,9 +2169,9 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero)
        }
 
        if (config_stats) {
-               bin->stats.allocated += size;
                bin->stats.nmalloc++;
                bin->stats.nrequests++;
+               bin->stats.curregs++;
        }
        malloc_mutex_unlock(&bin->lock);
        if (config_prof && !isthreaded && arena_prof_accum(arena, size))
@@ -1497,7 +2179,7 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero)
 
        if (!zero) {
                if (config_fill) {
-                       if (unlikely(opt_junk)) {
+                       if (unlikely(opt_junk_alloc)) {
                                arena_alloc_junk_small(ret,
                                    &arena_bin_info[binind], false);
                        } else if (unlikely(opt_zero))
@@ -1505,7 +2187,7 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero)
                }
                JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
        } else {
-               if (config_fill && unlikely(opt_junk)) {
+               if (config_fill && unlikely(opt_junk_alloc)) {
                        arena_alloc_junk_small(ret, &arena_bin_info[binind],
                            true);
                }
@@ -1520,40 +2202,59 @@ void *
 arena_malloc_large(arena_t *arena, size_t size, bool zero)
 {
        void *ret;
+       size_t usize;
+       uintptr_t random_offset;
        arena_run_t *run;
        arena_chunk_map_misc_t *miscelm;
        UNUSED bool idump;
 
        /* Large allocation. */
-       size = PAGE_CEILING(size);
+       usize = s2u(size);
        malloc_mutex_lock(&arena->lock);
-       run = arena_run_alloc_large(arena, size, zero);
+       if (config_cache_oblivious) {
+               uint64_t r;
+
+               /*
+                * Compute a uniformly distributed offset within the first page
+                * that is a multiple of the cacheline size, e.g. [0 .. 63) * 64
+                * for 4 KiB pages and 64-byte cachelines.
+                */
+               prng64(r, LG_PAGE - LG_CACHELINE, arena->offset_state,
+                   UINT64_C(6364136223846793009),
+                   UINT64_C(1442695040888963409));
+               random_offset = ((uintptr_t)r) << LG_CACHELINE;
+       } else
+               random_offset = 0;
+       run = arena_run_alloc_large(arena, usize + large_pad, zero);
        if (run == NULL) {
                malloc_mutex_unlock(&arena->lock);
                return (NULL);
        }
        miscelm = arena_run_to_miscelm(run);
-       ret = arena_miscelm_to_rpages(miscelm);
+       ret = (void *)((uintptr_t)arena_miscelm_to_rpages(miscelm) +
+           random_offset);
        if (config_stats) {
+               szind_t index = size2index(usize) - NBINS;
+
                arena->stats.nmalloc_large++;
                arena->stats.nrequests_large++;
-               arena->stats.allocated_large += size;
-               arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++;
-               arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++;
-               arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++;
+               arena->stats.allocated_large += usize;
+               arena->stats.lstats[index].nmalloc++;
+               arena->stats.lstats[index].nrequests++;
+               arena->stats.lstats[index].curruns++;
        }
        if (config_prof)
-               idump = arena_prof_accum_locked(arena, size);
+               idump = arena_prof_accum_locked(arena, usize);
        malloc_mutex_unlock(&arena->lock);
        if (config_prof && idump)
                prof_idump();
 
        if (!zero) {
                if (config_fill) {
-                       if (unlikely(opt_junk))
-                               memset(ret, 0xa5, size);
+                       if (unlikely(opt_junk_alloc))
+                               memset(ret, 0xa5, usize);
                        else if (unlikely(opt_zero))
-                               memset(ret, 0, size);
+                               memset(ret, 0, usize);
                }
        }
 
@@ -1561,8 +2262,9 @@ arena_malloc_large(arena_t *arena, size_t size, bool zero)
 }
 
 /* Only handles large allocations that require more than page alignment. */
-void *
-arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero)
+static void *
+arena_palloc_large(tsd_t *tsd, arena_t *arena, size_t usize, size_t alignment,
+    bool zero)
 {
        void *ret;
        size_t alloc_size, leadsize, trailsize;
@@ -1571,10 +2273,14 @@ arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero)
        arena_chunk_map_misc_t *miscelm;
        void *rpages;
 
-       assert((size & PAGE_MASK) == 0);
+       assert(usize == PAGE_CEILING(usize));
+
+       arena = arena_choose(tsd, arena);
+       if (unlikely(arena == NULL))
+               return (NULL);
 
        alignment = PAGE_CEILING(alignment);
-       alloc_size = size + alignment - PAGE;
+       alloc_size = usize + large_pad + alignment - PAGE;
 
        malloc_mutex_lock(&arena->lock);
        run = arena_run_alloc_large(arena, alloc_size, false);
@@ -1588,8 +2294,8 @@ arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero)
 
        leadsize = ALIGNMENT_CEILING((uintptr_t)rpages, alignment) -
            (uintptr_t)rpages;
-       assert(alloc_size >= leadsize + size);
-       trailsize = alloc_size - leadsize - size;
+       assert(alloc_size >= leadsize + usize);
+       trailsize = alloc_size - leadsize - usize - large_pad;
        if (leadsize != 0) {
                arena_chunk_map_misc_t *head_miscelm = miscelm;
                arena_run_t *head_run = run;
@@ -1603,27 +2309,74 @@ arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero)
                    alloc_size - leadsize);
        }
        if (trailsize != 0) {
-               arena_run_trim_tail(arena, chunk, run, size + trailsize, size,
-                   false);
+               arena_run_trim_tail(arena, chunk, run, usize + large_pad +
+                   trailsize, usize + large_pad, false);
+       }
+       if (arena_run_init_large(arena, run, usize + large_pad, zero)) {
+               size_t run_ind =
+                   arena_miscelm_to_pageind(arena_run_to_miscelm(run));
+               bool dirty = (arena_mapbits_dirty_get(chunk, run_ind) != 0);
+               bool decommitted = (arena_mapbits_decommitted_get(chunk,
+                   run_ind) != 0);
+
+               assert(decommitted); /* Cause of OOM. */
+               arena_run_dalloc(arena, run, dirty, false, decommitted);
+               malloc_mutex_unlock(&arena->lock);
+               return (NULL);
        }
-       arena_run_init_large(arena, run, size, zero);
        ret = arena_miscelm_to_rpages(miscelm);
 
        if (config_stats) {
+               szind_t index = size2index(usize) - NBINS;
+
                arena->stats.nmalloc_large++;
                arena->stats.nrequests_large++;
-               arena->stats.allocated_large += size;
-               arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++;
-               arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++;
-               arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++;
+               arena->stats.allocated_large += usize;
+               arena->stats.lstats[index].nmalloc++;
+               arena->stats.lstats[index].nrequests++;
+               arena->stats.lstats[index].curruns++;
        }
        malloc_mutex_unlock(&arena->lock);
 
        if (config_fill && !zero) {
-               if (unlikely(opt_junk))
-                       memset(ret, 0xa5, size);
+               if (unlikely(opt_junk_alloc))
+                       memset(ret, 0xa5, usize);
                else if (unlikely(opt_zero))
-                       memset(ret, 0, size);
+                       memset(ret, 0, usize);
+       }
+       return (ret);
+}
+
+void *
+arena_palloc(tsd_t *tsd, arena_t *arena, size_t usize, size_t alignment,
+    bool zero, tcache_t *tcache)
+{
+       void *ret;
+
+       if (usize <= SMALL_MAXCLASS && (alignment < PAGE || (alignment == PAGE
+           && (usize & PAGE_MASK) == 0))) {
+               /* Small; alignment doesn't require special run placement. */
+               ret = arena_malloc(tsd, arena, usize, zero, tcache);
+       } else if (usize <= large_maxclass && alignment <= PAGE) {
+               /*
+                * Large; alignment doesn't require special run placement.
+                * However, the cached pointer may be at a random offset from
+                * the base of the run, so do some bit manipulation to retrieve
+                * the base.
+                */
+               ret = arena_malloc(tsd, arena, usize, zero, tcache);
+               if (config_cache_oblivious)
+                       ret = (void *)((uintptr_t)ret & ~PAGE_MASK);
+       } else {
+               if (likely(usize <= large_maxclass)) {
+                       ret = arena_palloc_large(tsd, arena, usize, alignment,
+                           zero);
+               } else if (likely(alignment <= chunksize))
+                       ret = huge_malloc(tsd, arena, usize, zero, tcache);
+               else {
+                       ret = huge_palloc(tsd, arena, usize, alignment, zero,
+                           tcache);
+               }
        }
        return (ret);
 }
@@ -1632,22 +2385,23 @@ void
 arena_prof_promoted(const void *ptr, size_t size)
 {
        arena_chunk_t *chunk;
-       size_t pageind, binind;
+       size_t pageind;
+       szind_t binind;
 
        cassert(config_prof);
        assert(ptr != NULL);
        assert(CHUNK_ADDR2BASE(ptr) != ptr);
-       assert(isalloc(ptr, false) == PAGE);
-       assert(isalloc(ptr, true) == PAGE);
+       assert(isalloc(ptr, false) == LARGE_MINCLASS);
+       assert(isalloc(ptr, true) == LARGE_MINCLASS);
        assert(size <= SMALL_MAXCLASS);
 
        chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
        pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
-       binind = small_size2bin(size);
+       binind = size2index(size);
        assert(binind < NBINS);
        arena_mapbits_large_binind_set(chunk, pageind, binind);
 
-       assert(isalloc(ptr, false) == PAGE);
+       assert(isalloc(ptr, false) == LARGE_MINCLASS);
        assert(isalloc(ptr, true) == size);
 }
 
@@ -1660,7 +2414,8 @@ arena_dissociate_bin_run(arena_chunk_t *chunk, arena_run_t *run,
        if (run == bin->runcur)
                bin->runcur = NULL;
        else {
-               size_t binind = arena_bin_index(chunk->arena, bin);
+               szind_t binind = arena_bin_index(extent_node_arena_get(
+                   &chunk->node), bin);
                arena_bin_info_t *bin_info = &arena_bin_info[binind];
 
                if (bin_info->nregs != 1) {
@@ -1678,54 +2433,15 @@ static void
 arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
     arena_bin_t *bin)
 {
-       size_t binind;
-       arena_bin_info_t *bin_info;
-       size_t npages, run_ind, past;
-       arena_chunk_map_misc_t *miscelm;
-       void *rpages;
 
        assert(run != bin->runcur);
        assert(arena_run_tree_search(&bin->runs, arena_run_to_miscelm(run)) ==
            NULL);
 
-       binind = arena_bin_index(chunk->arena, run->bin);
-       bin_info = &arena_bin_info[binind];
-
        malloc_mutex_unlock(&bin->lock);
        /******************************/
-       npages = bin_info->run_size >> LG_PAGE;
-       miscelm = arena_run_to_miscelm(run);
-       run_ind = arena_miscelm_to_pageind(miscelm);
-       rpages = arena_miscelm_to_rpages(miscelm);
-       past = (size_t)(PAGE_CEILING((uintptr_t)rpages +
-           (uintptr_t)bin_info->reg0_offset + (uintptr_t)(run->nextind *
-           bin_info->reg_interval - bin_info->redzone_size) -
-           (uintptr_t)chunk) >> LG_PAGE);
        malloc_mutex_lock(&arena->lock);
-
-       /*
-        * If the run was originally clean, and some pages were never touched,
-        * trim the clean pages before deallocating the dirty portion of the
-        * run.
-        */
-       assert(arena_mapbits_dirty_get(chunk, run_ind) ==
-           arena_mapbits_dirty_get(chunk, run_ind+npages-1));
-       if (arena_mapbits_dirty_get(chunk, run_ind) == 0 && past - run_ind <
-           npages) {
-               /* Trim clean pages.  Convert to large run beforehand. */
-               assert(npages > 0);
-               if (past > run_ind) {
-                       arena_mapbits_large_set(chunk, run_ind,
-                           bin_info->run_size, 0);
-                       arena_mapbits_large_set(chunk, run_ind+npages-1, 0, 0);
-                       arena_run_trim_tail(arena, chunk, run, (npages <<
-                           LG_PAGE), ((past - run_ind) << LG_PAGE), false);
-                       arena_run_dalloc(arena, run, true, false);
-               } else
-                       arena_run_dalloc(arena, run, false, false);
-               /* npages = past - run_ind; */
-       } else
-               arena_run_dalloc(arena, run, true, false);
+       arena_run_dalloc_decommit(arena, chunk, run);
        malloc_mutex_unlock(&arena->lock);
        /****************************/
        malloc_mutex_lock(&bin->lock);
@@ -1754,27 +2470,24 @@ arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
                arena_bin_runs_insert(bin, run);
 }
 
-void
-arena_dalloc_bin_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr,
-    arena_chunk_map_bits_t *bitselm)
+static void
+arena_dalloc_bin_locked_impl(arena_t *arena, arena_chunk_t *chunk, void *ptr,
+    arena_chunk_map_bits_t *bitselm, bool junked)
 {
        size_t pageind, rpages_ind;
        arena_run_t *run;
        arena_bin_t *bin;
        arena_bin_info_t *bin_info;
-       size_t size, binind;
+       szind_t binind;
 
        pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
        rpages_ind = pageind - arena_mapbits_small_runind_get(chunk, pageind);
        run = &arena_miscelm_get(chunk, rpages_ind)->run;
-       bin = run->bin;
-       binind = arena_ptr_small_binind_get(ptr, arena_mapbits_get(chunk,
-           pageind));
+       binind = run->binind;
+       bin = &arena->bins[binind];
        bin_info = &arena_bin_info[binind];
-       if (config_fill || config_stats)
-               size = bin_info->reg_size;
 
-       if (config_fill && unlikely(opt_junk))
+       if (!junked && config_fill && unlikely(opt_junk_free))
                arena_dalloc_junk_small(ptr, bin_info);
 
        arena_run_reg_dalloc(run, ptr);
@@ -1785,11 +2498,19 @@ arena_dalloc_bin_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr,
                arena_bin_lower_run(arena, chunk, run, bin);
 
        if (config_stats) {
-               bin->stats.allocated -= size;
                bin->stats.ndalloc++;
+               bin->stats.curregs--;
        }
 }
 
+void
+arena_dalloc_bin_junked_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr,
+    arena_chunk_map_bits_t *bitselm)
+{
+
+       arena_dalloc_bin_locked_impl(arena, chunk, ptr, bitselm, true);
+}
+
 void
 arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr,
     size_t pageind, arena_chunk_map_bits_t *bitselm)
@@ -1800,9 +2521,9 @@ arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr,
 
        rpages_ind = pageind - arena_mapbits_small_runind_get(chunk, pageind);
        run = &arena_miscelm_get(chunk, rpages_ind)->run;
-       bin = run->bin;
+       bin = &arena->bins[run->binind];
        malloc_mutex_lock(&bin->lock);
-       arena_dalloc_bin_locked(arena, chunk, ptr, bitselm);
+       arena_dalloc_bin_locked_impl(arena, chunk, ptr, bitselm, false);
        malloc_mutex_unlock(&bin->lock);
 }
 
@@ -1825,11 +2546,11 @@ arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr,
 #undef arena_dalloc_junk_large
 #define        arena_dalloc_junk_large JEMALLOC_N(arena_dalloc_junk_large_impl)
 #endif
-static void
+void
 arena_dalloc_junk_large(void *ptr, size_t usize)
 {
 
-       if (config_fill && unlikely(opt_junk))
+       if (config_fill && unlikely(opt_junk_free))
                memset(ptr, 0x5a, usize);
 }
 #ifdef JEMALLOC_JET
@@ -1839,26 +2560,39 @@ arena_dalloc_junk_large_t *arena_dalloc_junk_large =
     JEMALLOC_N(arena_dalloc_junk_large_impl);
 #endif
 
-void
-arena_dalloc_large_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr)
+static void
+arena_dalloc_large_locked_impl(arena_t *arena, arena_chunk_t *chunk,
+    void *ptr, bool junked)
 {
        size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
        arena_chunk_map_misc_t *miscelm = arena_miscelm_get(chunk, pageind);
        arena_run_t *run = &miscelm->run;
 
        if (config_fill || config_stats) {
-               size_t usize = arena_mapbits_large_size_get(chunk, pageind);
+               size_t usize = arena_mapbits_large_size_get(chunk, pageind) -
+                   large_pad;
 
-               arena_dalloc_junk_large(ptr, usize);
+               if (!junked)
+                       arena_dalloc_junk_large(ptr, usize);
                if (config_stats) {
+                       szind_t index = size2index(usize) - NBINS;
+
                        arena->stats.ndalloc_large++;
                        arena->stats.allocated_large -= usize;
-                       arena->stats.lstats[(usize >> LG_PAGE) - 1].ndalloc++;
-                       arena->stats.lstats[(usize >> LG_PAGE) - 1].curruns--;
+                       arena->stats.lstats[index].ndalloc++;
+                       arena->stats.lstats[index].curruns--;
                }
        }
 
-       arena_run_dalloc(arena, run, true, false);
+       arena_run_dalloc_decommit(arena, chunk, run);
+}
+
+void
+arena_dalloc_large_junked_locked(arena_t *arena, arena_chunk_t *chunk,
+    void *ptr)
+{
+
+       arena_dalloc_large_locked_impl(arena, chunk, ptr, true);
 }
 
 void
@@ -1866,7 +2600,7 @@ arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr)
 {
 
        malloc_mutex_lock(&arena->lock);
-       arena_dalloc_large_locked(arena, chunk, ptr);
+       arena_dalloc_large_locked_impl(arena, chunk, ptr, false);
        malloc_mutex_unlock(&arena->lock);
 }
 
@@ -1885,54 +2619,84 @@ arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr,
         * allocations.
         */
        malloc_mutex_lock(&arena->lock);
-       arena_run_trim_tail(arena, chunk, run, oldsize, size, true);
+       arena_run_trim_tail(arena, chunk, run, oldsize + large_pad, size +
+           large_pad, true);
        if (config_stats) {
+               szind_t oldindex = size2index(oldsize) - NBINS;
+               szind_t index = size2index(size) - NBINS;
+
                arena->stats.ndalloc_large++;
                arena->stats.allocated_large -= oldsize;
-               arena->stats.lstats[(oldsize >> LG_PAGE) - 1].ndalloc++;
-               arena->stats.lstats[(oldsize >> LG_PAGE) - 1].curruns--;
+               arena->stats.lstats[oldindex].ndalloc++;
+               arena->stats.lstats[oldindex].curruns--;
 
                arena->stats.nmalloc_large++;
                arena->stats.nrequests_large++;
                arena->stats.allocated_large += size;
-               arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++;
-               arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++;
-               arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++;
+               arena->stats.lstats[index].nmalloc++;
+               arena->stats.lstats[index].nrequests++;
+               arena->stats.lstats[index].curruns++;
        }
        malloc_mutex_unlock(&arena->lock);
 }
 
 static bool
 arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr,
-    size_t oldsize, size_t size, size_t extra, bool zero)
+    size_t oldsize, size_t usize_min, size_t usize_max, bool zero)
 {
        size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE;
-       size_t npages = oldsize >> LG_PAGE;
+       size_t npages = (oldsize + large_pad) >> LG_PAGE;
        size_t followsize;
 
-       assert(oldsize == arena_mapbits_large_size_get(chunk, pageind));
+       assert(oldsize == arena_mapbits_large_size_get(chunk, pageind) -
+           large_pad);
 
        /* Try to extend the run. */
-       assert(size + extra > oldsize);
        malloc_mutex_lock(&arena->lock);
-       if (pageind + npages < chunk_npages &&
-           arena_mapbits_allocated_get(chunk, pageind+npages) == 0 &&
-           (followsize = arena_mapbits_unallocated_size_get(chunk,
-           pageind+npages)) >= size - oldsize) {
+       if (pageind+npages >= chunk_npages || arena_mapbits_allocated_get(chunk,
+           pageind+npages) != 0)
+               goto label_fail;
+       followsize = arena_mapbits_unallocated_size_get(chunk, pageind+npages);
+       if (oldsize + followsize >= usize_min) {
                /*
                 * The next run is available and sufficiently large.  Split the
                 * following run, then merge the first part with the existing
                 * allocation.
                 */
-               size_t flag_dirty;
-               size_t splitsize = (oldsize + followsize <= size + extra)
-                   ? followsize : size + extra - oldsize;
-               arena_run_t *run = &arena_miscelm_get(chunk,
-                   pageind+npages)->run;
-               arena_run_split_large(arena, run, splitsize, zero);
+               arena_run_t *run;
+               size_t usize, splitsize, size, flag_dirty, flag_unzeroed_mask;
+
+               usize = usize_max;
+               while (oldsize + followsize < usize)
+                       usize = index2size(size2index(usize)-1);
+               assert(usize >= usize_min);
+               assert(usize >= oldsize);
+               splitsize = usize - oldsize;
+               if (splitsize == 0)
+                       goto label_fail;
+
+               run = &arena_miscelm_get(chunk, pageind+npages)->run;
+               if (arena_run_split_large(arena, run, splitsize, zero))
+                       goto label_fail;
+
+               if (config_cache_oblivious && zero) {
+                       /*
+                        * Zero the trailing bytes of the original allocation's
+                        * last page, since they are in an indeterminate state.
+                        * There will always be trailing bytes, because ptr's
+                        * offset from the beginning of the run is a multiple of
+                        * CACHELINE in [0 .. PAGE).
+                        */
+                       void *zbase = (void *)((uintptr_t)ptr + oldsize);
+                       void *zpast = PAGE_ADDR2BASE((void *)((uintptr_t)zbase +
+                           PAGE));
+                       size_t nzero = (uintptr_t)zpast - (uintptr_t)zbase;
+                       assert(nzero > 0);
+                       memset(zbase, 0, nzero);
+               }
 
                size = oldsize + splitsize;
-               npages = size >> LG_PAGE;
+               npages = (size + large_pad) >> LG_PAGE;
 
                /*
                 * Mark the extended run as dirty if either portion of the run
@@ -1944,27 +2708,35 @@ arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr,
                 */
                flag_dirty = arena_mapbits_dirty_get(chunk, pageind) |
                    arena_mapbits_dirty_get(chunk, pageind+npages-1);
-               arena_mapbits_large_set(chunk, pageind, size, flag_dirty);
-               arena_mapbits_large_set(chunk, pageind+npages-1, 0, flag_dirty);
+               flag_unzeroed_mask = flag_dirty == 0 ? CHUNK_MAP_UNZEROED : 0;
+               arena_mapbits_large_set(chunk, pageind, size + large_pad,
+                   flag_dirty | (flag_unzeroed_mask &
+                   arena_mapbits_unzeroed_get(chunk, pageind)));
+               arena_mapbits_large_set(chunk, pageind+npages-1, 0, flag_dirty |
+                   (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk,
+                   pageind+npages-1)));
 
                if (config_stats) {
+                       szind_t oldindex = size2index(oldsize) - NBINS;
+                       szind_t index = size2index(size) - NBINS;
+
                        arena->stats.ndalloc_large++;
                        arena->stats.allocated_large -= oldsize;
-                       arena->stats.lstats[(oldsize >> LG_PAGE) - 1].ndalloc++;
-                       arena->stats.lstats[(oldsize >> LG_PAGE) - 1].curruns--;
+                       arena->stats.lstats[oldindex].ndalloc++;
+                       arena->stats.lstats[oldindex].curruns--;
 
                        arena->stats.nmalloc_large++;
                        arena->stats.nrequests_large++;
                        arena->stats.allocated_large += size;
-                       arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++;
-                       arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++;
-                       arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++;
+                       arena->stats.lstats[index].nmalloc++;
+                       arena->stats.lstats[index].nrequests++;
+                       arena->stats.lstats[index].curruns++;
                }
                malloc_mutex_unlock(&arena->lock);
                return (false);
        }
+label_fail:
        malloc_mutex_unlock(&arena->lock);
-
        return (true);
 }
 
@@ -1976,7 +2748,7 @@ static void
 arena_ralloc_junk_large(void *ptr, size_t old_usize, size_t usize)
 {
 
-       if (config_fill && unlikely(opt_junk)) {
+       if (config_fill && unlikely(opt_junk_free)) {
                memset((void *)((uintptr_t)ptr + usize), 0x5a,
                    old_usize - usize);
        }
@@ -1993,136 +2765,132 @@ arena_ralloc_junk_large_t *arena_ralloc_junk_large =
  * always fail if growing an object, and the following run is already in use.
  */
 static bool
-arena_ralloc_large(void *ptr, size_t oldsize, size_t size, size_t extra,
-    bool zero)
+arena_ralloc_large(void *ptr, size_t oldsize, size_t usize_min,
+    size_t usize_max, bool zero)
 {
-       size_t psize;
+       arena_chunk_t *chunk;
+       arena_t *arena;
 
-       psize = PAGE_CEILING(size + extra);
-       if (psize == oldsize) {
-               /* Same size class. */
+       if (oldsize == usize_max) {
+               /* Current size class is compatible and maximal. */
                return (false);
-       } else {
-               arena_chunk_t *chunk;
-               arena_t *arena;
-
-               chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-               arena = chunk->arena;
-
-               if (psize < oldsize) {
-                       /* Fill before shrinking in order avoid a race. */
-                       arena_ralloc_junk_large(ptr, oldsize, psize);
-                       arena_ralloc_large_shrink(arena, chunk, ptr, oldsize,
-                           psize);
-                       return (false);
-               } else {
-                       bool ret = arena_ralloc_large_grow(arena, chunk, ptr,
-                           oldsize, PAGE_CEILING(size),
-                           psize - PAGE_CEILING(size), zero);
-                       if (config_fill && !ret && !zero) {
-                               if (unlikely(opt_junk)) {
-                                       memset((void *)((uintptr_t)ptr +
-                                           oldsize), 0xa5, isalloc(ptr,
-                                           config_prof) - oldsize);
-                               } else if (unlikely(opt_zero)) {
-                                       memset((void *)((uintptr_t)ptr +
-                                           oldsize), 0, isalloc(ptr,
-                                           config_prof) - oldsize);
-                               }
+       }
+
+       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+       arena = extent_node_arena_get(&chunk->node);
+
+       if (oldsize < usize_max) {
+               bool ret = arena_ralloc_large_grow(arena, chunk, ptr, oldsize,
+                   usize_min, usize_max, zero);
+               if (config_fill && !ret && !zero) {
+                       if (unlikely(opt_junk_alloc)) {
+                               memset((void *)((uintptr_t)ptr + oldsize), 0xa5,
+                                   isalloc(ptr, config_prof) - oldsize);
+                       } else if (unlikely(opt_zero)) {
+                               memset((void *)((uintptr_t)ptr + oldsize), 0,
+                                   isalloc(ptr, config_prof) - oldsize);
                        }
-                       return (ret);
                }
+               return (ret);
        }
+
+       assert(oldsize > usize_max);
+       /* Fill before shrinking in order avoid a race. */
+       arena_ralloc_junk_large(ptr, oldsize, usize_max);
+       arena_ralloc_large_shrink(arena, chunk, ptr, oldsize, usize_max);
+       return (false);
 }
 
 bool
 arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra,
     bool zero)
 {
+       size_t usize_min, usize_max;
 
-       /*
-        * Avoid moving the allocation if the size class can be left the same.
-        */
-       if (oldsize <= arena_maxclass) {
+       usize_min = s2u(size);
+       usize_max = s2u(size + extra);
+       if (likely(oldsize <= large_maxclass && usize_min <= large_maxclass)) {
+               /*
+                * Avoid moving the allocation if the size class can be left the
+                * same.
+                */
                if (oldsize <= SMALL_MAXCLASS) {
-                       assert(arena_bin_info[small_size2bin(oldsize)].reg_size
-                           == oldsize);
-                       if ((size + extra <= SMALL_MAXCLASS &&
-                           small_size2bin(size + extra) ==
-                           small_size2bin(oldsize)) || (size <= oldsize &&
-                           size + extra >= oldsize))
+                       assert(arena_bin_info[size2index(oldsize)].reg_size ==
+                           oldsize);
+                       if ((usize_max <= SMALL_MAXCLASS &&
+                           size2index(usize_max) == size2index(oldsize)) ||
+                           (size <= oldsize && usize_max >= oldsize))
                                return (false);
                } else {
-                       assert(size <= arena_maxclass);
-                       if (size + extra > SMALL_MAXCLASS) {
-                               if (!arena_ralloc_large(ptr, oldsize, size,
-                                   extra, zero))
+                       if (usize_max > SMALL_MAXCLASS) {
+                               if (!arena_ralloc_large(ptr, oldsize, usize_min,
+                                   usize_max, zero))
                                        return (false);
                        }
                }
+
+               /* Reallocation would require a move. */
+               return (true);
+       } else {
+               return (huge_ralloc_no_move(ptr, oldsize, usize_min, usize_max,
+                   zero));
        }
+}
 
-       /* Reallocation would require a move. */
-       return (true);
+static void *
+arena_ralloc_move_helper(tsd_t *tsd, arena_t *arena, size_t usize,
+    size_t alignment, bool zero, tcache_t *tcache)
+{
+
+       if (alignment == 0)
+               return (arena_malloc(tsd, arena, usize, zero, tcache));
+       usize = sa2u(usize, alignment);
+       if (usize == 0)
+               return (NULL);
+       return (ipalloct(tsd, usize, alignment, zero, tcache, arena));
 }
 
 void *
 arena_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, size_t size,
-    size_t extra, size_t alignment, bool zero, bool try_tcache_alloc,
-    bool try_tcache_dalloc)
+    size_t alignment, bool zero, tcache_t *tcache)
 {
        void *ret;
-       size_t copysize;
+       size_t usize;
 
-       /* Try to avoid moving the allocation. */
-       if (!arena_ralloc_no_move(ptr, oldsize, size, extra, zero))
-               return (ptr);
+       usize = s2u(size);
+       if (usize == 0)
+               return (NULL);
 
-       /*
-        * size and oldsize are different enough that we need to move the
-        * object.  In that case, fall back to allocating new space and
-        * copying.
-        */
-       if (alignment != 0) {
-               size_t usize = sa2u(size + extra, alignment);
-               if (usize == 0)
-                       return (NULL);
-               ret = ipalloct(tsd, usize, alignment, zero, try_tcache_alloc,
-                   arena);
-       } else {
-               ret = arena_malloc(tsd, arena, size + extra, zero,
-                   try_tcache_alloc);
-       }
+       if (likely(usize <= large_maxclass)) {
+               size_t copysize;
 
-       if (ret == NULL) {
-               if (extra == 0)
-                       return (NULL);
-               /* Try again, this time without extra. */
-               if (alignment != 0) {
-                       size_t usize = sa2u(size, alignment);
-                       if (usize == 0)
-                               return (NULL);
-                       ret = ipalloct(tsd, usize, alignment, zero,
-                           try_tcache_alloc, arena);
-               } else {
-                       ret = arena_malloc(tsd, arena, size, zero,
-                           try_tcache_alloc);
-               }
+               /* Try to avoid moving the allocation. */
+               if (!arena_ralloc_no_move(ptr, oldsize, usize, 0, zero))
+                       return (ptr);
 
+               /*
+                * size and oldsize are different enough that we need to move
+                * the object.  In that case, fall back to allocating new space
+                * and copying.
+                */
+               ret = arena_ralloc_move_helper(tsd, arena, usize, alignment,
+                   zero, tcache);
                if (ret == NULL)
                        return (NULL);
-       }
 
-       /* Junk/zero-filling were already done by ipalloc()/arena_malloc(). */
+               /*
+                * Junk/zero-filling were already done by
+                * ipalloc()/arena_malloc().
+                */
 
-       /*
-        * Copy at most size bytes (not size+extra), since the caller has no
-        * expectation that the extra bytes will be reliably preserved.
-        */
-       copysize = (size < oldsize) ? size : oldsize;
-       JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, copysize);
-       memcpy(ret, ptr, copysize);
-       iqalloc(tsd, ptr, try_tcache_dalloc);
+               copysize = (usize < oldsize) ? usize : oldsize;
+               JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, copysize);
+               memcpy(ret, ptr, copysize);
+               isqalloc(tsd, ptr, oldsize, tcache);
+       } else {
+               ret = huge_ralloc(tsd, arena, ptr, oldsize, usize, alignment,
+                   zero, tcache);
+       }
        return (ret);
 }
 
@@ -2149,15 +2917,34 @@ arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec)
        return (false);
 }
 
+ssize_t
+arena_lg_dirty_mult_default_get(void)
+{
+
+       return ((ssize_t)atomic_read_z((size_t *)&lg_dirty_mult_default));
+}
+
+bool
+arena_lg_dirty_mult_default_set(ssize_t lg_dirty_mult)
+{
+
+       if (!arena_lg_dirty_mult_valid(lg_dirty_mult))
+               return (true);
+       atomic_write_z((size_t *)&lg_dirty_mult_default, (size_t)lg_dirty_mult);
+       return (false);
+}
+
 void
-arena_stats_merge(arena_t *arena, const char **dss, size_t *nactive,
-    size_t *ndirty, arena_stats_t *astats, malloc_bin_stats_t *bstats,
-    malloc_large_stats_t *lstats)
+arena_stats_merge(arena_t *arena, const char **dss, ssize_t *lg_dirty_mult,
+    size_t *nactive, size_t *ndirty, arena_stats_t *astats,
+    malloc_bin_stats_t *bstats, malloc_large_stats_t *lstats,
+    malloc_huge_stats_t *hstats)
 {
        unsigned i;
 
        malloc_mutex_lock(&arena->lock);
        *dss = dss_prec_names[arena->dss_prec];
+       *lg_dirty_mult = arena->lg_dirty_mult;
        *nactive += arena->nactive;
        *ndirty += arena->ndirty;
 
@@ -2165,6 +2952,8 @@ arena_stats_merge(arena_t *arena, const char **dss, size_t *nactive,
        astats->npurge += arena->stats.npurge;
        astats->nmadvise += arena->stats.nmadvise;
        astats->purged += arena->stats.purged;
+       astats->metadata_mapped += arena->stats.metadata_mapped;
+       astats->metadata_allocated += arena_metadata_allocated_get(arena);
        astats->allocated_large += arena->stats.allocated_large;
        astats->nmalloc_large += arena->stats.nmalloc_large;
        astats->ndalloc_large += arena->stats.ndalloc_large;
@@ -2172,7 +2961,6 @@ arena_stats_merge(arena_t *arena, const char **dss, size_t *nactive,
        astats->allocated_huge += arena->stats.allocated_huge;
        astats->nmalloc_huge += arena->stats.nmalloc_huge;
        astats->ndalloc_huge += arena->stats.ndalloc_huge;
-       astats->nrequests_huge += arena->stats.nrequests_huge;
 
        for (i = 0; i < nlclasses; i++) {
                lstats[i].nmalloc += arena->stats.lstats[i].nmalloc;
@@ -2180,16 +2968,22 @@ arena_stats_merge(arena_t *arena, const char **dss, size_t *nactive,
                lstats[i].nrequests += arena->stats.lstats[i].nrequests;
                lstats[i].curruns += arena->stats.lstats[i].curruns;
        }
+
+       for (i = 0; i < nhclasses; i++) {
+               hstats[i].nmalloc += arena->stats.hstats[i].nmalloc;
+               hstats[i].ndalloc += arena->stats.hstats[i].ndalloc;
+               hstats[i].curhchunks += arena->stats.hstats[i].curhchunks;
+       }
        malloc_mutex_unlock(&arena->lock);
 
        for (i = 0; i < NBINS; i++) {
                arena_bin_t *bin = &arena->bins[i];
 
                malloc_mutex_lock(&bin->lock);
-               bstats[i].allocated += bin->stats.allocated;
                bstats[i].nmalloc += bin->stats.nmalloc;
                bstats[i].ndalloc += bin->stats.ndalloc;
                bstats[i].nrequests += bin->stats.nrequests;
+               bstats[i].curregs += bin->stats.curregs;
                if (config_tcache) {
                        bstats[i].nfills += bin->stats.nfills;
                        bstats[i].nflushes += bin->stats.nflushes;
@@ -2201,29 +2995,42 @@ arena_stats_merge(arena_t *arena, const char **dss, size_t *nactive,
        }
 }
 
-bool
-arena_new(arena_t *arena, unsigned ind)
+arena_t *
+arena_new(unsigned ind)
 {
+       arena_t *arena;
        unsigned i;
        arena_bin_t *bin;
 
+       /*
+        * Allocate arena, arena->lstats, and arena->hstats contiguously, mainly
+        * because there is no way to clean up if base_alloc() OOMs.
+        */
+       if (config_stats) {
+               arena = (arena_t *)base_alloc(CACHELINE_CEILING(sizeof(arena_t))
+                   + QUANTUM_CEILING(nlclasses * sizeof(malloc_large_stats_t) +
+                   nhclasses) * sizeof(malloc_huge_stats_t));
+       } else
+               arena = (arena_t *)base_alloc(sizeof(arena_t));
+       if (arena == NULL)
+               return (NULL);
+
        arena->ind = ind;
        arena->nthreads = 0;
-       arena->chunk_alloc = chunk_alloc_default;
-       arena->chunk_dalloc = chunk_dalloc_default;
-
        if (malloc_mutex_init(&arena->lock))
-               return (true);
+               return (NULL);
 
        if (config_stats) {
                memset(&arena->stats, 0, sizeof(arena_stats_t));
-               arena->stats.lstats =
-                   (malloc_large_stats_t *)base_alloc(nlclasses *
-                   sizeof(malloc_large_stats_t));
-               if (arena->stats.lstats == NULL)
-                       return (true);
+               arena->stats.lstats = (malloc_large_stats_t *)((uintptr_t)arena
+                   + CACHELINE_CEILING(sizeof(arena_t)));
                memset(arena->stats.lstats, 0, nlclasses *
                    sizeof(malloc_large_stats_t));
+               arena->stats.hstats = (malloc_huge_stats_t *)((uintptr_t)arena
+                   + CACHELINE_CEILING(sizeof(arena_t)) +
+                   QUANTUM_CEILING(nlclasses * sizeof(malloc_large_stats_t)));
+               memset(arena->stats.hstats, 0, nhclasses *
+                   sizeof(malloc_huge_stats_t));
                if (config_tcache)
                        ql_new(&arena->tcache_ql);
        }
@@ -2231,34 +3038,65 @@ arena_new(arena_t *arena, unsigned ind)
        if (config_prof)
                arena->prof_accumbytes = 0;
 
+       if (config_cache_oblivious) {
+               /*
+                * A nondeterministic seed based on the address of arena reduces
+                * the likelihood of lockstep non-uniform cache index
+                * utilization among identical concurrent processes, but at the
+                * cost of test repeatability.  For debug builds, instead use a
+                * deterministic seed.
+                */
+               arena->offset_state = config_debug ? ind :
+                   (uint64_t)(uintptr_t)arena;
+       }
+
        arena->dss_prec = chunk_dss_prec_get();
 
        arena->spare = NULL;
 
+       arena->lg_dirty_mult = arena_lg_dirty_mult_default_get();
+       arena->purging = false;
        arena->nactive = 0;
        arena->ndirty = 0;
 
        arena_avail_tree_new(&arena->runs_avail);
-       ql_new(&arena->runs_dirty);
+       qr_new(&arena->runs_dirty, rd_link);
+       qr_new(&arena->chunks_cache, cc_link);
+
+       ql_new(&arena->huge);
+       if (malloc_mutex_init(&arena->huge_mtx))
+               return (NULL);
+
+       extent_tree_szad_new(&arena->chunks_szad_cached);
+       extent_tree_ad_new(&arena->chunks_ad_cached);
+       extent_tree_szad_new(&arena->chunks_szad_retained);
+       extent_tree_ad_new(&arena->chunks_ad_retained);
+       if (malloc_mutex_init(&arena->chunks_mtx))
+               return (NULL);
+       ql_new(&arena->node_cache);
+       if (malloc_mutex_init(&arena->node_cache_mtx))
+               return (NULL);
+
+       arena->chunk_hooks = chunk_hooks_default;
 
        /* Initialize bins. */
        for (i = 0; i < NBINS; i++) {
                bin = &arena->bins[i];
                if (malloc_mutex_init(&bin->lock))
-                       return (true);
+                       return (NULL);
                bin->runcur = NULL;
                arena_run_tree_new(&bin->runs);
                if (config_stats)
                        memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
        }
 
-       return (false);
+       return (arena);
 }
 
 /*
  * Calculate bin_info->run_size such that it meets the following constraints:
  *
- *   *) bin_info->run_size <= arena_maxclass
+ *   *) bin_info->run_size <= arena_maxrun
  *   *) bin_info->nregs <= RUN_MAXREGS
  *
  * bin_info->nregs and bin_info->reg0_offset are also calculated here, since
@@ -2330,12 +3168,13 @@ bin_info_run_size_calc(arena_bin_info_t *bin_info)
        /*
         * Make sure that the run will fit within an arena chunk.
         */
-       while (actual_run_size > arena_maxclass) {
+       while (actual_run_size > arena_maxrun) {
                actual_run_size -= PAGE;
                actual_nregs = (actual_run_size - pad_size) /
                    bin_info->reg_interval;
        }
        assert(actual_nregs > 0);
+       assert(actual_run_size == s2u(actual_run_size));
 
        /* Copy final settings. */
        bin_info->run_size = actual_run_size;
@@ -2343,6 +3182,9 @@ bin_info_run_size_calc(arena_bin_info_t *bin_info)
        bin_info->reg0_offset = actual_run_size - (actual_nregs *
            bin_info->reg_interval) - pad_size + bin_info->redzone_size;
 
+       if (actual_run_size > small_maxrun)
+               small_maxrun = actual_run_size;
+
        assert(bin_info->reg0_offset - bin_info->redzone_size + (bin_info->nregs
            * bin_info->reg_interval) + pad_size == bin_info->run_size);
 }
@@ -2352,7 +3194,7 @@ bin_info_init(void)
 {
        arena_bin_info_t *bin_info;
 
-#define        BIN_INFO_INIT_bin_yes(index, size) \
+#define        BIN_INFO_INIT_bin_yes(index, size)                              \
        bin_info = &arena_bin_info[index];                              \
        bin_info->reg_size = size;                                      \
        bin_info_run_size_calc(bin_info);                               \
@@ -2366,12 +3208,39 @@ bin_info_init(void)
 #undef SC
 }
 
-void
+static bool
+small_run_size_init(void)
+{
+
+       assert(small_maxrun != 0);
+
+       small_run_tab = (bool *)base_alloc(sizeof(bool) * (small_maxrun >>
+           LG_PAGE));
+       if (small_run_tab == NULL)
+               return (true);
+
+#define        TAB_INIT_bin_yes(index, size) {                                 \
+               arena_bin_info_t *bin_info = &arena_bin_info[index];    \
+               small_run_tab[bin_info->run_size >> LG_PAGE] = true;    \
+       }
+#define        TAB_INIT_bin_no(index, size)
+#define        SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup)       \
+       TAB_INIT_bin_##bin(index, (ZU(1)<<lg_grp) + (ZU(ndelta)<<lg_delta))
+       SIZE_CLASSES
+#undef TAB_INIT_bin_yes
+#undef TAB_INIT_bin_no
+#undef SC
+
+       return (false);
+}
+
+bool
 arena_boot(void)
 {
-       size_t header_size;
        unsigned i;
 
+       arena_lg_dirty_mult_default_set(opt_lg_dirty_mult);
+
        /*
         * Compute the header size such that it is large enough to contain the
         * page map.  The page map is biased to omit entries for the header
@@ -2386,7 +3255,7 @@ arena_boot(void)
         */
        map_bias = 0;
        for (i = 0; i < 3; i++) {
-               header_size = offsetof(arena_chunk_t, map_bits) +
+               size_t header_size = offsetof(arena_chunk_t, map_bits) +
                    ((sizeof(arena_chunk_map_bits_t) +
                    sizeof(arena_chunk_map_misc_t)) * (chunk_npages-map_bias));
                map_bias = (header_size + PAGE_MASK) >> LG_PAGE;
@@ -2396,9 +3265,23 @@ arena_boot(void)
        map_misc_offset = offsetof(arena_chunk_t, map_bits) +
            sizeof(arena_chunk_map_bits_t) * (chunk_npages-map_bias);
 
-       arena_maxclass = chunksize - (map_bias << LG_PAGE);
+       arena_maxrun = chunksize - (map_bias << LG_PAGE);
+       assert(arena_maxrun > 0);
+       large_maxclass = index2size(size2index(chunksize)-1);
+       if (large_maxclass > arena_maxrun) {
+               /*
+                * For small chunk sizes it's possible for there to be fewer
+                * non-header pages available than are necessary to serve the
+                * size classes just below chunksize.
+                */
+               large_maxclass = arena_maxrun;
+       }
+       assert(large_maxclass > 0);
+       nlclasses = size2index(large_maxclass) - size2index(SMALL_MAXCLASS);
+       nhclasses = NSIZES - nlclasses - NBINS;
 
        bin_info_init();
+       return (small_run_size_init());
 }
 
 void
@@ -2407,6 +3290,9 @@ arena_prefork(arena_t *arena)
        unsigned i;
 
        malloc_mutex_prefork(&arena->lock);
+       malloc_mutex_prefork(&arena->huge_mtx);
+       malloc_mutex_prefork(&arena->chunks_mtx);
+       malloc_mutex_prefork(&arena->node_cache_mtx);
        for (i = 0; i < NBINS; i++)
                malloc_mutex_prefork(&arena->bins[i].lock);
 }
@@ -2418,6 +3304,9 @@ arena_postfork_parent(arena_t *arena)
 
        for (i = 0; i < NBINS; i++)
                malloc_mutex_postfork_parent(&arena->bins[i].lock);
+       malloc_mutex_postfork_parent(&arena->node_cache_mtx);
+       malloc_mutex_postfork_parent(&arena->chunks_mtx);
+       malloc_mutex_postfork_parent(&arena->huge_mtx);
        malloc_mutex_postfork_parent(&arena->lock);
 }
 
@@ -2428,5 +3317,8 @@ arena_postfork_child(arena_t *arena)
 
        for (i = 0; i < NBINS; i++)
                malloc_mutex_postfork_child(&arena->bins[i].lock);
+       malloc_mutex_postfork_child(&arena->node_cache_mtx);
+       malloc_mutex_postfork_child(&arena->chunks_mtx);
+       malloc_mutex_postfork_child(&arena->huge_mtx);
        malloc_mutex_postfork_child(&arena->lock);
 }
index 409c7bb78c1e50a1d9ca5c6a2e27934878fea2c4..7cdcfed86bd8d435d8d715526dbba62647ab8a7a 100644 (file)
 /* Data. */
 
 static malloc_mutex_t  base_mtx;
-
-/*
- * Current pages that are being used for internal memory allocations.  These
- * pages are carved up in cacheline-size quanta, so that there is no chance of
- * false cache line sharing.
- */
-static void            *base_pages;
-static void            *base_next_addr;
-static void            *base_past_addr; /* Addr immediately past base_pages. */
+static extent_tree_t   base_avail_szad;
 static extent_node_t   *base_nodes;
+static size_t          base_allocated;
+static size_t          base_resident;
+static size_t          base_mapped;
 
 /******************************************************************************/
 
-static bool
-base_pages_alloc(size_t minsize)
+/* base_mtx must be held. */
+static extent_node_t *
+base_node_try_alloc(void)
 {
-       size_t csize;
-
-       assert(minsize != 0);
-       csize = CHUNK_CEILING(minsize);
-       base_pages = chunk_alloc_base(csize);
-       if (base_pages == NULL)
-               return (true);
-       base_next_addr = base_pages;
-       base_past_addr = (void *)((uintptr_t)base_pages + csize);
+       extent_node_t *node;
 
-       return (false);
+       if (base_nodes == NULL)
+               return (NULL);
+       node = base_nodes;
+       base_nodes = *(extent_node_t **)node;
+       JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(node, sizeof(extent_node_t));
+       return (node);
 }
 
-void *
-base_alloc(size_t size)
+/* base_mtx must be held. */
+static void
+base_node_dalloc(extent_node_t *node)
 {
-       void *ret;
-       size_t csize;
 
-       /* Round size up to nearest multiple of the cacheline size. */
-       csize = CACHELINE_CEILING(size);
-
-       malloc_mutex_lock(&base_mtx);
-       /* Make sure there's enough space for the allocation. */
-       if ((uintptr_t)base_next_addr + csize > (uintptr_t)base_past_addr) {
-               if (base_pages_alloc(csize)) {
-                       malloc_mutex_unlock(&base_mtx);
-                       return (NULL);
-               }
-       }
-       /* Allocate. */
-       ret = base_next_addr;
-       base_next_addr = (void *)((uintptr_t)base_next_addr + csize);
-       malloc_mutex_unlock(&base_mtx);
-       JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, csize);
-
-       return (ret);
+       JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(node, sizeof(extent_node_t));
+       *(extent_node_t **)node = base_nodes;
+       base_nodes = node;
 }
 
-void *
-base_calloc(size_t number, size_t size)
+/* base_mtx must be held. */
+static extent_node_t *
+base_chunk_alloc(size_t minsize)
 {
-       void *ret = base_alloc(number * size);
-
-       if (ret != NULL)
-               memset(ret, 0, number * size);
+       extent_node_t *node;
+       size_t csize, nsize;
+       void *addr;
 
-       return (ret);
+       assert(minsize != 0);
+       node = base_node_try_alloc();
+       /* Allocate enough space to also carve a node out if necessary. */
+       nsize = (node == NULL) ? CACHELINE_CEILING(sizeof(extent_node_t)) : 0;
+       csize = CHUNK_CEILING(minsize + nsize);
+       addr = chunk_alloc_base(csize);
+       if (addr == NULL) {
+               if (node != NULL)
+                       base_node_dalloc(node);
+               return (NULL);
+       }
+       base_mapped += csize;
+       if (node == NULL) {
+               node = (extent_node_t *)addr;
+               addr = (void *)((uintptr_t)addr + nsize);
+               csize -= nsize;
+               if (config_stats) {
+                       base_allocated += nsize;
+                       base_resident += PAGE_CEILING(nsize);
+               }
+       }
+       extent_node_init(node, NULL, addr, csize, true, true);
+       return (node);
 }
 
-extent_node_t *
-base_node_alloc(void)
+/*
+ * base_alloc() guarantees demand-zeroed memory, in order to make multi-page
+ * sparse data structures such as radix tree nodes efficient with respect to
+ * physical memory usage.
+ */
+void *
+base_alloc(size_t size)
 {
-       extent_node_t *ret;
+       void *ret;
+       size_t csize, usize;
+       extent_node_t *node;
+       extent_node_t key;
+
+       /*
+        * Round size up to nearest multiple of the cacheline size, so that
+        * there is no chance of false cache line sharing.
+        */
+       csize = CACHELINE_CEILING(size);
 
+       usize = s2u(csize);
+       extent_node_init(&key, NULL, NULL, usize, false, false);
        malloc_mutex_lock(&base_mtx);
-       if (base_nodes != NULL) {
-               ret = base_nodes;
-               base_nodes = *(extent_node_t **)ret;
-               malloc_mutex_unlock(&base_mtx);
-               JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret,
-                   sizeof(extent_node_t));
+       node = extent_tree_szad_nsearch(&base_avail_szad, &key);
+       if (node != NULL) {
+               /* Use existing space. */
+               extent_tree_szad_remove(&base_avail_szad, node);
        } else {
-               malloc_mutex_unlock(&base_mtx);
-               ret = (extent_node_t *)base_alloc(sizeof(extent_node_t));
+               /* Try to allocate more space. */
+               node = base_chunk_alloc(csize);
+       }
+       if (node == NULL) {
+               ret = NULL;
+               goto label_return;
        }
 
+       ret = extent_node_addr_get(node);
+       if (extent_node_size_get(node) > csize) {
+               extent_node_addr_set(node, (void *)((uintptr_t)ret + csize));
+               extent_node_size_set(node, extent_node_size_get(node) - csize);
+               extent_tree_szad_insert(&base_avail_szad, node);
+       } else
+               base_node_dalloc(node);
+       if (config_stats) {
+               base_allocated += csize;
+               /*
+                * Add one PAGE to base_resident for every page boundary that is
+                * crossed by the new allocation.
+                */
+               base_resident += PAGE_CEILING((uintptr_t)ret + csize) -
+                   PAGE_CEILING((uintptr_t)ret);
+       }
+       JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ret, csize);
+label_return:
+       malloc_mutex_unlock(&base_mtx);
        return (ret);
 }
 
 void
-base_node_dalloc(extent_node_t *node)
+base_stats_get(size_t *allocated, size_t *resident, size_t *mapped)
 {
 
-       JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(node, sizeof(extent_node_t));
        malloc_mutex_lock(&base_mtx);
-       *(extent_node_t **)node = base_nodes;
-       base_nodes = node;
+       assert(base_allocated <= base_resident);
+       assert(base_resident <= base_mapped);
+       *allocated = base_allocated;
+       *resident = base_resident;
+       *mapped = base_mapped;
        malloc_mutex_unlock(&base_mtx);
 }
 
@@ -106,9 +144,10 @@ bool
 base_boot(void)
 {
 
-       base_nodes = NULL;
        if (malloc_mutex_init(&base_mtx))
                return (true);
+       extent_tree_szad_new(&base_avail_szad);
+       base_nodes = NULL;
 
        return (false);
 }
index 32b8b3a6c6bd278025486a0537a0b1ce4b89a347..6ba1ca7a51baa0fe152da9b3f68cae0211d3ecc3 100644 (file)
@@ -5,31 +5,43 @@
 /* Data. */
 
 const char     *opt_dss = DSS_DEFAULT;
-size_t         opt_lg_chunk = LG_CHUNK_DEFAULT;
+size_t         opt_lg_chunk = 0;
 
-malloc_mutex_t chunks_mtx;
-chunk_stats_t  stats_chunks;
+/* Used exclusively for gdump triggering. */
+static size_t  curchunks;
+static size_t  highchunks;
 
-/*
- * Trees of chunks that were previously allocated (trees differ only in node
- * ordering).  These are used when allocating chunks, in an attempt to re-use
- * address space.  Depending on function, different tree orderings are needed,
- * which is why there are two trees with the same contents.
- */
-static extent_tree_t   chunks_szad_mmap;
-static extent_tree_t   chunks_ad_mmap;
-static extent_tree_t   chunks_szad_dss;
-static extent_tree_t   chunks_ad_dss;
-
-rtree_t                *chunks_rtree;
+rtree_t                chunks_rtree;
 
 /* Various chunk-related settings. */
 size_t         chunksize;
 size_t         chunksize_mask; /* (chunksize - 1). */
 size_t         chunk_npages;
-size_t         map_bias;
-size_t         map_misc_offset;
-size_t         arena_maxclass; /* Max size class for arenas. */
+
+static void    *chunk_alloc_default(void *new_addr, size_t size,
+    size_t alignment, bool *zero, bool *commit, unsigned arena_ind);
+static bool    chunk_dalloc_default(void *chunk, size_t size, bool committed,
+    unsigned arena_ind);
+static bool    chunk_commit_default(void *chunk, size_t size, size_t offset,
+    size_t length, unsigned arena_ind);
+static bool    chunk_decommit_default(void *chunk, size_t size, size_t offset,
+    size_t length, unsigned arena_ind);
+static bool    chunk_purge_default(void *chunk, size_t size, size_t offset,
+    size_t length, unsigned arena_ind);
+static bool    chunk_split_default(void *chunk, size_t size, size_t size_a,
+    size_t size_b, bool committed, unsigned arena_ind);
+static bool    chunk_merge_default(void *chunk_a, size_t size_a, void *chunk_b,
+    size_t size_b, bool committed, unsigned arena_ind);
+
+const chunk_hooks_t    chunk_hooks_default = {
+       chunk_alloc_default,
+       chunk_dalloc_default,
+       chunk_commit_default,
+       chunk_decommit_default,
+       chunk_purge_default,
+       chunk_split_default,
+       chunk_merge_default
+};
 
 /******************************************************************************/
 /*
@@ -37,89 +49,263 @@ size_t             arena_maxclass; /* Max size class for arenas. */
  * definition.
  */
 
-static void    chunk_dalloc_core(void *chunk, size_t size);
+static void    chunk_record(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, bool cache,
+    void *chunk, size_t size, bool zeroed, bool committed);
 
 /******************************************************************************/
 
+static chunk_hooks_t
+chunk_hooks_get_locked(arena_t *arena)
+{
+
+       return (arena->chunk_hooks);
+}
+
+chunk_hooks_t
+chunk_hooks_get(arena_t *arena)
+{
+       chunk_hooks_t chunk_hooks;
+
+       malloc_mutex_lock(&arena->chunks_mtx);
+       chunk_hooks = chunk_hooks_get_locked(arena);
+       malloc_mutex_unlock(&arena->chunks_mtx);
+
+       return (chunk_hooks);
+}
+
+chunk_hooks_t
+chunk_hooks_set(arena_t *arena, const chunk_hooks_t *chunk_hooks)
+{
+       chunk_hooks_t old_chunk_hooks;
+
+       malloc_mutex_lock(&arena->chunks_mtx);
+       old_chunk_hooks = arena->chunk_hooks;
+       /*
+        * Copy each field atomically so that it is impossible for readers to
+        * see partially updated pointers.  There are places where readers only
+        * need one hook function pointer (therefore no need to copy the
+        * entirety of arena->chunk_hooks), and stale reads do not affect
+        * correctness, so they perform unlocked reads.
+        */
+#define        ATOMIC_COPY_HOOK(n) do {                                        \
+       union {                                                         \
+               chunk_##n##_t   **n;                                    \
+               void            **v;                                    \
+       } u;                                                            \
+       u.n = &arena->chunk_hooks.n;                                    \
+       atomic_write_p(u.v, chunk_hooks->n);                            \
+} while (0)
+       ATOMIC_COPY_HOOK(alloc);
+       ATOMIC_COPY_HOOK(dalloc);
+       ATOMIC_COPY_HOOK(commit);
+       ATOMIC_COPY_HOOK(decommit);
+       ATOMIC_COPY_HOOK(purge);
+       ATOMIC_COPY_HOOK(split);
+       ATOMIC_COPY_HOOK(merge);
+#undef ATOMIC_COPY_HOOK
+       malloc_mutex_unlock(&arena->chunks_mtx);
+
+       return (old_chunk_hooks);
+}
+
+static void
+chunk_hooks_assure_initialized_impl(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    bool locked)
+{
+       static const chunk_hooks_t uninitialized_hooks =
+           CHUNK_HOOKS_INITIALIZER;
+
+       if (memcmp(chunk_hooks, &uninitialized_hooks, sizeof(chunk_hooks_t)) ==
+           0) {
+               *chunk_hooks = locked ? chunk_hooks_get_locked(arena) :
+                   chunk_hooks_get(arena);
+       }
+}
+
+static void
+chunk_hooks_assure_initialized_locked(arena_t *arena,
+    chunk_hooks_t *chunk_hooks)
+{
+
+       chunk_hooks_assure_initialized_impl(arena, chunk_hooks, true);
+}
+
+static void
+chunk_hooks_assure_initialized(arena_t *arena, chunk_hooks_t *chunk_hooks)
+{
+
+       chunk_hooks_assure_initialized_impl(arena, chunk_hooks, false);
+}
+
+bool
+chunk_register(const void *chunk, const extent_node_t *node)
+{
+
+       assert(extent_node_addr_get(node) == chunk);
+
+       if (rtree_set(&chunks_rtree, (uintptr_t)chunk, node))
+               return (true);
+       if (config_prof && opt_prof) {
+               size_t size = extent_node_size_get(node);
+               size_t nadd = (size == 0) ? 1 : size / chunksize;
+               size_t cur = atomic_add_z(&curchunks, nadd);
+               size_t high = atomic_read_z(&highchunks);
+               while (cur > high && atomic_cas_z(&highchunks, high, cur)) {
+                       /*
+                        * Don't refresh cur, because it may have decreased
+                        * since this thread lost the highchunks update race.
+                        */
+                       high = atomic_read_z(&highchunks);
+               }
+               if (cur > high && prof_gdump_get_unlocked())
+                       prof_gdump();
+       }
+
+       return (false);
+}
+
+void
+chunk_deregister(const void *chunk, const extent_node_t *node)
+{
+       bool err;
+
+       err = rtree_set(&chunks_rtree, (uintptr_t)chunk, NULL);
+       assert(!err);
+       if (config_prof && opt_prof) {
+               size_t size = extent_node_size_get(node);
+               size_t nsub = (size == 0) ? 1 : size / chunksize;
+               assert(atomic_read_z(&curchunks) >= nsub);
+               atomic_sub_z(&curchunks, nsub);
+       }
+}
+
+/*
+ * Do first-best-fit chunk selection, i.e. select the lowest chunk that best
+ * fits.
+ */
+static extent_node_t *
+chunk_first_best_fit(arena_t *arena, extent_tree_t *chunks_szad,
+    extent_tree_t *chunks_ad, size_t size)
+{
+       extent_node_t key;
+
+       assert(size == CHUNK_CEILING(size));
+
+       extent_node_init(&key, arena, NULL, size, false, false);
+       return (extent_tree_szad_nsearch(chunks_szad, &key));
+}
+
 static void *
-chunk_recycle(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad,
-    void *new_addr, size_t size, size_t alignment, bool base, bool *zero)
+chunk_recycle(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, bool cache,
+    void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit,
+    bool dalloc_node)
 {
        void *ret;
        extent_node_t *node;
-       extent_node_t key;
        size_t alloc_size, leadsize, trailsize;
-       bool zeroed;
+       bool zeroed, committed;
 
-       if (base) {
-               /*
-                * This function may need to call base_node_{,de}alloc(), but
-                * the current chunk allocation request is on behalf of the
-                * base allocator.  Avoid deadlock (and if that weren't an
-                * issue, potential for infinite recursion) by returning NULL.
-                */
-               return (NULL);
-       }
+       assert(new_addr == NULL || alignment == chunksize);
+       /*
+        * Cached chunks use the node linkage embedded in their headers, in
+        * which case dalloc_node is true, and new_addr is non-NULL because
+        * we're operating on a specific chunk.
+        */
+       assert(dalloc_node || new_addr != NULL);
 
-       alloc_size = size + alignment - chunksize;
+       alloc_size = CHUNK_CEILING(s2u(size + alignment - chunksize));
        /* Beware size_t wrap-around. */
        if (alloc_size < size)
                return (NULL);
-       key.addr = new_addr;
-       key.size = alloc_size;
-       malloc_mutex_lock(&chunks_mtx);
-       node = extent_tree_szad_nsearch(chunks_szad, &key);
-       if (node == NULL || (new_addr && node->addr != new_addr)) {
-               malloc_mutex_unlock(&chunks_mtx);
+       malloc_mutex_lock(&arena->chunks_mtx);
+       chunk_hooks_assure_initialized_locked(arena, chunk_hooks);
+       if (new_addr != NULL) {
+               extent_node_t key;
+               extent_node_init(&key, arena, new_addr, alloc_size, false,
+                   false);
+               node = extent_tree_ad_search(chunks_ad, &key);
+       } else {
+               node = chunk_first_best_fit(arena, chunks_szad, chunks_ad,
+                   alloc_size);
+       }
+       if (node == NULL || (new_addr != NULL && extent_node_size_get(node) <
+           size)) {
+               malloc_mutex_unlock(&arena->chunks_mtx);
                return (NULL);
        }
-       leadsize = ALIGNMENT_CEILING((uintptr_t)node->addr, alignment) -
-           (uintptr_t)node->addr;
-       assert(node->size >= leadsize + size);
-       trailsize = node->size - leadsize - size;
-       ret = (void *)((uintptr_t)node->addr + leadsize);
-       zeroed = node->zeroed;
+       leadsize = ALIGNMENT_CEILING((uintptr_t)extent_node_addr_get(node),
+           alignment) - (uintptr_t)extent_node_addr_get(node);
+       assert(new_addr == NULL || leadsize == 0);
+       assert(extent_node_size_get(node) >= leadsize + size);
+       trailsize = extent_node_size_get(node) - leadsize - size;
+       ret = (void *)((uintptr_t)extent_node_addr_get(node) + leadsize);
+       zeroed = extent_node_zeroed_get(node);
        if (zeroed)
-           *zero = true;
+               *zero = true;
+       committed = extent_node_committed_get(node);
+       if (committed)
+               *commit = true;
+       /* Split the lead. */
+       if (leadsize != 0 &&
+           chunk_hooks->split(extent_node_addr_get(node),
+           extent_node_size_get(node), leadsize, size, false, arena->ind)) {
+               malloc_mutex_unlock(&arena->chunks_mtx);
+               return (NULL);
+       }
        /* Remove node from the tree. */
        extent_tree_szad_remove(chunks_szad, node);
        extent_tree_ad_remove(chunks_ad, node);
+       arena_chunk_cache_maybe_remove(arena, node, cache);
        if (leadsize != 0) {
                /* Insert the leading space as a smaller chunk. */
-               node->size = leadsize;
+               extent_node_size_set(node, leadsize);
                extent_tree_szad_insert(chunks_szad, node);
                extent_tree_ad_insert(chunks_ad, node);
+               arena_chunk_cache_maybe_insert(arena, node, cache);
                node = NULL;
        }
        if (trailsize != 0) {
+               /* Split the trail. */
+               if (chunk_hooks->split(ret, size + trailsize, size,
+                   trailsize, false, arena->ind)) {
+                       if (dalloc_node && node != NULL)
+                               arena_node_dalloc(arena, node);
+                       malloc_mutex_unlock(&arena->chunks_mtx);
+                       chunk_record(arena, chunk_hooks, chunks_szad, chunks_ad,
+                           cache, ret, size + trailsize, zeroed, committed);
+                       return (NULL);
+               }
                /* Insert the trailing space as a smaller chunk. */
                if (node == NULL) {
-                       /*
-                        * An additional node is required, but
-                        * base_node_alloc() can cause a new base chunk to be
-                        * allocated.  Drop chunks_mtx in order to avoid
-                        * deadlock, and if node allocation fails, deallocate
-                        * the result before returning an error.
-                        */
-                       malloc_mutex_unlock(&chunks_mtx);
-                       node = base_node_alloc();
+                       node = arena_node_alloc(arena);
                        if (node == NULL) {
-                               chunk_dalloc_core(ret, size);
+                               malloc_mutex_unlock(&arena->chunks_mtx);
+                               chunk_record(arena, chunk_hooks, chunks_szad,
+                                   chunks_ad, cache, ret, size + trailsize,
+                                   zeroed, committed);
                                return (NULL);
                        }
-                       malloc_mutex_lock(&chunks_mtx);
                }
-               node->addr = (void *)((uintptr_t)(ret) + size);
-               node->size = trailsize;
-               node->zeroed = zeroed;
+               extent_node_init(node, arena, (void *)((uintptr_t)(ret) + size),
+                   trailsize, zeroed, committed);
                extent_tree_szad_insert(chunks_szad, node);
                extent_tree_ad_insert(chunks_ad, node);
+               arena_chunk_cache_maybe_insert(arena, node, cache);
                node = NULL;
        }
-       malloc_mutex_unlock(&chunks_mtx);
+       if (!committed && chunk_hooks->commit(ret, size, 0, size, arena->ind)) {
+               malloc_mutex_unlock(&arena->chunks_mtx);
+               chunk_record(arena, chunk_hooks, chunks_szad, chunks_ad, cache,
+                   ret, size, zeroed, committed);
+               return (NULL);
+       }
+       malloc_mutex_unlock(&arena->chunks_mtx);
 
-       if (node != NULL)
-               base_node_dalloc(node);
+       assert(dalloc_node || node != NULL);
+       if (dalloc_node && node != NULL)
+               arena_node_dalloc(arena, node);
        if (*zero) {
                if (!zeroed)
                        memset(ret, 0, size);
@@ -142,185 +328,208 @@ chunk_recycle(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad,
  * them if they are returned.
  */
 static void *
-chunk_alloc_core(void *new_addr, size_t size, size_t alignment, bool base,
-    bool *zero, dss_prec_t dss_prec)
+chunk_alloc_core(arena_t *arena, void *new_addr, size_t size, size_t alignment,
+    bool *zero, bool *commit, dss_prec_t dss_prec)
 {
        void *ret;
+       chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER;
 
        assert(size != 0);
        assert((size & chunksize_mask) == 0);
        assert(alignment != 0);
        assert((alignment & chunksize_mask) == 0);
 
+       /* Retained. */
+       if ((ret = chunk_recycle(arena, &chunk_hooks,
+           &arena->chunks_szad_retained, &arena->chunks_ad_retained, false,
+           new_addr, size, alignment, zero, commit, true)) != NULL)
+               return (ret);
+
        /* "primary" dss. */
-       if (have_dss && dss_prec == dss_prec_primary) {
-               if ((ret = chunk_recycle(&chunks_szad_dss, &chunks_ad_dss,
-                   new_addr, size, alignment, base, zero)) != NULL)
-                       return (ret);
-               /* requesting an address only implemented for recycle */
-               if (new_addr == NULL
-                   && (ret = chunk_alloc_dss(size, alignment, zero)) != NULL)
-                       return (ret);
-       }
-       /* mmap. */
-       if ((ret = chunk_recycle(&chunks_szad_mmap, &chunks_ad_mmap, new_addr,
-           size, alignment, base, zero)) != NULL)
+       if (have_dss && dss_prec == dss_prec_primary && (ret =
+           chunk_alloc_dss(arena, new_addr, size, alignment, zero, commit)) !=
+           NULL)
                return (ret);
-       /* requesting an address only implemented for recycle */
-       if (new_addr == NULL &&
-           (ret = chunk_alloc_mmap(size, alignment, zero)) != NULL)
+       /*
+        * mmap.  Requesting an address is not implemented for
+        * chunk_alloc_mmap(), so only call it if (new_addr == NULL).
+        */
+       if (new_addr == NULL && (ret = chunk_alloc_mmap(size, alignment, zero,
+           commit)) != NULL)
                return (ret);
        /* "secondary" dss. */
-       if (have_dss && dss_prec == dss_prec_secondary) {
-               if ((ret = chunk_recycle(&chunks_szad_dss, &chunks_ad_dss,
-                   new_addr, size, alignment, base, zero)) != NULL)
-                       return (ret);
-               /* requesting an address only implemented for recycle */
-               if (new_addr == NULL &&
-                   (ret = chunk_alloc_dss(size, alignment, zero)) != NULL)
-                       return (ret);
-       }
+       if (have_dss && dss_prec == dss_prec_secondary && (ret =
+           chunk_alloc_dss(arena, new_addr, size, alignment, zero, commit)) !=
+           NULL)
+               return (ret);
 
        /* All strategies for allocation failed. */
        return (NULL);
 }
 
-static bool
-chunk_register(void *chunk, size_t size, bool base)
+void *
+chunk_alloc_base(size_t size)
 {
+       void *ret;
+       bool zero, commit;
 
-       assert(chunk != NULL);
-       assert(CHUNK_ADDR2BASE(chunk) == chunk);
-
-       if (config_ivsalloc && !base) {
-               if (rtree_set(chunks_rtree, (uintptr_t)chunk, 1))
-                       return (true);
-       }
-       if (config_stats || config_prof) {
-               bool gdump;
-               malloc_mutex_lock(&chunks_mtx);
-               if (config_stats)
-                       stats_chunks.nchunks += (size / chunksize);
-               stats_chunks.curchunks += (size / chunksize);
-               if (stats_chunks.curchunks > stats_chunks.highchunks) {
-                       stats_chunks.highchunks =
-                           stats_chunks.curchunks;
-                       if (config_prof)
-                               gdump = true;
-               } else if (config_prof)
-                       gdump = false;
-               malloc_mutex_unlock(&chunks_mtx);
-               if (config_prof && opt_prof && opt_prof_gdump && gdump)
-                       prof_gdump();
-       }
+       /*
+        * Directly call chunk_alloc_mmap() rather than chunk_alloc_core()
+        * because it's critical that chunk_alloc_base() return untouched
+        * demand-zeroed virtual memory.
+        */
+       zero = true;
+       commit = true;
+       ret = chunk_alloc_mmap(size, chunksize, &zero, &commit);
+       if (ret == NULL)
+               return (NULL);
        if (config_valgrind)
-               JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(chunk, size);
-       return (false);
+               JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
+
+       return (ret);
 }
 
 void *
-chunk_alloc_base(size_t size)
+chunk_alloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks, void *new_addr,
+    size_t size, size_t alignment, bool *zero, bool dalloc_node)
 {
        void *ret;
-       bool zero;
+       bool commit;
+
+       assert(size != 0);
+       assert((size & chunksize_mask) == 0);
+       assert(alignment != 0);
+       assert((alignment & chunksize_mask) == 0);
 
-       zero = false;
-       ret = chunk_alloc_core(NULL, size, chunksize, true, &zero,
-           chunk_dss_prec_get());
+       commit = true;
+       ret = chunk_recycle(arena, chunk_hooks, &arena->chunks_szad_cached,
+           &arena->chunks_ad_cached, true, new_addr, size, alignment, zero,
+           &commit, dalloc_node);
        if (ret == NULL)
                return (NULL);
-       if (chunk_register(ret, size, true)) {
-               chunk_dalloc_core(ret, size);
-               return (NULL);
-       }
+       assert(commit);
+       if (config_valgrind)
+               JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
        return (ret);
 }
 
-void *
-chunk_alloc_arena(chunk_alloc_t *chunk_alloc, chunk_dalloc_t *chunk_dalloc,
-    unsigned arena_ind, void *new_addr, size_t size, size_t alignment,
-    bool *zero)
+static arena_t *
+chunk_arena_get(unsigned arena_ind)
+{
+       arena_t *arena;
+
+       /* Dodge tsd for a0 in order to avoid bootstrapping issues. */
+       arena = (arena_ind == 0) ? a0get() : arena_get(tsd_fetch(), arena_ind,
+            false, true);
+       /*
+        * The arena we're allocating on behalf of must have been initialized
+        * already.
+        */
+       assert(arena != NULL);
+       return (arena);
+}
+
+static void *
+chunk_alloc_default(void *new_addr, size_t size, size_t alignment, bool *zero,
+    bool *commit, unsigned arena_ind)
 {
        void *ret;
+       arena_t *arena;
 
-       ret = chunk_alloc(new_addr, size, alignment, zero, arena_ind);
-       if (ret != NULL && chunk_register(ret, size, false)) {
-               chunk_dalloc(ret, size, arena_ind);
-               ret = NULL;
-       }
+       arena = chunk_arena_get(arena_ind);
+       ret = chunk_alloc_core(arena, new_addr, size, alignment, zero,
+           commit, arena->dss_prec);
+       if (ret == NULL)
+               return (NULL);
+       if (config_valgrind)
+               JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
 
        return (ret);
 }
 
-/* Default arena chunk allocation routine in the absence of user override. */
 void *
-chunk_alloc_default(void *new_addr, size_t size, size_t alignment, bool *zero,
-    unsigned arena_ind)
+chunk_alloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, void *new_addr,
+    size_t size, size_t alignment, bool *zero, bool *commit)
 {
+       void *ret;
 
-       return (chunk_alloc_core(new_addr, size, alignment, false, zero,
-           arenas[arena_ind]->dss_prec));
+       chunk_hooks_assure_initialized(arena, chunk_hooks);
+       ret = chunk_hooks->alloc(new_addr, size, alignment, zero, commit,
+           arena->ind);
+       if (ret == NULL)
+               return (NULL);
+       if (config_valgrind && chunk_hooks->alloc != chunk_alloc_default)
+               JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, chunksize);
+       return (ret);
 }
 
 static void
-chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk,
-    size_t size)
+chunk_record(arena_t *arena, chunk_hooks_t *chunk_hooks,
+    extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, bool cache,
+    void *chunk, size_t size, bool zeroed, bool committed)
 {
        bool unzeroed;
-       extent_node_t *xnode, *node, *prev, *xprev, key;
+       extent_node_t *node, *prev;
+       extent_node_t key;
 
-       unzeroed = pages_purge(chunk, size);
+       assert(!cache || !zeroed);
+       unzeroed = cache || !zeroed;
        JEMALLOC_VALGRIND_MAKE_MEM_NOACCESS(chunk, size);
 
-       /*
-        * Allocate a node before acquiring chunks_mtx even though it might not
-        * be needed, because base_node_alloc() may cause a new base chunk to
-        * be allocated, which could cause deadlock if chunks_mtx were already
-        * held.
-        */
-       xnode = base_node_alloc();
-       /* Use xprev to implement conditional deferred deallocation of prev. */
-       xprev = NULL;
-
-       malloc_mutex_lock(&chunks_mtx);
-       key.addr = (void *)((uintptr_t)chunk + size);
+       malloc_mutex_lock(&arena->chunks_mtx);
+       chunk_hooks_assure_initialized_locked(arena, chunk_hooks);
+       extent_node_init(&key, arena, (void *)((uintptr_t)chunk + size), 0,
+           false, false);
        node = extent_tree_ad_nsearch(chunks_ad, &key);
        /* Try to coalesce forward. */
-       if (node != NULL && node->addr == key.addr) {
+       if (node != NULL && extent_node_addr_get(node) ==
+           extent_node_addr_get(&key) && extent_node_committed_get(node) ==
+           committed && !chunk_hooks->merge(chunk, size,
+           extent_node_addr_get(node), extent_node_size_get(node), false,
+           arena->ind)) {
                /*
                 * Coalesce chunk with the following address range.  This does
                 * not change the position within chunks_ad, so only
                 * remove/insert from/into chunks_szad.
                 */
                extent_tree_szad_remove(chunks_szad, node);
-               node->addr = chunk;
-               node->size += size;
-               node->zeroed = (node->zeroed && !unzeroed);
+               arena_chunk_cache_maybe_remove(arena, node, cache);
+               extent_node_addr_set(node, chunk);
+               extent_node_size_set(node, size + extent_node_size_get(node));
+               extent_node_zeroed_set(node, extent_node_zeroed_get(node) &&
+                   !unzeroed);
                extent_tree_szad_insert(chunks_szad, node);
+               arena_chunk_cache_maybe_insert(arena, node, cache);
        } else {
                /* Coalescing forward failed, so insert a new node. */
-               if (xnode == NULL) {
+               node = arena_node_alloc(arena);
+               if (node == NULL) {
                        /*
-                        * base_node_alloc() failed, which is an exceedingly
-                        * unlikely failure.  Leak chunk; its pages have
-                        * already been purged, so this is only a virtual
-                        * memory leak.
+                        * Node allocation failed, which is an exceedingly
+                        * unlikely failure.  Leak chunk after making sure its
+                        * pages have already been purged, so that this is only
+                        * a virtual memory leak.
                         */
+                       if (cache) {
+                               chunk_purge_wrapper(arena, chunk_hooks, chunk,
+                                   size, 0, size);
+                       }
                        goto label_return;
                }
-               node = xnode;
-               xnode = NULL; /* Prevent deallocation below. */
-               node->addr = chunk;
-               node->size = size;
-               node->zeroed = !unzeroed;
+               extent_node_init(node, arena, chunk, size, !unzeroed,
+                   committed);
                extent_tree_ad_insert(chunks_ad, node);
                extent_tree_szad_insert(chunks_szad, node);
+               arena_chunk_cache_maybe_insert(arena, node, cache);
        }
 
        /* Try to coalesce backward. */
        prev = extent_tree_ad_prev(chunks_ad, node);
-       if (prev != NULL && (void *)((uintptr_t)prev->addr + prev->size) ==
-           chunk) {
+       if (prev != NULL && (void *)((uintptr_t)extent_node_addr_get(prev) +
+           extent_node_size_get(prev)) == chunk &&
+           extent_node_committed_get(prev) == committed &&
+           !chunk_hooks->merge(extent_node_addr_get(prev),
+           extent_node_size_get(prev), chunk, size, false, arena->ind)) {
                /*
                 * Coalesce chunk with the previous address range.  This does
                 * not change the position within chunks_ad, so only
@@ -328,44 +537,42 @@ chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk,
                 */
                extent_tree_szad_remove(chunks_szad, prev);
                extent_tree_ad_remove(chunks_ad, prev);
-
+               arena_chunk_cache_maybe_remove(arena, prev, cache);
                extent_tree_szad_remove(chunks_szad, node);
-               node->addr = prev->addr;
-               node->size += prev->size;
-               node->zeroed = (node->zeroed && prev->zeroed);
+               arena_chunk_cache_maybe_remove(arena, node, cache);
+               extent_node_addr_set(node, extent_node_addr_get(prev));
+               extent_node_size_set(node, extent_node_size_get(prev) +
+                   extent_node_size_get(node));
+               extent_node_zeroed_set(node, extent_node_zeroed_get(prev) &&
+                   extent_node_zeroed_get(node));
                extent_tree_szad_insert(chunks_szad, node);
+               arena_chunk_cache_maybe_insert(arena, node, cache);
 
-               xprev = prev;
+               arena_node_dalloc(arena, prev);
        }
 
 label_return:
-       malloc_mutex_unlock(&chunks_mtx);
-       /*
-        * Deallocate xnode and/or xprev after unlocking chunks_mtx in order to
-        * avoid potential deadlock.
-        */
-       if (xnode != NULL)
-               base_node_dalloc(xnode);
-       if (xprev != NULL)
-               base_node_dalloc(xprev);
+       malloc_mutex_unlock(&arena->chunks_mtx);
 }
 
 void
-chunk_unmap(void *chunk, size_t size)
+chunk_dalloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk,
+    size_t size, bool committed)
 {
+
        assert(chunk != NULL);
        assert(CHUNK_ADDR2BASE(chunk) == chunk);
        assert(size != 0);
        assert((size & chunksize_mask) == 0);
 
-       if (have_dss && chunk_in_dss(chunk))
-               chunk_record(&chunks_szad_dss, &chunks_ad_dss, chunk, size);
-       else if (chunk_dalloc_mmap(chunk, size))
-               chunk_record(&chunks_szad_mmap, &chunks_ad_mmap, chunk, size);
+       chunk_record(arena, chunk_hooks, &arena->chunks_szad_cached,
+           &arena->chunks_ad_cached, true, chunk, size, false, committed);
+       arena_maybe_purge(arena);
 }
 
-static void
-chunk_dalloc_core(void *chunk, size_t size)
+void
+chunk_dalloc_arena(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk,
+    size_t size, bool zeroed, bool committed)
 {
 
        assert(chunk != NULL);
@@ -373,30 +580,149 @@ chunk_dalloc_core(void *chunk, size_t size)
        assert(size != 0);
        assert((size & chunksize_mask) == 0);
 
-       if (config_ivsalloc)
-               rtree_set(chunks_rtree, (uintptr_t)chunk, 0);
-       if (config_stats || config_prof) {
-               malloc_mutex_lock(&chunks_mtx);
-               assert(stats_chunks.curchunks >= (size / chunksize));
-               stats_chunks.curchunks -= (size / chunksize);
-               malloc_mutex_unlock(&chunks_mtx);
+       chunk_hooks_assure_initialized(arena, chunk_hooks);
+       /* Try to deallocate. */
+       if (!chunk_hooks->dalloc(chunk, size, committed, arena->ind))
+               return;
+       /* Try to decommit; purge if that fails. */
+       if (committed) {
+               committed = chunk_hooks->decommit(chunk, size, 0, size,
+                   arena->ind);
        }
+       zeroed = !committed || !chunk_hooks->purge(chunk, size, 0, size,
+           arena->ind);
+       chunk_record(arena, chunk_hooks, &arena->chunks_szad_retained,
+           &arena->chunks_ad_retained, false, chunk, size, zeroed, committed);
+}
+
+static bool
+chunk_dalloc_default(void *chunk, size_t size, bool committed,
+    unsigned arena_ind)
+{
+
+       if (!have_dss || !chunk_in_dss(chunk))
+               return (chunk_dalloc_mmap(chunk, size));
+       return (true);
+}
+
+void
+chunk_dalloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk,
+    size_t size, bool committed)
+{
+
+       chunk_hooks_assure_initialized(arena, chunk_hooks);
+       chunk_hooks->dalloc(chunk, size, committed, arena->ind);
+       if (config_valgrind && chunk_hooks->dalloc != chunk_dalloc_default)
+               JEMALLOC_VALGRIND_MAKE_MEM_NOACCESS(chunk, size);
+}
+
+static bool
+chunk_commit_default(void *chunk, size_t size, size_t offset, size_t length,
+    unsigned arena_ind)
+{
+
+       return (pages_commit((void *)((uintptr_t)chunk + (uintptr_t)offset),
+           length));
+}
+
+static bool
+chunk_decommit_default(void *chunk, size_t size, size_t offset, size_t length,
+    unsigned arena_ind)
+{
+
+       return (pages_decommit((void *)((uintptr_t)chunk + (uintptr_t)offset),
+           length));
+}
+
+bool
+chunk_purge_arena(arena_t *arena, void *chunk, size_t offset, size_t length)
+{
 
-       chunk_unmap(chunk, size);
+       assert(chunk != NULL);
+       assert(CHUNK_ADDR2BASE(chunk) == chunk);
+       assert((offset & PAGE_MASK) == 0);
+       assert(length != 0);
+       assert((length & PAGE_MASK) == 0);
+
+       return (pages_purge((void *)((uintptr_t)chunk + (uintptr_t)offset),
+           length));
+}
+
+static bool
+chunk_purge_default(void *chunk, size_t size, size_t offset, size_t length,
+    unsigned arena_ind)
+{
+
+       return (chunk_purge_arena(chunk_arena_get(arena_ind), chunk, offset,
+           length));
 }
 
-/* Default arena chunk deallocation routine in the absence of user override. */
 bool
-chunk_dalloc_default(void *chunk, size_t size, unsigned arena_ind)
+chunk_purge_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk,
+    size_t size, size_t offset, size_t length)
+{
+
+       chunk_hooks_assure_initialized(arena, chunk_hooks);
+       return (chunk_hooks->purge(chunk, size, offset, length, arena->ind));
+}
+
+static bool
+chunk_split_default(void *chunk, size_t size, size_t size_a, size_t size_b,
+    bool committed, unsigned arena_ind)
 {
 
-       chunk_dalloc_core(chunk, size);
+       if (!maps_coalesce)
+               return (true);
        return (false);
 }
 
+static bool
+chunk_merge_default(void *chunk_a, size_t size_a, void *chunk_b, size_t size_b,
+    bool committed, unsigned arena_ind)
+{
+
+       if (!maps_coalesce)
+               return (true);
+       if (have_dss && chunk_in_dss(chunk_a) != chunk_in_dss(chunk_b))
+               return (true);
+
+       return (false);
+}
+
+static rtree_node_elm_t *
+chunks_rtree_node_alloc(size_t nelms)
+{
+
+       return ((rtree_node_elm_t *)base_alloc(nelms *
+           sizeof(rtree_node_elm_t)));
+}
+
 bool
 chunk_boot(void)
 {
+#ifdef _WIN32
+       SYSTEM_INFO info;
+       GetSystemInfo(&info);
+
+       /*
+        * Verify actual page size is equal to or an integral multiple of
+        * configured page size.
+        */
+       if (info.dwPageSize & ((1U << LG_PAGE) - 1))
+               return (true);
+
+       /*
+        * Configure chunksize (if not set) to match granularity (usually 64K),
+        * so pages_map will always take fast path.
+        */
+       if (!opt_lg_chunk) {
+               opt_lg_chunk = jemalloc_ffs((int)info.dwAllocationGranularity)
+                   - 1;
+       }
+#else
+       if (!opt_lg_chunk)
+               opt_lg_chunk = LG_CHUNK_DEFAULT;
+#endif
 
        /* Set variables according to the value of opt_lg_chunk. */
        chunksize = (ZU(1) << opt_lg_chunk);
@@ -404,23 +730,11 @@ chunk_boot(void)
        chunksize_mask = chunksize - 1;
        chunk_npages = (chunksize >> LG_PAGE);
 
-       if (config_stats || config_prof) {
-               if (malloc_mutex_init(&chunks_mtx))
-                       return (true);
-               memset(&stats_chunks, 0, sizeof(chunk_stats_t));
-       }
        if (have_dss && chunk_dss_boot())
                return (true);
-       extent_tree_szad_new(&chunks_szad_mmap);
-       extent_tree_ad_new(&chunks_ad_mmap);
-       extent_tree_szad_new(&chunks_szad_dss);
-       extent_tree_ad_new(&chunks_ad_dss);
-       if (config_ivsalloc) {
-               chunks_rtree = rtree_new((ZU(1) << (LG_SIZEOF_PTR+3)) -
-                   opt_lg_chunk, base_alloc, NULL);
-               if (chunks_rtree == NULL)
-                       return (true);
-       }
+       if (rtree_new(&chunks_rtree, (ZU(1) << (LG_SIZEOF_PTR+3)) -
+           opt_lg_chunk, chunks_rtree_node_alloc, NULL))
+               return (true);
 
        return (false);
 }
@@ -429,9 +743,6 @@ void
 chunk_prefork(void)
 {
 
-       malloc_mutex_prefork(&chunks_mtx);
-       if (config_ivsalloc)
-               rtree_prefork(chunks_rtree);
        chunk_dss_prefork();
 }
 
@@ -440,9 +751,6 @@ chunk_postfork_parent(void)
 {
 
        chunk_dss_postfork_parent();
-       if (config_ivsalloc)
-               rtree_postfork_parent(chunks_rtree);
-       malloc_mutex_postfork_parent(&chunks_mtx);
 }
 
 void
@@ -450,7 +758,4 @@ chunk_postfork_child(void)
 {
 
        chunk_dss_postfork_child();
-       if (config_ivsalloc)
-               rtree_postfork_child(chunks_rtree);
-       malloc_mutex_postfork_child(&chunks_mtx);
 }
index cce71041c4f61742bed4432c885c2eeec2fd6497..61fc91696192476304939721e8c338779575614f 100644 (file)
@@ -66,10 +66,9 @@ chunk_dss_prec_set(dss_prec_t dss_prec)
 }
 
 void *
-chunk_alloc_dss(size_t size, size_t alignment, bool *zero)
+chunk_alloc_dss(arena_t *arena, void *new_addr, size_t size, size_t alignment,
+    bool *zero, bool *commit)
 {
-       void *ret;
-
        cassert(have_dss);
        assert(size > 0 && (size & chunksize_mask) == 0);
        assert(alignment > 0 && (alignment & chunksize_mask) == 0);
@@ -83,9 +82,6 @@ chunk_alloc_dss(size_t size, size_t alignment, bool *zero)
 
        malloc_mutex_lock(&dss_mtx);
        if (dss_prev != (void *)-1) {
-               size_t gap_size, cpad_size;
-               void *cpad, *dss_next;
-               intptr_t incr;
 
                /*
                 * The loop is necessary to recover from races with other
@@ -93,8 +89,20 @@ chunk_alloc_dss(size_t size, size_t alignment, bool *zero)
                 * malloc.
                 */
                do {
+                       void *ret, *cpad, *dss_next;
+                       size_t gap_size, cpad_size;
+                       intptr_t incr;
+                       /* Avoid an unnecessary system call. */
+                       if (new_addr != NULL && dss_max != new_addr)
+                               break;
+
                        /* Get the current end of the DSS. */
                        dss_max = chunk_dss_sbrk(0);
+
+                       /* Make sure the earlier condition still holds. */
+                       if (new_addr != NULL && dss_max != new_addr)
+                               break;
+
                        /*
                         * Calculate how much padding is necessary to
                         * chunk-align the end of the DSS.
@@ -123,13 +131,20 @@ chunk_alloc_dss(size_t size, size_t alignment, bool *zero)
                                /* Success. */
                                dss_max = dss_next;
                                malloc_mutex_unlock(&dss_mtx);
-                               if (cpad_size != 0)
-                                       chunk_unmap(cpad, cpad_size);
+                               if (cpad_size != 0) {
+                                       chunk_hooks_t chunk_hooks =
+                                           CHUNK_HOOKS_INITIALIZER;
+                                       chunk_dalloc_wrapper(arena,
+                                           &chunk_hooks, cpad, cpad_size,
+                                           true);
+                               }
                                if (*zero) {
                                        JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(
                                            ret, size);
                                        memset(ret, 0, size);
                                }
+                               if (!*commit)
+                                       *commit = pages_decommit(ret, size);
                                return (ret);
                        }
                } while (dss_prev != (void *)-1);
index 7e02c10223ea93b4cb4a0a9cb07e2883e944a931..b9ba74191a416a1eb5a994dce5f194d0bde6d561 100644 (file)
 #define        JEMALLOC_CHUNK_MMAP_C_
 #include "jemalloc/internal/jemalloc_internal.h"
 
-/******************************************************************************/
-/* Function prototypes for non-inline static functions. */
-
-static void    *pages_map(void *addr, size_t size);
-static void    pages_unmap(void *addr, size_t size);
-static void    *chunk_alloc_mmap_slow(size_t size, size_t alignment,
-    bool *zero);
-
 /******************************************************************************/
 
 static void *
-pages_map(void *addr, size_t size)
+chunk_alloc_mmap_slow(size_t size, size_t alignment, bool *zero, bool *commit)
 {
        void *ret;
-
-       assert(size != 0);
-
-#ifdef _WIN32
-       /*
-        * If VirtualAlloc can't allocate at the given address when one is
-        * given, it fails and returns NULL.
-        */
-       ret = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE,
-           PAGE_READWRITE);
-#else
-       /*
-        * We don't use MAP_FIXED here, because it can cause the *replacement*
-        * of existing mappings, and we only want to create new mappings.
-        */
-       ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
-           -1, 0);
-       assert(ret != NULL);
-
-       if (ret == MAP_FAILED)
-               ret = NULL;
-       else if (addr != NULL && ret != addr) {
-               /*
-                * We succeeded in mapping memory, but not in the right place.
-                */
-               if (munmap(ret, size) == -1) {
-                       char buf[BUFERROR_BUF];
-
-                       buferror(get_errno(), buf, sizeof(buf));
-                       malloc_printf("<jemalloc: Error in munmap(): %s\n",
-                           buf);
-                       if (opt_abort)
-                               abort();
-               }
-               ret = NULL;
-       }
-#endif
-       assert(ret == NULL || (addr == NULL && ret != addr)
-           || (addr != NULL && ret == addr));
-       return (ret);
-}
-
-static void
-pages_unmap(void *addr, size_t size)
-{
-
-#ifdef _WIN32
-       if (VirtualFree(addr, 0, MEM_RELEASE) == 0)
-#else
-       if (munmap(addr, size) == -1)
-#endif
-       {
-               char buf[BUFERROR_BUF];
-
-               buferror(get_errno(), buf, sizeof(buf));
-               malloc_printf("<jemalloc>: Error in "
-#ifdef _WIN32
-                             "VirtualFree"
-#else
-                             "munmap"
-#endif
-                             "(): %s\n", buf);
-               if (opt_abort)
-                       abort();
-       }
-}
-
-static void *
-pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size)
-{
-       void *ret = (void *)((uintptr_t)addr + leadsize);
-
-       assert(alloc_size >= leadsize + size);
-#ifdef _WIN32
-       {
-               void *new_addr;
-
-               pages_unmap(addr, alloc_size);
-               new_addr = pages_map(ret, size);
-               if (new_addr == ret)
-                       return (ret);
-               if (new_addr)
-                       pages_unmap(new_addr, size);
-               return (NULL);
-       }
-#else
-       {
-               size_t trailsize = alloc_size - leadsize - size;
-
-               if (leadsize != 0)
-                       pages_unmap(addr, leadsize);
-               if (trailsize != 0)
-                       pages_unmap((void *)((uintptr_t)ret + size), trailsize);
-               return (ret);
-       }
-#endif
-}
-
-bool
-pages_purge(void *addr, size_t length)
-{
-       bool unzeroed;
-
-#ifdef _WIN32
-       VirtualAlloc(addr, length, MEM_RESET, PAGE_READWRITE);
-       unzeroed = true;
-#elif defined(JEMALLOC_HAVE_MADVISE)
-#  ifdef JEMALLOC_PURGE_MADVISE_DONTNEED
-#    define JEMALLOC_MADV_PURGE MADV_DONTNEED
-#    define JEMALLOC_MADV_ZEROS true
-#  elif defined(JEMALLOC_PURGE_MADVISE_FREE)
-#    define JEMALLOC_MADV_PURGE MADV_FREE
-#    define JEMALLOC_MADV_ZEROS false
-#  else
-#    error "No madvise(2) flag defined for purging unused dirty pages."
-#  endif
-       int err = madvise(addr, length, JEMALLOC_MADV_PURGE);
-       unzeroed = (!JEMALLOC_MADV_ZEROS || err != 0);
-#  undef JEMALLOC_MADV_PURGE
-#  undef JEMALLOC_MADV_ZEROS
-#else
-       /* Last resort no-op. */
-       unzeroed = true;
-#endif
-       return (unzeroed);
-}
-
-static void *
-chunk_alloc_mmap_slow(size_t size, size_t alignment, bool *zero)
-{
-       void *ret, *pages;
-       size_t alloc_size, leadsize;
+       size_t alloc_size;
 
        alloc_size = size + alignment - PAGE;
        /* Beware size_t wrap-around. */
        if (alloc_size < size)
                return (NULL);
        do {
+               void *pages;
+               size_t leadsize;
                pages = pages_map(NULL, alloc_size);
                if (pages == NULL)
                        return (NULL);
@@ -163,11 +26,13 @@ chunk_alloc_mmap_slow(size_t size, size_t alignment, bool *zero)
 
        assert(ret != NULL);
        *zero = true;
+       if (!*commit)
+               *commit = pages_decommit(ret, size);
        return (ret);
 }
 
 void *
-chunk_alloc_mmap(size_t size, size_t alignment, bool *zero)
+chunk_alloc_mmap(size_t size, size_t alignment, bool *zero, bool *commit)
 {
        void *ret;
        size_t offset;
@@ -194,11 +59,13 @@ chunk_alloc_mmap(size_t size, size_t alignment, bool *zero)
        offset = ALIGNMENT_ADDR2OFFSET(ret, alignment);
        if (offset != 0) {
                pages_unmap(ret, size);
-               return (chunk_alloc_mmap_slow(size, alignment, zero));
+               return (chunk_alloc_mmap_slow(size, alignment, zero, commit));
        }
 
        assert(ret != NULL);
        *zero = true;
+       if (!*commit)
+               *commit = pages_decommit(ret, size);
        return (ret);
 }
 
index 3a545966abbf3c03e50bf8bf63f896ba34696d0c..53a1c1ef11d292d0db81c859d763c29e6c9043ea 100644 (file)
@@ -270,7 +270,8 @@ ckh_grow(tsd_t *tsd, ckh_t *ckh)
                        ret = true;
                        goto label_return;
                }
-               tab = (ckhc_t *)ipalloc(tsd, usize, CACHELINE, true);
+               tab = (ckhc_t *)ipallocztm(tsd, usize, CACHELINE, true, NULL,
+                   true, NULL);
                if (tab == NULL) {
                        ret = true;
                        goto label_return;
@@ -282,12 +283,12 @@ ckh_grow(tsd_t *tsd, ckh_t *ckh)
                ckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS;
 
                if (!ckh_rebuild(ckh, tab)) {
-                       idalloc(tsd, tab);
+                       idalloctm(tsd, tab, tcache_get(tsd, false), true);
                        break;
                }
 
                /* Rebuilding failed, so back out partially rebuilt table. */
-               idalloc(tsd, ckh->tab);
+               idalloctm(tsd, ckh->tab, tcache_get(tsd, false), true);
                ckh->tab = tab;
                ckh->lg_curbuckets = lg_prevbuckets;
        }
@@ -313,7 +314,8 @@ ckh_shrink(tsd_t *tsd, ckh_t *ckh)
        usize = sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE);
        if (usize == 0)
                return;
-       tab = (ckhc_t *)ipalloc(tsd, usize, CACHELINE, true);
+       tab = (ckhc_t *)ipallocztm(tsd, usize, CACHELINE, true, NULL, true,
+           NULL);
        if (tab == NULL) {
                /*
                 * An OOM error isn't worth propagating, since it doesn't
@@ -328,7 +330,7 @@ ckh_shrink(tsd_t *tsd, ckh_t *ckh)
        ckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS;
 
        if (!ckh_rebuild(ckh, tab)) {
-               idalloc(tsd, tab);
+               idalloctm(tsd, tab, tcache_get(tsd, false), true);
 #ifdef CKH_COUNT
                ckh->nshrinks++;
 #endif
@@ -336,7 +338,7 @@ ckh_shrink(tsd_t *tsd, ckh_t *ckh)
        }
 
        /* Rebuilding failed, so back out partially rebuilt table. */
-       idalloc(tsd, ckh->tab);
+       idalloctm(tsd, ckh->tab, tcache_get(tsd, false), true);
        ckh->tab = tab;
        ckh->lg_curbuckets = lg_prevbuckets;
 #ifdef CKH_COUNT
@@ -367,10 +369,10 @@ ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash,
        ckh->count = 0;
 
        /*
-        * Find the minimum power of 2 that is large enough to fit aBaseCount
+        * Find the minimum power of 2 that is large enough to fit minitems
         * entries.  We are using (2+,2) cuckoo hashing, which has an expected
         * maximum load factor of at least ~0.86, so 0.75 is a conservative load
-        * factor that will typically allow 2^aLgMinItems to fit without ever
+        * factor that will typically allow mincells items to fit without ever
         * growing the table.
         */
        assert(LG_CKH_BUCKET_CELLS > 0);
@@ -389,7 +391,8 @@ ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash,
                ret = true;
                goto label_return;
        }
-       ckh->tab = (ckhc_t *)ipalloc(tsd, usize, CACHELINE, true);
+       ckh->tab = (ckhc_t *)ipallocztm(tsd, usize, CACHELINE, true, NULL, true,
+           NULL);
        if (ckh->tab == NULL) {
                ret = true;
                goto label_return;
@@ -408,9 +411,9 @@ ckh_delete(tsd_t *tsd, ckh_t *ckh)
 
 #ifdef CKH_VERBOSE
        malloc_printf(
-           "%s(%p): ngrows: %"PRIu64", nshrinks: %"PRIu64","
-           " nshrinkfails: %"PRIu64", ninserts: %"PRIu64","
-           " nrelocs: %"PRIu64"\n", __func__, ckh,
+           "%s(%p): ngrows: %"FMTu64", nshrinks: %"FMTu64","
+           " nshrinkfails: %"FMTu64", ninserts: %"FMTu64","
+           " nrelocs: %"FMTu64"\n", __func__, ckh,
            (unsigned long long)ckh->ngrows,
            (unsigned long long)ckh->nshrinks,
            (unsigned long long)ckh->nshrinkfails,
@@ -418,7 +421,7 @@ ckh_delete(tsd_t *tsd, ckh_t *ckh)
            (unsigned long long)ckh->nrelocs);
 #endif
 
-       idalloc(tsd, ckh->tab);
+       idalloctm(tsd, ckh->tab, tcache_get(tsd, false), true);
        if (config_debug)
                memset(ckh, 0x5a, sizeof(ckh_t));
 }
index 309f1f65d9858cf451d53bedcb86785e36052f79..3de8e602d11f414a86860cbf1514a8a473fb3f2a 100644 (file)
@@ -16,14 +16,14 @@ static ctl_stats_t  ctl_stats;
 /******************************************************************************/
 /* Helpers for named and indexed nodes. */
 
-static inline const ctl_named_node_t *
+JEMALLOC_INLINE_C const ctl_named_node_t *
 ctl_named_node(const ctl_node_t *node)
 {
 
        return ((node->named) ? (const ctl_named_node_t *)node : NULL);
 }
 
-static inline const ctl_named_node_t *
+JEMALLOC_INLINE_C const ctl_named_node_t *
 ctl_named_children(const ctl_named_node_t *node, int index)
 {
        const ctl_named_node_t *children = ctl_named_node(node->children);
@@ -31,7 +31,7 @@ ctl_named_children(const ctl_named_node_t *node, int index)
        return (children ? &children[index] : NULL);
 }
 
-static inline const ctl_indexed_node_t *
+JEMALLOC_INLINE_C const ctl_indexed_node_t *
 ctl_indexed_node(const ctl_node_t *node)
 {
 
@@ -73,6 +73,7 @@ CTL_PROTO(thread_allocated)
 CTL_PROTO(thread_allocatedp)
 CTL_PROTO(thread_deallocated)
 CTL_PROTO(thread_deallocatedp)
+CTL_PROTO(config_cache_oblivious)
 CTL_PROTO(config_debug)
 CTL_PROTO(config_fill)
 CTL_PROTO(config_lazy_lock)
@@ -110,11 +111,14 @@ CTL_PROTO(opt_prof_gdump)
 CTL_PROTO(opt_prof_final)
 CTL_PROTO(opt_prof_leak)
 CTL_PROTO(opt_prof_accum)
+CTL_PROTO(tcache_create)
+CTL_PROTO(tcache_flush)
+CTL_PROTO(tcache_destroy)
 CTL_PROTO(arena_i_purge)
 static void    arena_purge(unsigned arena_ind);
 CTL_PROTO(arena_i_dss)
-CTL_PROTO(arena_i_chunk_alloc)
-CTL_PROTO(arena_i_chunk_dalloc)
+CTL_PROTO(arena_i_lg_dirty_mult)
+CTL_PROTO(arena_i_chunk_hooks)
 INDEX_PROTO(arena_i)
 CTL_PROTO(arenas_bin_i_size)
 CTL_PROTO(arenas_bin_i_nregs)
@@ -122,24 +126,26 @@ CTL_PROTO(arenas_bin_i_run_size)
 INDEX_PROTO(arenas_bin_i)
 CTL_PROTO(arenas_lrun_i_size)
 INDEX_PROTO(arenas_lrun_i)
+CTL_PROTO(arenas_hchunk_i_size)
+INDEX_PROTO(arenas_hchunk_i)
 CTL_PROTO(arenas_narenas)
 CTL_PROTO(arenas_initialized)
+CTL_PROTO(arenas_lg_dirty_mult)
 CTL_PROTO(arenas_quantum)
 CTL_PROTO(arenas_page)
 CTL_PROTO(arenas_tcache_max)
 CTL_PROTO(arenas_nbins)
 CTL_PROTO(arenas_nhbins)
 CTL_PROTO(arenas_nlruns)
+CTL_PROTO(arenas_nhchunks)
 CTL_PROTO(arenas_extend)
 CTL_PROTO(prof_thread_active_init)
 CTL_PROTO(prof_active)
 CTL_PROTO(prof_dump)
+CTL_PROTO(prof_gdump)
 CTL_PROTO(prof_reset)
 CTL_PROTO(prof_interval)
 CTL_PROTO(lg_prof_sample)
-CTL_PROTO(stats_chunks_current)
-CTL_PROTO(stats_chunks_total)
-CTL_PROTO(stats_chunks_high)
 CTL_PROTO(stats_arenas_i_small_allocated)
 CTL_PROTO(stats_arenas_i_small_nmalloc)
 CTL_PROTO(stats_arenas_i_small_ndalloc)
@@ -152,10 +158,10 @@ CTL_PROTO(stats_arenas_i_huge_allocated)
 CTL_PROTO(stats_arenas_i_huge_nmalloc)
 CTL_PROTO(stats_arenas_i_huge_ndalloc)
 CTL_PROTO(stats_arenas_i_huge_nrequests)
-CTL_PROTO(stats_arenas_i_bins_j_allocated)
 CTL_PROTO(stats_arenas_i_bins_j_nmalloc)
 CTL_PROTO(stats_arenas_i_bins_j_ndalloc)
 CTL_PROTO(stats_arenas_i_bins_j_nrequests)
+CTL_PROTO(stats_arenas_i_bins_j_curregs)
 CTL_PROTO(stats_arenas_i_bins_j_nfills)
 CTL_PROTO(stats_arenas_i_bins_j_nflushes)
 CTL_PROTO(stats_arenas_i_bins_j_nruns)
@@ -167,18 +173,28 @@ CTL_PROTO(stats_arenas_i_lruns_j_ndalloc)
 CTL_PROTO(stats_arenas_i_lruns_j_nrequests)
 CTL_PROTO(stats_arenas_i_lruns_j_curruns)
 INDEX_PROTO(stats_arenas_i_lruns_j)
+CTL_PROTO(stats_arenas_i_hchunks_j_nmalloc)
+CTL_PROTO(stats_arenas_i_hchunks_j_ndalloc)
+CTL_PROTO(stats_arenas_i_hchunks_j_nrequests)
+CTL_PROTO(stats_arenas_i_hchunks_j_curhchunks)
+INDEX_PROTO(stats_arenas_i_hchunks_j)
 CTL_PROTO(stats_arenas_i_nthreads)
 CTL_PROTO(stats_arenas_i_dss)
+CTL_PROTO(stats_arenas_i_lg_dirty_mult)
 CTL_PROTO(stats_arenas_i_pactive)
 CTL_PROTO(stats_arenas_i_pdirty)
 CTL_PROTO(stats_arenas_i_mapped)
 CTL_PROTO(stats_arenas_i_npurge)
 CTL_PROTO(stats_arenas_i_nmadvise)
 CTL_PROTO(stats_arenas_i_purged)
+CTL_PROTO(stats_arenas_i_metadata_mapped)
+CTL_PROTO(stats_arenas_i_metadata_allocated)
 INDEX_PROTO(stats_arenas_i)
 CTL_PROTO(stats_cactive)
 CTL_PROTO(stats_allocated)
 CTL_PROTO(stats_active)
+CTL_PROTO(stats_metadata)
+CTL_PROTO(stats_resident)
 CTL_PROTO(stats_mapped)
 
 /******************************************************************************/
@@ -221,60 +237,63 @@ static const ctl_named_node_t     thread_node[] = {
 };
 
 static const ctl_named_node_t  config_node[] = {
-       {NAME("debug"),                 CTL(config_debug)},
-       {NAME("fill"),                  CTL(config_fill)},
-       {NAME("lazy_lock"),             CTL(config_lazy_lock)},
-       {NAME("munmap"),                CTL(config_munmap)},
-       {NAME("prof"),                  CTL(config_prof)},
-       {NAME("prof_libgcc"),           CTL(config_prof_libgcc)},
-       {NAME("prof_libunwind"),        CTL(config_prof_libunwind)},
-       {NAME("stats"),                 CTL(config_stats)},
-       {NAME("tcache"),                CTL(config_tcache)},
-       {NAME("tls"),                   CTL(config_tls)},
-       {NAME("utrace"),                CTL(config_utrace)},
-       {NAME("valgrind"),              CTL(config_valgrind)},
-       {NAME("xmalloc"),               CTL(config_xmalloc)}
+       {NAME("cache_oblivious"), CTL(config_cache_oblivious)},
+       {NAME("debug"),         CTL(config_debug)},
+       {NAME("fill"),          CTL(config_fill)},
+       {NAME("lazy_lock"),     CTL(config_lazy_lock)},
+       {NAME("munmap"),        CTL(config_munmap)},
+       {NAME("prof"),          CTL(config_prof)},
+       {NAME("prof_libgcc"),   CTL(config_prof_libgcc)},
+       {NAME("prof_libunwind"), CTL(config_prof_libunwind)},
+       {NAME("stats"),         CTL(config_stats)},
+       {NAME("tcache"),        CTL(config_tcache)},
+       {NAME("tls"),           CTL(config_tls)},
+       {NAME("utrace"),        CTL(config_utrace)},
+       {NAME("valgrind"),      CTL(config_valgrind)},
+       {NAME("xmalloc"),       CTL(config_xmalloc)}
 };
 
 static const ctl_named_node_t opt_node[] = {
-       {NAME("abort"),                 CTL(opt_abort)},
-       {NAME("dss"),                   CTL(opt_dss)},
-       {NAME("lg_chunk"),              CTL(opt_lg_chunk)},
-       {NAME("narenas"),               CTL(opt_narenas)},
-       {NAME("lg_dirty_mult"),         CTL(opt_lg_dirty_mult)},
-       {NAME("stats_print"),           CTL(opt_stats_print)},
-       {NAME("junk"),                  CTL(opt_junk)},
-       {NAME("zero"),                  CTL(opt_zero)},
-       {NAME("quarantine"),            CTL(opt_quarantine)},
-       {NAME("redzone"),               CTL(opt_redzone)},
-       {NAME("utrace"),                CTL(opt_utrace)},
-       {NAME("xmalloc"),               CTL(opt_xmalloc)},
-       {NAME("tcache"),                CTL(opt_tcache)},
-       {NAME("lg_tcache_max"),         CTL(opt_lg_tcache_max)},
-       {NAME("prof"),                  CTL(opt_prof)},
-       {NAME("prof_prefix"),           CTL(opt_prof_prefix)},
-       {NAME("prof_active"),           CTL(opt_prof_active)},
+       {NAME("abort"),         CTL(opt_abort)},
+       {NAME("dss"),           CTL(opt_dss)},
+       {NAME("lg_chunk"),      CTL(opt_lg_chunk)},
+       {NAME("narenas"),       CTL(opt_narenas)},
+       {NAME("lg_dirty_mult"), CTL(opt_lg_dirty_mult)},
+       {NAME("stats_print"),   CTL(opt_stats_print)},
+       {NAME("junk"),          CTL(opt_junk)},
+       {NAME("zero"),          CTL(opt_zero)},
+       {NAME("quarantine"),    CTL(opt_quarantine)},
+       {NAME("redzone"),       CTL(opt_redzone)},
+       {NAME("utrace"),        CTL(opt_utrace)},
+       {NAME("xmalloc"),       CTL(opt_xmalloc)},
+       {NAME("tcache"),        CTL(opt_tcache)},
+       {NAME("lg_tcache_max"), CTL(opt_lg_tcache_max)},
+       {NAME("prof"),          CTL(opt_prof)},
+       {NAME("prof_prefix"),   CTL(opt_prof_prefix)},
+       {NAME("prof_active"),   CTL(opt_prof_active)},
        {NAME("prof_thread_active_init"), CTL(opt_prof_thread_active_init)},
-       {NAME("lg_prof_sample"),        CTL(opt_lg_prof_sample)},
-       {NAME("lg_prof_interval"),      CTL(opt_lg_prof_interval)},
-       {NAME("prof_gdump"),            CTL(opt_prof_gdump)},
-       {NAME("prof_final"),            CTL(opt_prof_final)},
-       {NAME("prof_leak"),             CTL(opt_prof_leak)},
-       {NAME("prof_accum"),            CTL(opt_prof_accum)}
+       {NAME("lg_prof_sample"), CTL(opt_lg_prof_sample)},
+       {NAME("lg_prof_interval"), CTL(opt_lg_prof_interval)},
+       {NAME("prof_gdump"),    CTL(opt_prof_gdump)},
+       {NAME("prof_final"),    CTL(opt_prof_final)},
+       {NAME("prof_leak"),     CTL(opt_prof_leak)},
+       {NAME("prof_accum"),    CTL(opt_prof_accum)}
 };
 
-static const ctl_named_node_t chunk_node[] = {
-       {NAME("alloc"),                 CTL(arena_i_chunk_alloc)},
-       {NAME("dalloc"),                CTL(arena_i_chunk_dalloc)}
+static const ctl_named_node_t  tcache_node[] = {
+       {NAME("create"),        CTL(tcache_create)},
+       {NAME("flush"),         CTL(tcache_flush)},
+       {NAME("destroy"),       CTL(tcache_destroy)}
 };
 
 static const ctl_named_node_t arena_i_node[] = {
-       {NAME("purge"),                 CTL(arena_i_purge)},
-       {NAME("dss"),                   CTL(arena_i_dss)},
-       {NAME("chunk"),                 CHILD(named, chunk)},
+       {NAME("purge"),         CTL(arena_i_purge)},
+       {NAME("dss"),           CTL(arena_i_dss)},
+       {NAME("lg_dirty_mult"), CTL(arena_i_lg_dirty_mult)},
+       {NAME("chunk_hooks"),   CTL(arena_i_chunk_hooks)}
 };
 static const ctl_named_node_t super_arena_i_node[] = {
-       {NAME(""),                      CHILD(named, arena_i)}
+       {NAME(""),              CHILD(named, arena_i)}
 };
 
 static const ctl_indexed_node_t arena_node[] = {
@@ -282,12 +301,12 @@ static const ctl_indexed_node_t arena_node[] = {
 };
 
 static const ctl_named_node_t arenas_bin_i_node[] = {
-       {NAME("size"),                  CTL(arenas_bin_i_size)},
-       {NAME("nregs"),                 CTL(arenas_bin_i_nregs)},
-       {NAME("run_size"),              CTL(arenas_bin_i_run_size)}
+       {NAME("size"),          CTL(arenas_bin_i_size)},
+       {NAME("nregs"),         CTL(arenas_bin_i_nregs)},
+       {NAME("run_size"),      CTL(arenas_bin_i_run_size)}
 };
 static const ctl_named_node_t super_arenas_bin_i_node[] = {
-       {NAME(""),                      CHILD(named, arenas_bin_i)}
+       {NAME(""),              CHILD(named, arenas_bin_i)}
 };
 
 static const ctl_indexed_node_t arenas_bin_node[] = {
@@ -295,79 +314,93 @@ static const ctl_indexed_node_t arenas_bin_node[] = {
 };
 
 static const ctl_named_node_t arenas_lrun_i_node[] = {
-       {NAME("size"),                  CTL(arenas_lrun_i_size)}
+       {NAME("size"),          CTL(arenas_lrun_i_size)}
 };
 static const ctl_named_node_t super_arenas_lrun_i_node[] = {
-       {NAME(""),                      CHILD(named, arenas_lrun_i)}
+       {NAME(""),              CHILD(named, arenas_lrun_i)}
 };
 
 static const ctl_indexed_node_t arenas_lrun_node[] = {
        {INDEX(arenas_lrun_i)}
 };
 
+static const ctl_named_node_t arenas_hchunk_i_node[] = {
+       {NAME("size"),          CTL(arenas_hchunk_i_size)}
+};
+static const ctl_named_node_t super_arenas_hchunk_i_node[] = {
+       {NAME(""),              CHILD(named, arenas_hchunk_i)}
+};
+
+static const ctl_indexed_node_t arenas_hchunk_node[] = {
+       {INDEX(arenas_hchunk_i)}
+};
+
 static const ctl_named_node_t arenas_node[] = {
-       {NAME("narenas"),               CTL(arenas_narenas)},
-       {NAME("initialized"),           CTL(arenas_initialized)},
-       {NAME("quantum"),               CTL(arenas_quantum)},
-       {NAME("page"),                  CTL(arenas_page)},
-       {NAME("tcache_max"),            CTL(arenas_tcache_max)},
-       {NAME("nbins"),                 CTL(arenas_nbins)},
-       {NAME("nhbins"),                CTL(arenas_nhbins)},
-       {NAME("bin"),                   CHILD(indexed, arenas_bin)},
-       {NAME("nlruns"),                CTL(arenas_nlruns)},
-       {NAME("lrun"),                  CHILD(indexed, arenas_lrun)},
-       {NAME("extend"),                CTL(arenas_extend)}
+       {NAME("narenas"),       CTL(arenas_narenas)},
+       {NAME("initialized"),   CTL(arenas_initialized)},
+       {NAME("lg_dirty_mult"), CTL(arenas_lg_dirty_mult)},
+       {NAME("quantum"),       CTL(arenas_quantum)},
+       {NAME("page"),          CTL(arenas_page)},
+       {NAME("tcache_max"),    CTL(arenas_tcache_max)},
+       {NAME("nbins"),         CTL(arenas_nbins)},
+       {NAME("nhbins"),        CTL(arenas_nhbins)},
+       {NAME("bin"),           CHILD(indexed, arenas_bin)},
+       {NAME("nlruns"),        CTL(arenas_nlruns)},
+       {NAME("lrun"),          CHILD(indexed, arenas_lrun)},
+       {NAME("nhchunks"),      CTL(arenas_nhchunks)},
+       {NAME("hchunk"),        CHILD(indexed, arenas_hchunk)},
+       {NAME("extend"),        CTL(arenas_extend)}
 };
 
 static const ctl_named_node_t  prof_node[] = {
        {NAME("thread_active_init"), CTL(prof_thread_active_init)},
        {NAME("active"),        CTL(prof_active)},
        {NAME("dump"),          CTL(prof_dump)},
+       {NAME("gdump"),         CTL(prof_gdump)},
        {NAME("reset"),         CTL(prof_reset)},
        {NAME("interval"),      CTL(prof_interval)},
        {NAME("lg_sample"),     CTL(lg_prof_sample)}
 };
 
-static const ctl_named_node_t stats_chunks_node[] = {
-       {NAME("current"),               CTL(stats_chunks_current)},
-       {NAME("total"),                 CTL(stats_chunks_total)},
-       {NAME("high"),                  CTL(stats_chunks_high)}
+static const ctl_named_node_t stats_arenas_i_metadata_node[] = {
+       {NAME("mapped"),        CTL(stats_arenas_i_metadata_mapped)},
+       {NAME("allocated"),     CTL(stats_arenas_i_metadata_allocated)}
 };
 
 static const ctl_named_node_t stats_arenas_i_small_node[] = {
-       {NAME("allocated"),             CTL(stats_arenas_i_small_allocated)},
-       {NAME("nmalloc"),               CTL(stats_arenas_i_small_nmalloc)},
-       {NAME("ndalloc"),               CTL(stats_arenas_i_small_ndalloc)},
-       {NAME("nrequests"),             CTL(stats_arenas_i_small_nrequests)}
+       {NAME("allocated"),     CTL(stats_arenas_i_small_allocated)},
+       {NAME("nmalloc"),       CTL(stats_arenas_i_small_nmalloc)},
+       {NAME("ndalloc"),       CTL(stats_arenas_i_small_ndalloc)},
+       {NAME("nrequests"),     CTL(stats_arenas_i_small_nrequests)}
 };
 
 static const ctl_named_node_t stats_arenas_i_large_node[] = {
-       {NAME("allocated"),             CTL(stats_arenas_i_large_allocated)},
-       {NAME("nmalloc"),               CTL(stats_arenas_i_large_nmalloc)},
-       {NAME("ndalloc"),               CTL(stats_arenas_i_large_ndalloc)},
-       {NAME("nrequests"),             CTL(stats_arenas_i_large_nrequests)}
+       {NAME("allocated"),     CTL(stats_arenas_i_large_allocated)},
+       {NAME("nmalloc"),       CTL(stats_arenas_i_large_nmalloc)},
+       {NAME("ndalloc"),       CTL(stats_arenas_i_large_ndalloc)},
+       {NAME("nrequests"),     CTL(stats_arenas_i_large_nrequests)}
 };
 
 static const ctl_named_node_t stats_arenas_i_huge_node[] = {
-       {NAME("allocated"),             CTL(stats_arenas_i_huge_allocated)},
-       {NAME("nmalloc"),               CTL(stats_arenas_i_huge_nmalloc)},
-       {NAME("ndalloc"),               CTL(stats_arenas_i_huge_ndalloc)},
-       {NAME("nrequests"),             CTL(stats_arenas_i_huge_nrequests)},
+       {NAME("allocated"),     CTL(stats_arenas_i_huge_allocated)},
+       {NAME("nmalloc"),       CTL(stats_arenas_i_huge_nmalloc)},
+       {NAME("ndalloc"),       CTL(stats_arenas_i_huge_ndalloc)},
+       {NAME("nrequests"),     CTL(stats_arenas_i_huge_nrequests)}
 };
 
 static const ctl_named_node_t stats_arenas_i_bins_j_node[] = {
-       {NAME("allocated"),             CTL(stats_arenas_i_bins_j_allocated)},
-       {NAME("nmalloc"),               CTL(stats_arenas_i_bins_j_nmalloc)},
-       {NAME("ndalloc"),               CTL(stats_arenas_i_bins_j_ndalloc)},
-       {NAME("nrequests"),             CTL(stats_arenas_i_bins_j_nrequests)},
-       {NAME("nfills"),                CTL(stats_arenas_i_bins_j_nfills)},
-       {NAME("nflushes"),              CTL(stats_arenas_i_bins_j_nflushes)},
-       {NAME("nruns"),                 CTL(stats_arenas_i_bins_j_nruns)},
-       {NAME("nreruns"),               CTL(stats_arenas_i_bins_j_nreruns)},
-       {NAME("curruns"),               CTL(stats_arenas_i_bins_j_curruns)}
+       {NAME("nmalloc"),       CTL(stats_arenas_i_bins_j_nmalloc)},
+       {NAME("ndalloc"),       CTL(stats_arenas_i_bins_j_ndalloc)},
+       {NAME("nrequests"),     CTL(stats_arenas_i_bins_j_nrequests)},
+       {NAME("curregs"),       CTL(stats_arenas_i_bins_j_curregs)},
+       {NAME("nfills"),        CTL(stats_arenas_i_bins_j_nfills)},
+       {NAME("nflushes"),      CTL(stats_arenas_i_bins_j_nflushes)},
+       {NAME("nruns"),         CTL(stats_arenas_i_bins_j_nruns)},
+       {NAME("nreruns"),       CTL(stats_arenas_i_bins_j_nreruns)},
+       {NAME("curruns"),       CTL(stats_arenas_i_bins_j_curruns)}
 };
 static const ctl_named_node_t super_stats_arenas_i_bins_j_node[] = {
-       {NAME(""),                      CHILD(named, stats_arenas_i_bins_j)}
+       {NAME(""),              CHILD(named, stats_arenas_i_bins_j)}
 };
 
 static const ctl_indexed_node_t stats_arenas_i_bins_node[] = {
@@ -375,36 +408,53 @@ static const ctl_indexed_node_t stats_arenas_i_bins_node[] = {
 };
 
 static const ctl_named_node_t stats_arenas_i_lruns_j_node[] = {
-       {NAME("nmalloc"),               CTL(stats_arenas_i_lruns_j_nmalloc)},
-       {NAME("ndalloc"),               CTL(stats_arenas_i_lruns_j_ndalloc)},
-       {NAME("nrequests"),             CTL(stats_arenas_i_lruns_j_nrequests)},
-       {NAME("curruns"),               CTL(stats_arenas_i_lruns_j_curruns)}
+       {NAME("nmalloc"),       CTL(stats_arenas_i_lruns_j_nmalloc)},
+       {NAME("ndalloc"),       CTL(stats_arenas_i_lruns_j_ndalloc)},
+       {NAME("nrequests"),     CTL(stats_arenas_i_lruns_j_nrequests)},
+       {NAME("curruns"),       CTL(stats_arenas_i_lruns_j_curruns)}
 };
 static const ctl_named_node_t super_stats_arenas_i_lruns_j_node[] = {
-       {NAME(""),                      CHILD(named, stats_arenas_i_lruns_j)}
+       {NAME(""),              CHILD(named, stats_arenas_i_lruns_j)}
 };
 
 static const ctl_indexed_node_t stats_arenas_i_lruns_node[] = {
        {INDEX(stats_arenas_i_lruns_j)}
 };
 
+static const ctl_named_node_t stats_arenas_i_hchunks_j_node[] = {
+       {NAME("nmalloc"),       CTL(stats_arenas_i_hchunks_j_nmalloc)},
+       {NAME("ndalloc"),       CTL(stats_arenas_i_hchunks_j_ndalloc)},
+       {NAME("nrequests"),     CTL(stats_arenas_i_hchunks_j_nrequests)},
+       {NAME("curhchunks"),    CTL(stats_arenas_i_hchunks_j_curhchunks)}
+};
+static const ctl_named_node_t super_stats_arenas_i_hchunks_j_node[] = {
+       {NAME(""),              CHILD(named, stats_arenas_i_hchunks_j)}
+};
+
+static const ctl_indexed_node_t stats_arenas_i_hchunks_node[] = {
+       {INDEX(stats_arenas_i_hchunks_j)}
+};
+
 static const ctl_named_node_t stats_arenas_i_node[] = {
-       {NAME("nthreads"),              CTL(stats_arenas_i_nthreads)},
-       {NAME("dss"),                   CTL(stats_arenas_i_dss)},
-       {NAME("pactive"),               CTL(stats_arenas_i_pactive)},
-       {NAME("pdirty"),                CTL(stats_arenas_i_pdirty)},
-       {NAME("mapped"),                CTL(stats_arenas_i_mapped)},
-       {NAME("npurge"),                CTL(stats_arenas_i_npurge)},
-       {NAME("nmadvise"),              CTL(stats_arenas_i_nmadvise)},
-       {NAME("purged"),                CTL(stats_arenas_i_purged)},
-       {NAME("small"),                 CHILD(named, stats_arenas_i_small)},
-       {NAME("large"),                 CHILD(named, stats_arenas_i_large)},
-       {NAME("huge"),                  CHILD(named, stats_arenas_i_huge)},
-       {NAME("bins"),                  CHILD(indexed, stats_arenas_i_bins)},
-       {NAME("lruns"),                 CHILD(indexed, stats_arenas_i_lruns)}
+       {NAME("nthreads"),      CTL(stats_arenas_i_nthreads)},
+       {NAME("dss"),           CTL(stats_arenas_i_dss)},
+       {NAME("lg_dirty_mult"), CTL(stats_arenas_i_lg_dirty_mult)},
+       {NAME("pactive"),       CTL(stats_arenas_i_pactive)},
+       {NAME("pdirty"),        CTL(stats_arenas_i_pdirty)},
+       {NAME("mapped"),        CTL(stats_arenas_i_mapped)},
+       {NAME("npurge"),        CTL(stats_arenas_i_npurge)},
+       {NAME("nmadvise"),      CTL(stats_arenas_i_nmadvise)},
+       {NAME("purged"),        CTL(stats_arenas_i_purged)},
+       {NAME("metadata"),      CHILD(named, stats_arenas_i_metadata)},
+       {NAME("small"),         CHILD(named, stats_arenas_i_small)},
+       {NAME("large"),         CHILD(named, stats_arenas_i_large)},
+       {NAME("huge"),          CHILD(named, stats_arenas_i_huge)},
+       {NAME("bins"),          CHILD(indexed, stats_arenas_i_bins)},
+       {NAME("lruns"),         CHILD(indexed, stats_arenas_i_lruns)},
+       {NAME("hchunks"),       CHILD(indexed, stats_arenas_i_hchunks)}
 };
 static const ctl_named_node_t super_stats_arenas_i_node[] = {
-       {NAME(""),                      CHILD(named, stats_arenas_i)}
+       {NAME(""),              CHILD(named, stats_arenas_i)}
 };
 
 static const ctl_indexed_node_t stats_arenas_node[] = {
@@ -412,12 +462,13 @@ static const ctl_indexed_node_t stats_arenas_node[] = {
 };
 
 static const ctl_named_node_t stats_node[] = {
-       {NAME("cactive"),               CTL(stats_cactive)},
-       {NAME("allocated"),             CTL(stats_allocated)},
-       {NAME("active"),                CTL(stats_active)},
-       {NAME("mapped"),                CTL(stats_mapped)},
-       {NAME("chunks"),                CHILD(named, stats_chunks)},
-       {NAME("arenas"),                CHILD(indexed, stats_arenas)}
+       {NAME("cactive"),       CTL(stats_cactive)},
+       {NAME("allocated"),     CTL(stats_allocated)},
+       {NAME("active"),        CTL(stats_active)},
+       {NAME("metadata"),      CTL(stats_metadata)},
+       {NAME("resident"),      CTL(stats_resident)},
+       {NAME("mapped"),        CTL(stats_mapped)},
+       {NAME("arenas"),        CHILD(indexed, stats_arenas)}
 };
 
 static const ctl_named_node_t  root_node[] = {
@@ -426,6 +477,7 @@ static const ctl_named_node_t       root_node[] = {
        {NAME("thread"),        CHILD(named, thread)},
        {NAME("config"),        CHILD(named, config)},
        {NAME("opt"),           CHILD(named, opt)},
+       {NAME("tcache"),        CHILD(named, tcache)},
        {NAME("arena"),         CHILD(indexed, arena)},
        {NAME("arenas"),        CHILD(named, arenas)},
        {NAME("prof"),          CHILD(named, prof)},
@@ -447,12 +499,19 @@ ctl_arena_init(ctl_arena_stats_t *astats)
 {
 
        if (astats->lstats == NULL) {
-               astats->lstats = (malloc_large_stats_t *)base_alloc(nlclasses *
+               astats->lstats = (malloc_large_stats_t *)a0malloc(nlclasses *
                    sizeof(malloc_large_stats_t));
                if (astats->lstats == NULL)
                        return (true);
        }
 
+       if (astats->hstats == NULL) {
+               astats->hstats = (malloc_huge_stats_t *)a0malloc(nhclasses *
+                   sizeof(malloc_huge_stats_t));
+               if (astats->hstats == NULL)
+                       return (true);
+       }
+
        return (false);
 }
 
@@ -461,6 +520,7 @@ ctl_arena_clear(ctl_arena_stats_t *astats)
 {
 
        astats->dss = dss_prec_names[dss_prec_limit];
+       astats->lg_dirty_mult = -1;
        astats->pactive = 0;
        astats->pdirty = 0;
        if (config_stats) {
@@ -472,6 +532,8 @@ ctl_arena_clear(ctl_arena_stats_t *astats)
                memset(astats->bstats, 0, NBINS * sizeof(malloc_bin_stats_t));
                memset(astats->lstats, 0, nlclasses *
                    sizeof(malloc_large_stats_t));
+               memset(astats->hstats, 0, nhclasses *
+                   sizeof(malloc_huge_stats_t));
        }
 }
 
@@ -480,11 +542,13 @@ ctl_arena_stats_amerge(ctl_arena_stats_t *cstats, arena_t *arena)
 {
        unsigned i;
 
-       arena_stats_merge(arena, &cstats->dss, &cstats->pactive,
-           &cstats->pdirty, &cstats->astats, cstats->bstats, cstats->lstats);
+       arena_stats_merge(arena, &cstats->dss, &cstats->lg_dirty_mult,
+           &cstats->pactive, &cstats->pdirty, &cstats->astats, cstats->bstats,
+           cstats->lstats, cstats->hstats);
 
        for (i = 0; i < NBINS; i++) {
-               cstats->allocated_small += cstats->bstats[i].allocated;
+               cstats->allocated_small += cstats->bstats[i].curregs *
+                   index2size(i);
                cstats->nmalloc_small += cstats->bstats[i].nmalloc;
                cstats->ndalloc_small += cstats->bstats[i].ndalloc;
                cstats->nrequests_small += cstats->bstats[i].nrequests;
@@ -504,6 +568,9 @@ ctl_arena_stats_smerge(ctl_arena_stats_t *sstats, ctl_arena_stats_t *astats)
        sstats->astats.nmadvise += astats->astats.nmadvise;
        sstats->astats.purged += astats->astats.purged;
 
+       sstats->astats.metadata_mapped += astats->astats.metadata_mapped;
+       sstats->astats.metadata_allocated += astats->astats.metadata_allocated;
+
        sstats->allocated_small += astats->allocated_small;
        sstats->nmalloc_small += astats->nmalloc_small;
        sstats->ndalloc_small += astats->ndalloc_small;
@@ -517,20 +584,12 @@ ctl_arena_stats_smerge(ctl_arena_stats_t *sstats, ctl_arena_stats_t *astats)
        sstats->astats.allocated_huge += astats->astats.allocated_huge;
        sstats->astats.nmalloc_huge += astats->astats.nmalloc_huge;
        sstats->astats.ndalloc_huge += astats->astats.ndalloc_huge;
-       sstats->astats.nrequests_huge += astats->astats.nrequests_huge;
-
-       for (i = 0; i < nlclasses; i++) {
-               sstats->lstats[i].nmalloc += astats->lstats[i].nmalloc;
-               sstats->lstats[i].ndalloc += astats->lstats[i].ndalloc;
-               sstats->lstats[i].nrequests += astats->lstats[i].nrequests;
-               sstats->lstats[i].curruns += astats->lstats[i].curruns;
-       }
 
        for (i = 0; i < NBINS; i++) {
-               sstats->bstats[i].allocated += astats->bstats[i].allocated;
                sstats->bstats[i].nmalloc += astats->bstats[i].nmalloc;
                sstats->bstats[i].ndalloc += astats->bstats[i].ndalloc;
                sstats->bstats[i].nrequests += astats->bstats[i].nrequests;
+               sstats->bstats[i].curregs += astats->bstats[i].curregs;
                if (config_tcache) {
                        sstats->bstats[i].nfills += astats->bstats[i].nfills;
                        sstats->bstats[i].nflushes +=
@@ -540,6 +599,19 @@ ctl_arena_stats_smerge(ctl_arena_stats_t *sstats, ctl_arena_stats_t *astats)
                sstats->bstats[i].reruns += astats->bstats[i].reruns;
                sstats->bstats[i].curruns += astats->bstats[i].curruns;
        }
+
+       for (i = 0; i < nlclasses; i++) {
+               sstats->lstats[i].nmalloc += astats->lstats[i].nmalloc;
+               sstats->lstats[i].ndalloc += astats->lstats[i].ndalloc;
+               sstats->lstats[i].nrequests += astats->lstats[i].nrequests;
+               sstats->lstats[i].curruns += astats->lstats[i].curruns;
+       }
+
+       for (i = 0; i < nhclasses; i++) {
+               sstats->hstats[i].nmalloc += astats->hstats[i].nmalloc;
+               sstats->hstats[i].ndalloc += astats->hstats[i].ndalloc;
+               sstats->hstats[i].curhchunks += astats->hstats[i].curhchunks;
+       }
 }
 
 static void
@@ -567,31 +639,24 @@ ctl_arena_refresh(arena_t *arena, unsigned i)
 static bool
 ctl_grow(void)
 {
-       tsd_t *tsd;
        ctl_arena_stats_t *astats;
-       arena_t **tarenas;
 
-       tsd = tsd_fetch();
+       /* Initialize new arena. */
+       if (arena_init(ctl_stats.narenas) == NULL)
+               return (true);
 
-       /* Allocate extended arena stats and arenas arrays. */
-       astats = (ctl_arena_stats_t *)imalloc(tsd, (ctl_stats.narenas + 2) *
+       /* Allocate extended arena stats. */
+       astats = (ctl_arena_stats_t *)a0malloc((ctl_stats.narenas + 2) *
            sizeof(ctl_arena_stats_t));
        if (astats == NULL)
                return (true);
-       tarenas = (arena_t **)imalloc(tsd, (ctl_stats.narenas + 1) *
-           sizeof(arena_t *));
-       if (tarenas == NULL) {
-               idalloc(tsd, astats);
-               return (true);
-       }
 
        /* Initialize the new astats element. */
        memcpy(astats, ctl_stats.arenas, (ctl_stats.narenas + 1) *
            sizeof(ctl_arena_stats_t));
        memset(&astats[ctl_stats.narenas + 1], 0, sizeof(ctl_arena_stats_t));
        if (ctl_arena_init(&astats[ctl_stats.narenas + 1])) {
-               idalloc(tsd, tarenas);
-               idalloc(tsd, astats);
+               a0dalloc(astats);
                return (true);
        }
        /* Swap merged stats to their new location. */
@@ -604,32 +669,7 @@ ctl_grow(void)
                memcpy(&astats[ctl_stats.narenas + 1], &tstats,
                    sizeof(ctl_arena_stats_t));
        }
-       /* Initialize the new arenas element. */
-       tarenas[ctl_stats.narenas] = NULL;
-       {
-               arena_t **arenas_old = arenas;
-               /*
-                * Swap extended arenas array into place.  Although ctl_mtx
-                * protects this function from other threads extending the
-                * array, it does not protect from other threads mutating it
-                * (i.e. initializing arenas and setting array elements to
-                * point to them).  Therefore, array copying must happen under
-                * the protection of arenas_lock.
-                */
-               malloc_mutex_lock(&arenas_lock);
-               arenas = tarenas;
-               memcpy(arenas, arenas_old, ctl_stats.narenas *
-                   sizeof(arena_t *));
-               narenas_total++;
-               arenas_extend(narenas_total - 1);
-               malloc_mutex_unlock(&arenas_lock);
-               /*
-                * Deallocate arenas_old only if it came from imalloc() (not
-                * base_alloc()).
-                */
-               if (ctl_stats.narenas != narenas_auto)
-                       idalloc(tsd, arenas_old);
-       }
+       a0dalloc(ctl_stats.arenas);
        ctl_stats.arenas = astats;
        ctl_stats.narenas++;
 
@@ -639,17 +679,11 @@ ctl_grow(void)
 static void
 ctl_refresh(void)
 {
+       tsd_t *tsd;
        unsigned i;
+       bool refreshed;
        VARIABLE_ARRAY(arena_t *, tarenas, ctl_stats.narenas);
 
-       if (config_stats) {
-               malloc_mutex_lock(&chunks_mtx);
-               ctl_stats.chunks.current = stats_chunks.curchunks;
-               ctl_stats.chunks.total = stats_chunks.nchunks;
-               ctl_stats.chunks.high = stats_chunks.highchunks;
-               malloc_mutex_unlock(&chunks_mtx);
-       }
-
        /*
         * Clear sum stats, since they will be merged into by
         * ctl_arena_refresh().
@@ -657,15 +691,22 @@ ctl_refresh(void)
        ctl_stats.arenas[ctl_stats.narenas].nthreads = 0;
        ctl_arena_clear(&ctl_stats.arenas[ctl_stats.narenas]);
 
-       malloc_mutex_lock(&arenas_lock);
-       memcpy(tarenas, arenas, sizeof(arena_t *) * ctl_stats.narenas);
+       tsd = tsd_fetch();
+       for (i = 0, refreshed = false; i < ctl_stats.narenas; i++) {
+               tarenas[i] = arena_get(tsd, i, false, false);
+               if (tarenas[i] == NULL && !refreshed) {
+                       tarenas[i] = arena_get(tsd, i, false, true);
+                       refreshed = true;
+               }
+       }
+
        for (i = 0; i < ctl_stats.narenas; i++) {
-               if (arenas[i] != NULL)
-                       ctl_stats.arenas[i].nthreads = arenas[i]->nthreads;
+               if (tarenas[i] != NULL)
+                       ctl_stats.arenas[i].nthreads = arena_nbound(i);
                else
                        ctl_stats.arenas[i].nthreads = 0;
        }
-       malloc_mutex_unlock(&arenas_lock);
+
        for (i = 0; i < ctl_stats.narenas; i++) {
                bool initialized = (tarenas[i] != NULL);
 
@@ -675,13 +716,24 @@ ctl_refresh(void)
        }
 
        if (config_stats) {
+               size_t base_allocated, base_resident, base_mapped;
+               base_stats_get(&base_allocated, &base_resident, &base_mapped);
                ctl_stats.allocated =
-                   ctl_stats.arenas[ctl_stats.narenas].allocated_small
-                   + ctl_stats.arenas[ctl_stats.narenas].astats.allocated_large
-                   ctl_stats.arenas[ctl_stats.narenas].astats.allocated_huge;
+                   ctl_stats.arenas[ctl_stats.narenas].allocated_small +
+                   ctl_stats.arenas[ctl_stats.narenas].astats.allocated_large +
+                   ctl_stats.arenas[ctl_stats.narenas].astats.allocated_huge;
                ctl_stats.active =
                    (ctl_stats.arenas[ctl_stats.narenas].pactive << LG_PAGE);
-               ctl_stats.mapped = (ctl_stats.chunks.current << opt_lg_chunk);
+               ctl_stats.metadata = base_allocated +
+                   ctl_stats.arenas[ctl_stats.narenas].astats.metadata_mapped +
+                   ctl_stats.arenas[ctl_stats.narenas].astats
+                   .metadata_allocated;
+               ctl_stats.resident = base_resident +
+                   ctl_stats.arenas[ctl_stats.narenas].astats.metadata_mapped +
+                   ((ctl_stats.arenas[ctl_stats.narenas].pactive +
+                   ctl_stats.arenas[ctl_stats.narenas].pdirty) << LG_PAGE);
+               ctl_stats.mapped = base_mapped +
+                   ctl_stats.arenas[ctl_stats.narenas].astats.mapped;
        }
 
        ctl_epoch++;
@@ -698,9 +750,8 @@ ctl_init(void)
                 * Allocate space for one extra arena stats element, which
                 * contains summed stats across all arenas.
                 */
-               assert(narenas_auto == narenas_total_get());
-               ctl_stats.narenas = narenas_auto;
-               ctl_stats.arenas = (ctl_arena_stats_t *)base_alloc(
+               ctl_stats.narenas = narenas_total_get();
+               ctl_stats.arenas = (ctl_arena_stats_t *)a0malloc(
                    (ctl_stats.narenas + 1) * sizeof(ctl_arena_stats_t));
                if (ctl_stats.arenas == NULL) {
                        ret = true;
@@ -718,6 +769,15 @@ ctl_init(void)
                        unsigned i;
                        for (i = 0; i <= ctl_stats.narenas; i++) {
                                if (ctl_arena_init(&ctl_stats.arenas[i])) {
+                                       unsigned j;
+                                       for (j = 0; j < i; j++) {
+                                               a0dalloc(
+                                                   ctl_stats.arenas[j].lstats);
+                                               a0dalloc(
+                                                   ctl_stats.arenas[j].hstats);
+                                       }
+                                       a0dalloc(ctl_stats.arenas);
+                                       ctl_stats.arenas = NULL;
                                        ret = true;
                                        goto label_return;
                                }
@@ -996,8 +1056,8 @@ ctl_postfork_child(void)
                        memcpy(oldp, (void *)&(v), copylen);            \
                        ret = EINVAL;                                   \
                        goto label_return;                              \
-               } else                                                  \
-                       *(t *)oldp = (v);                               \
+               }                                                       \
+               *(t *)oldp = (v);                                       \
        }                                                               \
 } while (0)
 
@@ -1181,6 +1241,7 @@ label_return:
 
 /******************************************************************************/
 
+CTL_RO_BOOL_CONFIG_GEN(config_cache_oblivious)
 CTL_RO_BOOL_CONFIG_GEN(config_debug)
 CTL_RO_BOOL_CONFIG_GEN(config_fill)
 CTL_RO_BOOL_CONFIG_GEN(config_lazy_lock)
@@ -1203,7 +1264,7 @@ CTL_RO_NL_GEN(opt_lg_chunk, opt_lg_chunk, size_t)
 CTL_RO_NL_GEN(opt_narenas, opt_narenas, size_t)
 CTL_RO_NL_GEN(opt_lg_dirty_mult, opt_lg_dirty_mult, ssize_t)
 CTL_RO_NL_GEN(opt_stats_print, opt_stats_print, bool)
-CTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, bool)
+CTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, const char *)
 CTL_RO_NL_CGEN(config_fill, opt_quarantine, opt_quarantine, size_t)
 CTL_RO_NL_CGEN(config_fill, opt_redzone, opt_redzone, bool)
 CTL_RO_NL_CGEN(config_fill, opt_zero, opt_zero, bool)
@@ -1231,16 +1292,20 @@ thread_arena_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
 {
        int ret;
        tsd_t *tsd;
+       arena_t *oldarena;
        unsigned newind, oldind;
 
        tsd = tsd_fetch();
+       oldarena = arena_choose(tsd, NULL);
+       if (oldarena == NULL)
+               return (EAGAIN);
 
        malloc_mutex_lock(&ctl_mtx);
-       newind = oldind = choose_arena(tsd, NULL)->ind;
+       newind = oldind = oldarena->ind;
        WRITE(newind, unsigned);
        READ(oldind, unsigned);
        if (newind != oldind) {
-               arena_t *arena;
+               arena_t *newarena;
 
                if (newind >= ctl_stats.narenas) {
                        /* New arena index is out of range. */
@@ -1249,28 +1314,20 @@ thread_arena_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
                }
 
                /* Initialize arena if necessary. */
-               malloc_mutex_lock(&arenas_lock);
-               if ((arena = arenas[newind]) == NULL && (arena =
-                   arenas_extend(newind)) == NULL) {
-                       malloc_mutex_unlock(&arenas_lock);
+               newarena = arena_get(tsd, newind, true, true);
+               if (newarena == NULL) {
                        ret = EAGAIN;
                        goto label_return;
                }
-               assert(arena == arenas[newind]);
-               arenas[oldind]->nthreads--;
-               arenas[newind]->nthreads++;
-               malloc_mutex_unlock(&arenas_lock);
-
-               /* Set new arena association. */
+               /* Set new arena/tcache associations. */
+               arena_migrate(tsd, oldind, newind);
                if (config_tcache) {
                        tcache_t *tcache = tsd_tcache_get(tsd);
                        if (tcache != NULL) {
-                               tcache_arena_dissociate(tcache);
-                               tcache_arena_associate(tcache, arena);
+                               tcache_arena_reassociate(tcache, oldarena,
+                                   newarena);
                        }
                }
-
-               tsd_arena_set(tsd, arena);
        }
 
        ret = 0;
@@ -1396,15 +1453,106 @@ label_return:
 
 /******************************************************************************/
 
+static int
+tcache_create_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
+    void *newp, size_t newlen)
+{
+       int ret;
+       tsd_t *tsd;
+       unsigned tcache_ind;
+
+       if (!config_tcache)
+               return (ENOENT);
+
+       tsd = tsd_fetch();
+
+       malloc_mutex_lock(&ctl_mtx);
+       READONLY();
+       if (tcaches_create(tsd, &tcache_ind)) {
+               ret = EFAULT;
+               goto label_return;
+       }
+       READ(tcache_ind, unsigned);
+
+       ret = 0;
+label_return:
+       malloc_mutex_unlock(&ctl_mtx);
+       return (ret);
+}
+
+static int
+tcache_flush_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
+    void *newp, size_t newlen)
+{
+       int ret;
+       tsd_t *tsd;
+       unsigned tcache_ind;
+
+       if (!config_tcache)
+               return (ENOENT);
+
+       tsd = tsd_fetch();
+
+       WRITEONLY();
+       tcache_ind = UINT_MAX;
+       WRITE(tcache_ind, unsigned);
+       if (tcache_ind == UINT_MAX) {
+               ret = EFAULT;
+               goto label_return;
+       }
+       tcaches_flush(tsd, tcache_ind);
+
+       ret = 0;
+label_return:
+       return (ret);
+}
+
+static int
+tcache_destroy_ctl(const size_t *mib, size_t miblen, void *oldp,
+    size_t *oldlenp, void *newp, size_t newlen)
+{
+       int ret;
+       tsd_t *tsd;
+       unsigned tcache_ind;
+
+       if (!config_tcache)
+               return (ENOENT);
+
+       tsd = tsd_fetch();
+
+       WRITEONLY();
+       tcache_ind = UINT_MAX;
+       WRITE(tcache_ind, unsigned);
+       if (tcache_ind == UINT_MAX) {
+               ret = EFAULT;
+               goto label_return;
+       }
+       tcaches_destroy(tsd, tcache_ind);
+
+       ret = 0;
+label_return:
+       return (ret);
+}
+
+/******************************************************************************/
+
 /* ctl_mutex must be held during execution of this function. */
 static void
 arena_purge(unsigned arena_ind)
 {
+       tsd_t *tsd;
+       unsigned i;
+       bool refreshed;
        VARIABLE_ARRAY(arena_t *, tarenas, ctl_stats.narenas);
 
-       malloc_mutex_lock(&arenas_lock);
-       memcpy(tarenas, arenas, sizeof(arena_t *) * ctl_stats.narenas);
-       malloc_mutex_unlock(&arenas_lock);
+       tsd = tsd_fetch();
+       for (i = 0, refreshed = false; i < ctl_stats.narenas; i++) {
+               tarenas[i] = arena_get(tsd, i, false, false);
+               if (tarenas[i] == NULL && !refreshed) {
+                       tarenas[i] = arena_get(tsd, i, false, true);
+                       refreshed = true;
+               }
+       }
 
        if (arena_ind == ctl_stats.narenas) {
                unsigned i;
@@ -1467,7 +1615,7 @@ arena_i_dss_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
        }
 
        if (arena_ind < ctl_stats.narenas) {
-               arena_t *arena = arenas[arena_ind];
+               arena_t *arena = arena_get(tsd_fetch(), arena_ind, false, true);
                if (arena == NULL || (dss_prec != dss_prec_limit &&
                    arena_dss_prec_set(arena, dss_prec))) {
                        ret = EFAULT;
@@ -1493,52 +1641,66 @@ label_return:
 }
 
 static int
-arena_i_chunk_alloc_ctl(const size_t *mib, size_t miblen, void *oldp,
+arena_i_lg_dirty_mult_ctl(const size_t *mib, size_t miblen, void *oldp,
     size_t *oldlenp, void *newp, size_t newlen)
 {
        int ret;
        unsigned arena_ind = mib[1];
        arena_t *arena;
 
-       malloc_mutex_lock(&ctl_mtx);
-       if (arena_ind < narenas_total && (arena = arenas[arena_ind]) != NULL) {
-               malloc_mutex_lock(&arena->lock);
-               READ(arena->chunk_alloc, chunk_alloc_t *);
-               WRITE(arena->chunk_alloc, chunk_alloc_t *);
-       } else {
+       arena = arena_get(tsd_fetch(), arena_ind, false, true);
+       if (arena == NULL) {
                ret = EFAULT;
-               goto label_outer_return;
+               goto label_return;
+       }
+
+       if (oldp != NULL && oldlenp != NULL) {
+               size_t oldval = arena_lg_dirty_mult_get(arena);
+               READ(oldval, ssize_t);
        }
+       if (newp != NULL) {
+               if (newlen != sizeof(ssize_t)) {
+                       ret = EINVAL;
+                       goto label_return;
+               }
+               if (arena_lg_dirty_mult_set(arena, *(ssize_t *)newp)) {
+                       ret = EFAULT;
+                       goto label_return;
+               }
+       }
+
        ret = 0;
 label_return:
-       malloc_mutex_unlock(&arena->lock);
-label_outer_return:
-       malloc_mutex_unlock(&ctl_mtx);
        return (ret);
 }
 
 static int
-arena_i_chunk_dalloc_ctl(const size_t *mib, size_t miblen, void *oldp,
+arena_i_chunk_hooks_ctl(const size_t *mib, size_t miblen, void *oldp,
     size_t *oldlenp, void *newp, size_t newlen)
 {
-
        int ret;
        unsigned arena_ind = mib[1];
        arena_t *arena;
 
        malloc_mutex_lock(&ctl_mtx);
-       if (arena_ind < narenas_total && (arena = arenas[arena_ind]) != NULL) {
-               malloc_mutex_lock(&arena->lock);
-               READ(arena->chunk_dalloc, chunk_dalloc_t *);
-               WRITE(arena->chunk_dalloc, chunk_dalloc_t *);
+       if (arena_ind < narenas_total_get() && (arena =
+           arena_get(tsd_fetch(), arena_ind, false, true)) != NULL) {
+               if (newp != NULL) {
+                       chunk_hooks_t old_chunk_hooks, new_chunk_hooks;
+                       WRITE(new_chunk_hooks, chunk_hooks_t);
+                       old_chunk_hooks = chunk_hooks_set(arena,
+                           &new_chunk_hooks);
+                       READ(old_chunk_hooks, chunk_hooks_t);
+               } else {
+                       chunk_hooks_t old_chunk_hooks = chunk_hooks_get(arena);
+                       READ(old_chunk_hooks, chunk_hooks_t);
+               }
        } else {
                ret = EFAULT;
-               goto label_outer_return;
+               goto label_return;
        }
        ret = 0;
 label_return:
-       malloc_mutex_unlock(&arena->lock);
-label_outer_return:
        malloc_mutex_unlock(&ctl_mtx);
        return (ret);
 }
@@ -1610,6 +1772,32 @@ label_return:
        return (ret);
 }
 
+static int
+arenas_lg_dirty_mult_ctl(const size_t *mib, size_t miblen, void *oldp,
+    size_t *oldlenp, void *newp, size_t newlen)
+{
+       int ret;
+
+       if (oldp != NULL && oldlenp != NULL) {
+               size_t oldval = arena_lg_dirty_mult_default_get();
+               READ(oldval, ssize_t);
+       }
+       if (newp != NULL) {
+               if (newlen != sizeof(ssize_t)) {
+                       ret = EINVAL;
+                       goto label_return;
+               }
+               if (arena_lg_dirty_mult_default_set(*(ssize_t *)newp)) {
+                       ret = EFAULT;
+                       goto label_return;
+               }
+       }
+
+       ret = 0;
+label_return:
+       return (ret);
+}
+
 CTL_RO_NL_GEN(arenas_quantum, QUANTUM, size_t)
 CTL_RO_NL_GEN(arenas_page, PAGE, size_t)
 CTL_RO_NL_CGEN(config_tcache, arenas_tcache_max, tcache_maxclass, size_t)
@@ -1627,8 +1815,8 @@ arenas_bin_i_index(const size_t *mib, size_t miblen, size_t i)
        return (super_arenas_bin_i_node);
 }
 
-CTL_RO_NL_GEN(arenas_nlruns, nlclasses, size_t)
-CTL_RO_NL_GEN(arenas_lrun_i_size, ((mib[2]+1) << LG_PAGE), size_t)
+CTL_RO_NL_GEN(arenas_nlruns, nlclasses, unsigned)
+CTL_RO_NL_GEN(arenas_lrun_i_size, index2size(NBINS+mib[2]), size_t)
 static const ctl_named_node_t *
 arenas_lrun_i_index(const size_t *mib, size_t miblen, size_t i)
 {
@@ -1638,6 +1826,17 @@ arenas_lrun_i_index(const size_t *mib, size_t miblen, size_t i)
        return (super_arenas_lrun_i_node);
 }
 
+CTL_RO_NL_GEN(arenas_nhchunks, nhclasses, unsigned)
+CTL_RO_NL_GEN(arenas_hchunk_i_size, index2size(NBINS+nlclasses+mib[2]), size_t)
+static const ctl_named_node_t *
+arenas_hchunk_i_index(const size_t *mib, size_t miblen, size_t i)
+{
+
+       if (i > nhclasses)
+               return (NULL);
+       return (super_arenas_hchunk_i_node);
+}
+
 static int
 arenas_extend_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
     void *newp, size_t newlen)
@@ -1735,6 +1934,31 @@ label_return:
        return (ret);
 }
 
+static int
+prof_gdump_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
+    void *newp, size_t newlen)
+{
+       int ret;
+       bool oldval;
+
+       if (!config_prof)
+               return (ENOENT);
+
+       if (newp != NULL) {
+               if (newlen != sizeof(bool)) {
+                       ret = EINVAL;
+                       goto label_return;
+               }
+               oldval = prof_gdump_set(*(bool *)newp);
+       } else
+               oldval = prof_gdump_get();
+       READ(oldval, bool);
+
+       ret = 0;
+label_return:
+       return (ret);
+}
+
 static int
 prof_reset_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
     void *newp, size_t newlen)
@@ -1768,14 +1992,13 @@ CTL_RO_NL_CGEN(config_prof, lg_prof_sample, lg_prof_sample, size_t)
 CTL_RO_CGEN(config_stats, stats_cactive, &stats_cactive, size_t *)
 CTL_RO_CGEN(config_stats, stats_allocated, ctl_stats.allocated, size_t)
 CTL_RO_CGEN(config_stats, stats_active, ctl_stats.active, size_t)
+CTL_RO_CGEN(config_stats, stats_metadata, ctl_stats.metadata, size_t)
+CTL_RO_CGEN(config_stats, stats_resident, ctl_stats.resident, size_t)
 CTL_RO_CGEN(config_stats, stats_mapped, ctl_stats.mapped, size_t)
 
-CTL_RO_CGEN(config_stats, stats_chunks_current, ctl_stats.chunks.current,
-    size_t)
-CTL_RO_CGEN(config_stats, stats_chunks_total, ctl_stats.chunks.total, uint64_t)
-CTL_RO_CGEN(config_stats, stats_chunks_high, ctl_stats.chunks.high, size_t)
-
 CTL_RO_GEN(stats_arenas_i_dss, ctl_stats.arenas[mib[2]].dss, const char *)
+CTL_RO_GEN(stats_arenas_i_lg_dirty_mult, ctl_stats.arenas[mib[2]].lg_dirty_mult,
+    ssize_t)
 CTL_RO_GEN(stats_arenas_i_nthreads, ctl_stats.arenas[mib[2]].nthreads, unsigned)
 CTL_RO_GEN(stats_arenas_i_pactive, ctl_stats.arenas[mib[2]].pactive, size_t)
 CTL_RO_GEN(stats_arenas_i_pdirty, ctl_stats.arenas[mib[2]].pdirty, size_t)
@@ -1787,6 +2010,10 @@ CTL_RO_CGEN(config_stats, stats_arenas_i_nmadvise,
     ctl_stats.arenas[mib[2]].astats.nmadvise, uint64_t)
 CTL_RO_CGEN(config_stats, stats_arenas_i_purged,
     ctl_stats.arenas[mib[2]].astats.purged, uint64_t)
+CTL_RO_CGEN(config_stats, stats_arenas_i_metadata_mapped,
+    ctl_stats.arenas[mib[2]].astats.metadata_mapped, size_t)
+CTL_RO_CGEN(config_stats, stats_arenas_i_metadata_allocated,
+    ctl_stats.arenas[mib[2]].astats.metadata_allocated, size_t)
 
 CTL_RO_CGEN(config_stats, stats_arenas_i_small_allocated,
     ctl_stats.arenas[mib[2]].allocated_small, size_t)
@@ -1811,16 +2038,16 @@ CTL_RO_CGEN(config_stats, stats_arenas_i_huge_nmalloc,
 CTL_RO_CGEN(config_stats, stats_arenas_i_huge_ndalloc,
     ctl_stats.arenas[mib[2]].astats.ndalloc_huge, uint64_t)
 CTL_RO_CGEN(config_stats, stats_arenas_i_huge_nrequests,
-    ctl_stats.arenas[mib[2]].astats.nrequests_huge, uint64_t)
+    ctl_stats.arenas[mib[2]].astats.nmalloc_huge, uint64_t) /* Intentional. */
 
-CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_allocated,
-    ctl_stats.arenas[mib[2]].bstats[mib[4]].allocated, size_t)
 CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nmalloc,
     ctl_stats.arenas[mib[2]].bstats[mib[4]].nmalloc, uint64_t)
 CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_ndalloc,
     ctl_stats.arenas[mib[2]].bstats[mib[4]].ndalloc, uint64_t)
 CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nrequests,
     ctl_stats.arenas[mib[2]].bstats[mib[4]].nrequests, uint64_t)
+CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curregs,
+    ctl_stats.arenas[mib[2]].bstats[mib[4]].curregs, size_t)
 CTL_RO_CGEN(config_stats && config_tcache, stats_arenas_i_bins_j_nfills,
     ctl_stats.arenas[mib[2]].bstats[mib[4]].nfills, uint64_t)
 CTL_RO_CGEN(config_stats && config_tcache, stats_arenas_i_bins_j_nflushes,
@@ -1859,6 +2086,25 @@ stats_arenas_i_lruns_j_index(const size_t *mib, size_t miblen, size_t j)
        return (super_stats_arenas_i_lruns_j_node);
 }
 
+CTL_RO_CGEN(config_stats, stats_arenas_i_hchunks_j_nmalloc,
+    ctl_stats.arenas[mib[2]].hstats[mib[4]].nmalloc, uint64_t)
+CTL_RO_CGEN(config_stats, stats_arenas_i_hchunks_j_ndalloc,
+    ctl_stats.arenas[mib[2]].hstats[mib[4]].ndalloc, uint64_t)
+CTL_RO_CGEN(config_stats, stats_arenas_i_hchunks_j_nrequests,
+    ctl_stats.arenas[mib[2]].hstats[mib[4]].nmalloc, /* Intentional. */
+    uint64_t)
+CTL_RO_CGEN(config_stats, stats_arenas_i_hchunks_j_curhchunks,
+    ctl_stats.arenas[mib[2]].hstats[mib[4]].curhchunks, size_t)
+
+static const ctl_named_node_t *
+stats_arenas_i_hchunks_j_index(const size_t *mib, size_t miblen, size_t j)
+{
+
+       if (j > nhclasses)
+               return (NULL);
+       return (super_stats_arenas_i_hchunks_j_node);
+}
+
 static const ctl_named_node_t *
 stats_arenas_i_index(const size_t *mib, size_t miblen, size_t i)
 {
index 8c09b486ed819a4af685488031ed17e70498fcfd..13f94411c15a1cd812df5d5066bef87d97ecf018 100644 (file)
@@ -3,17 +3,32 @@
 
 /******************************************************************************/
 
-static inline int
+JEMALLOC_INLINE_C size_t
+extent_quantize(size_t size)
+{
+
+       /*
+        * Round down to the nearest chunk size that can actually be requested
+        * during normal huge allocation.
+        */
+       return (index2size(size2index(size + 1) - 1));
+}
+
+JEMALLOC_INLINE_C int
 extent_szad_comp(extent_node_t *a, extent_node_t *b)
 {
        int ret;
-       size_t a_size = a->size;
-       size_t b_size = b->size;
-
-       ret = (a_size > b_size) - (a_size < b_size);
+       size_t a_qsize = extent_quantize(extent_node_size_get(a));
+       size_t b_qsize = extent_quantize(extent_node_size_get(b));
+
+       /*
+        * Compare based on quantized size rather than size, in order to sort
+        * equally useful extents only by address.
+        */
+       ret = (a_qsize > b_qsize) - (a_qsize < b_qsize);
        if (ret == 0) {
-               uintptr_t a_addr = (uintptr_t)a->addr;
-               uintptr_t b_addr = (uintptr_t)b->addr;
+               uintptr_t a_addr = (uintptr_t)extent_node_addr_get(a);
+               uintptr_t b_addr = (uintptr_t)extent_node_addr_get(b);
 
                ret = (a_addr > b_addr) - (a_addr < b_addr);
        }
@@ -22,18 +37,17 @@ extent_szad_comp(extent_node_t *a, extent_node_t *b)
 }
 
 /* Generate red-black tree functions. */
-rb_gen(, extent_tree_szad_, extent_tree_t, extent_node_t, link_szad,
+rb_gen(, extent_tree_szad_, extent_tree_t, extent_node_t, szad_link,
     extent_szad_comp)
 
-static inline int
+JEMALLOC_INLINE_C int
 extent_ad_comp(extent_node_t *a, extent_node_t *b)
 {
-       uintptr_t a_addr = (uintptr_t)a->addr;
-       uintptr_t b_addr = (uintptr_t)b->addr;
+       uintptr_t a_addr = (uintptr_t)extent_node_addr_get(a);
+       uintptr_t b_addr = (uintptr_t)extent_node_addr_get(b);
 
        return ((a_addr > b_addr) - (a_addr < b_addr));
 }
 
 /* Generate red-black tree functions. */
-rb_gen(, extent_tree_ad_, extent_tree_t, extent_node_t, link_ad,
-    extent_ad_comp)
+rb_gen(, extent_tree_ad_, extent_tree_t, extent_node_t, ad_link, extent_ad_comp)
index 6bdc0767f177e58dbc9d401c66d0230562a00df4..1e9a66512f12fb61a541730df5b652d40270ccfa 100644 (file)
@@ -2,42 +2,68 @@
 #include "jemalloc/internal/jemalloc_internal.h"
 
 /******************************************************************************/
-/* Data. */
 
-/* Protects chunk-related data structures. */
-static malloc_mutex_t  huge_mtx;
+static extent_node_t *
+huge_node_get(const void *ptr)
+{
+       extent_node_t *node;
 
-/******************************************************************************/
+       node = chunk_lookup(ptr, true);
+       assert(!extent_node_achunk_get(node));
+
+       return (node);
+}
 
-/* Tree of chunks that are stand-alone huge allocations. */
-static extent_tree_t   huge;
+static bool
+huge_node_set(const void *ptr, extent_node_t *node)
+{
+
+       assert(extent_node_addr_get(node) == ptr);
+       assert(!extent_node_achunk_get(node));
+       return (chunk_register(ptr, node));
+}
+
+static void
+huge_node_unset(const void *ptr, const extent_node_t *node)
+{
+
+       chunk_deregister(ptr, node);
+}
 
 void *
-huge_malloc(tsd_t *tsd, arena_t *arena, size_t size, bool zero)
+huge_malloc(tsd_t *tsd, arena_t *arena, size_t size, bool zero,
+    tcache_t *tcache)
 {
+       size_t usize;
 
-       return (huge_palloc(tsd, arena, size, chunksize, zero));
+       usize = s2u(size);
+       if (usize == 0) {
+               /* size_t overflow. */
+               return (NULL);
+       }
+
+       return (huge_palloc(tsd, arena, usize, chunksize, zero, tcache));
 }
 
 void *
 huge_palloc(tsd_t *tsd, arena_t *arena, size_t size, size_t alignment,
-    bool zero)
+    bool zero, tcache_t *tcache)
 {
        void *ret;
-       size_t csize;
+       size_t usize;
        extent_node_t *node;
        bool is_zeroed;
 
        /* Allocate one or more contiguous chunks for this request. */
 
-       csize = CHUNK_CEILING(size);
-       if (csize == 0) {
-               /* size is large enough to cause size_t wrap-around. */
+       usize = sa2u(size, alignment);
+       if (unlikely(usize == 0))
                return (NULL);
-       }
+       assert(usize >= chunksize);
 
        /* Allocate an extent node with which to track the chunk. */
-       node = base_node_alloc();
+       node = ipallocztm(tsd, CACHELINE_CEILING(sizeof(extent_node_t)),
+           CACHELINE, false, tcache, true, arena);
        if (node == NULL)
                return (NULL);
 
@@ -46,29 +72,33 @@ huge_palloc(tsd_t *tsd, arena_t *arena, size_t size, size_t alignment,
         * it is possible to make correct junk/zero fill decisions below.
         */
        is_zeroed = zero;
-       arena = choose_arena(tsd, arena);
-       ret = arena_chunk_alloc_huge(arena, NULL, csize, alignment, &is_zeroed);
-       if (ret == NULL) {
-               base_node_dalloc(node);
+       arena = arena_choose(tsd, arena);
+       if (unlikely(arena == NULL) || (ret = arena_chunk_alloc_huge(arena,
+           size, alignment, &is_zeroed)) == NULL) {
+               idalloctm(tsd, node, tcache, true);
                return (NULL);
        }
 
-       /* Insert node into huge. */
-       node->addr = ret;
-       node->size = csize;
-       node->arena = arena;
-
-       malloc_mutex_lock(&huge_mtx);
-       extent_tree_ad_insert(&huge, node);
-       malloc_mutex_unlock(&huge_mtx);
-
-       if (config_fill && !zero) {
-               if (unlikely(opt_junk))
-                       memset(ret, 0xa5, csize);
-               else if (unlikely(opt_zero) && !is_zeroed)
-                       memset(ret, 0, csize);
+       extent_node_init(node, arena, ret, size, is_zeroed, true);
+
+       if (huge_node_set(ret, node)) {
+               arena_chunk_dalloc_huge(arena, ret, size);
+               idalloctm(tsd, node, tcache, true);
+               return (NULL);
        }
 
+       /* Insert node into huge. */
+       malloc_mutex_lock(&arena->huge_mtx);
+       ql_elm_new(node, ql_link);
+       ql_tail_insert(&arena->huge, node, ql_link);
+       malloc_mutex_unlock(&arena->huge_mtx);
+
+       if (zero || (config_fill && unlikely(opt_zero))) {
+               if (!is_zeroed)
+                       memset(ret, 0, size);
+       } else if (config_fill && unlikely(opt_junk_alloc))
+               memset(ret, 0xa5, size);
+
        return (ret);
 }
 
@@ -80,7 +110,7 @@ static void
 huge_dalloc_junk(void *ptr, size_t usize)
 {
 
-       if (config_fill && have_dss && unlikely(opt_junk)) {
+       if (config_fill && have_dss && unlikely(opt_junk_free)) {
                /*
                 * Only bother junk filling if the chunk isn't about to be
                 * unmapped.
@@ -95,276 +125,311 @@ huge_dalloc_junk(void *ptr, size_t usize)
 huge_dalloc_junk_t *huge_dalloc_junk = JEMALLOC_N(huge_dalloc_junk_impl);
 #endif
 
+static void
+huge_ralloc_no_move_similar(void *ptr, size_t oldsize, size_t usize_min,
+    size_t usize_max, bool zero)
+{
+       size_t usize, usize_next;
+       extent_node_t *node;
+       arena_t *arena;
+       chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER;
+       bool pre_zeroed, post_zeroed;
+
+       /* Increase usize to incorporate extra. */
+       for (usize = usize_min; usize < usize_max && (usize_next = s2u(usize+1))
+           <= oldsize; usize = usize_next)
+               ; /* Do nothing. */
+
+       if (oldsize == usize)
+               return;
+
+       node = huge_node_get(ptr);
+       arena = extent_node_arena_get(node);
+       pre_zeroed = extent_node_zeroed_get(node);
+
+       /* Fill if necessary (shrinking). */
+       if (oldsize > usize) {
+               size_t sdiff = oldsize - usize;
+               if (config_fill && unlikely(opt_junk_free)) {
+                       memset((void *)((uintptr_t)ptr + usize), 0x5a, sdiff);
+                       post_zeroed = false;
+               } else {
+                       post_zeroed = !chunk_purge_wrapper(arena, &chunk_hooks,
+                           ptr, CHUNK_CEILING(oldsize), usize, sdiff);
+               }
+       } else
+               post_zeroed = pre_zeroed;
+
+       malloc_mutex_lock(&arena->huge_mtx);
+       /* Update the size of the huge allocation. */
+       assert(extent_node_size_get(node) != usize);
+       extent_node_size_set(node, usize);
+       /* Update zeroed. */
+       extent_node_zeroed_set(node, post_zeroed);
+       malloc_mutex_unlock(&arena->huge_mtx);
+
+       arena_chunk_ralloc_huge_similar(arena, ptr, oldsize, usize);
+
+       /* Fill if necessary (growing). */
+       if (oldsize < usize) {
+               if (zero || (config_fill && unlikely(opt_zero))) {
+                       if (!pre_zeroed) {
+                               memset((void *)((uintptr_t)ptr + oldsize), 0,
+                                   usize - oldsize);
+                       }
+               } else if (config_fill && unlikely(opt_junk_alloc)) {
+                       memset((void *)((uintptr_t)ptr + oldsize), 0xa5, usize -
+                           oldsize);
+               }
+       }
+}
+
 static bool
-huge_ralloc_no_move_expand(void *ptr, size_t oldsize, size_t size, bool zero) {
-       size_t csize;
-       void *expand_addr;
-       size_t expand_size;
-       extent_node_t *node, key;
+huge_ralloc_no_move_shrink(void *ptr, size_t oldsize, size_t usize)
+{
+       extent_node_t *node;
        arena_t *arena;
-       bool is_zeroed;
-       void *ret;
+       chunk_hooks_t chunk_hooks;
+       size_t cdiff;
+       bool pre_zeroed, post_zeroed;
 
-       csize = CHUNK_CEILING(size);
-       if (csize == 0) {
-               /* size is large enough to cause size_t wrap-around. */
+       node = huge_node_get(ptr);
+       arena = extent_node_arena_get(node);
+       pre_zeroed = extent_node_zeroed_get(node);
+       chunk_hooks = chunk_hooks_get(arena);
+
+       assert(oldsize > usize);
+
+       /* Split excess chunks. */
+       cdiff = CHUNK_CEILING(oldsize) - CHUNK_CEILING(usize);
+       if (cdiff != 0 && chunk_hooks.split(ptr, CHUNK_CEILING(oldsize),
+           CHUNK_CEILING(usize), cdiff, true, arena->ind))
                return (true);
-       }
 
-       expand_addr = ptr + oldsize;
-       expand_size = csize - oldsize;
+       if (oldsize > usize) {
+               size_t sdiff = oldsize - usize;
+               if (config_fill && unlikely(opt_junk_free)) {
+                       huge_dalloc_junk((void *)((uintptr_t)ptr + usize),
+                           sdiff);
+                       post_zeroed = false;
+               } else {
+                       post_zeroed = !chunk_purge_wrapper(arena, &chunk_hooks,
+                           CHUNK_ADDR2BASE((uintptr_t)ptr + usize),
+                           CHUNK_CEILING(oldsize),
+                           CHUNK_ADDR2OFFSET((uintptr_t)ptr + usize), sdiff);
+               }
+       } else
+               post_zeroed = pre_zeroed;
+
+       malloc_mutex_lock(&arena->huge_mtx);
+       /* Update the size of the huge allocation. */
+       extent_node_size_set(node, usize);
+       /* Update zeroed. */
+       extent_node_zeroed_set(node, post_zeroed);
+       malloc_mutex_unlock(&arena->huge_mtx);
 
-       malloc_mutex_lock(&huge_mtx);
+       /* Zap the excess chunks. */
+       arena_chunk_ralloc_huge_shrink(arena, ptr, oldsize, usize);
 
-       key.addr = ptr;
-       node = extent_tree_ad_search(&huge, &key);
-       assert(node != NULL);
-       assert(node->addr == ptr);
+       return (false);
+}
 
-       /* Find the current arena. */
-       arena = node->arena;
+static bool
+huge_ralloc_no_move_expand(void *ptr, size_t oldsize, size_t usize, bool zero) {
+       extent_node_t *node;
+       arena_t *arena;
+       bool is_zeroed_subchunk, is_zeroed_chunk;
 
-       malloc_mutex_unlock(&huge_mtx);
+       node = huge_node_get(ptr);
+       arena = extent_node_arena_get(node);
+       malloc_mutex_lock(&arena->huge_mtx);
+       is_zeroed_subchunk = extent_node_zeroed_get(node);
+       malloc_mutex_unlock(&arena->huge_mtx);
 
        /*
-        * Copy zero into is_zeroed and pass the copy to chunk_alloc(), so that
-        * it is possible to make correct junk/zero fill decisions below.
+        * Copy zero into is_zeroed_chunk and pass the copy to chunk_alloc(), so
+        * that it is possible to make correct junk/zero fill decisions below.
         */
-       is_zeroed = zero;
-       ret = arena_chunk_alloc_huge(arena, expand_addr, expand_size, chunksize,
-                                    &is_zeroed);
-       if (ret == NULL)
-               return (true);
+       is_zeroed_chunk = zero;
 
-       assert(ret == expand_addr);
+       if (arena_chunk_ralloc_huge_expand(arena, ptr, oldsize, usize,
+            &is_zeroed_chunk))
+               return (true);
 
-       malloc_mutex_lock(&huge_mtx);
+       malloc_mutex_lock(&arena->huge_mtx);
        /* Update the size of the huge allocation. */
-       node->size = csize;
-       malloc_mutex_unlock(&huge_mtx);
-
-       if (config_fill && !zero) {
-               if (unlikely(opt_junk))
-                       memset(expand_addr, 0xa5, expand_size);
-               else if (unlikely(opt_zero) && !is_zeroed)
-                       memset(expand_addr, 0, expand_size);
+       extent_node_size_set(node, usize);
+       malloc_mutex_unlock(&arena->huge_mtx);
+
+       if (zero || (config_fill && unlikely(opt_zero))) {
+               if (!is_zeroed_subchunk) {
+                       memset((void *)((uintptr_t)ptr + oldsize), 0,
+                           CHUNK_CEILING(oldsize) - oldsize);
+               }
+               if (!is_zeroed_chunk) {
+                       memset((void *)((uintptr_t)ptr +
+                           CHUNK_CEILING(oldsize)), 0, usize -
+                           CHUNK_CEILING(oldsize));
+               }
+       } else if (config_fill && unlikely(opt_junk_alloc)) {
+               memset((void *)((uintptr_t)ptr + oldsize), 0xa5, usize -
+                   oldsize);
        }
+
        return (false);
 }
 
 bool
-huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra,
-    bool zero)
+huge_ralloc_no_move(void *ptr, size_t oldsize, size_t usize_min,
+    size_t usize_max, bool zero)
 {
 
+       assert(s2u(oldsize) == oldsize);
+
        /* Both allocations must be huge to avoid a move. */
-       if (oldsize <= arena_maxclass)
+       if (oldsize < chunksize || usize_max < chunksize)
                return (true);
 
-       assert(CHUNK_CEILING(oldsize) == oldsize);
+       if (CHUNK_CEILING(usize_max) > CHUNK_CEILING(oldsize)) {
+               /* Attempt to expand the allocation in-place. */
+               if (!huge_ralloc_no_move_expand(ptr, oldsize, usize_max, zero))
+                       return (false);
+               /* Try again, this time with usize_min. */
+               if (usize_min < usize_max && CHUNK_CEILING(usize_min) >
+                   CHUNK_CEILING(oldsize) && huge_ralloc_no_move_expand(ptr,
+                   oldsize, usize_min, zero))
+                       return (false);
+       }
 
        /*
-        * Avoid moving the allocation if the size class can be left the same.
+        * Avoid moving the allocation if the existing chunk size accommodates
+        * the new size.
         */
-       if (CHUNK_CEILING(oldsize) >= CHUNK_CEILING(size)
-           && CHUNK_CEILING(oldsize) <= CHUNK_CEILING(size+extra)) {
+       if (CHUNK_CEILING(oldsize) >= CHUNK_CEILING(usize_min)
+           && CHUNK_CEILING(oldsize) <= CHUNK_CEILING(usize_max)) {
+               huge_ralloc_no_move_similar(ptr, oldsize, usize_min, usize_max,
+                   zero);
                return (false);
        }
 
-       /* Overflow. */
-       if (CHUNK_CEILING(size) == 0)
-               return (true);
-
-       /* Shrink the allocation in-place. */
-       if (CHUNK_CEILING(oldsize) > CHUNK_CEILING(size)) {
-               extent_node_t *node, key;
-               void *excess_addr;
-               size_t excess_size;
-
-               malloc_mutex_lock(&huge_mtx);
-
-               key.addr = ptr;
-               node = extent_tree_ad_search(&huge, &key);
-               assert(node != NULL);
-               assert(node->addr == ptr);
-
-               /* Update the size of the huge allocation. */
-               node->size = CHUNK_CEILING(size);
-
-               malloc_mutex_unlock(&huge_mtx);
-
-               excess_addr = node->addr + CHUNK_CEILING(size);
-               excess_size = CHUNK_CEILING(oldsize) - CHUNK_CEILING(size);
-
-               /* Zap the excess chunks. */
-               huge_dalloc_junk(excess_addr, excess_size);
-               arena_chunk_dalloc_huge(node->arena, excess_addr, excess_size);
-
-               return (false);
-       }
+       /* Attempt to shrink the allocation in-place. */
+       if (CHUNK_CEILING(oldsize) > CHUNK_CEILING(usize_max))
+               return (huge_ralloc_no_move_shrink(ptr, oldsize, usize_max));
+       return (true);
+}
 
-       /* Attempt to expand the allocation in-place. */
-       if (huge_ralloc_no_move_expand(ptr, oldsize, size + extra, zero)) {
-               if (extra == 0)
-                       return (true);
+static void *
+huge_ralloc_move_helper(tsd_t *tsd, arena_t *arena, size_t usize,
+    size_t alignment, bool zero, tcache_t *tcache)
+{
 
-               /* Try again, this time without extra. */
-               return (huge_ralloc_no_move_expand(ptr, oldsize, size, zero));
-       }
-       return (false);
+       if (alignment <= chunksize)
+               return (huge_malloc(tsd, arena, usize, zero, tcache));
+       return (huge_palloc(tsd, arena, usize, alignment, zero, tcache));
 }
 
 void *
-huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, size_t size,
-    size_t extra, size_t alignment, bool zero, bool try_tcache_dalloc)
+huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, size_t usize,
+    size_t alignment, bool zero, tcache_t *tcache)
 {
        void *ret;
        size_t copysize;
 
        /* Try to avoid moving the allocation. */
-       if (!huge_ralloc_no_move(ptr, oldsize, size, extra, zero))
+       if (!huge_ralloc_no_move(ptr, oldsize, usize, usize, zero))
                return (ptr);
 
        /*
-        * size and oldsize are different enough that we need to use a
+        * usize and oldsize are different enough that we need to use a
         * different size class.  In that case, fall back to allocating new
         * space and copying.
         */
-       if (alignment > chunksize)
-               ret = huge_palloc(tsd, arena, size + extra, alignment, zero);
-       else
-               ret = huge_malloc(tsd, arena, size + extra, zero);
-
-       if (ret == NULL) {
-               if (extra == 0)
-                       return (NULL);
-               /* Try again, this time without extra. */
-               if (alignment > chunksize)
-                       ret = huge_palloc(tsd, arena, size, alignment, zero);
-               else
-                       ret = huge_malloc(tsd, arena, size, zero);
-
-               if (ret == NULL)
-                       return (NULL);
-       }
+       ret = huge_ralloc_move_helper(tsd, arena, usize, alignment, zero,
+           tcache);
+       if (ret == NULL)
+               return (NULL);
 
-       /*
-        * Copy at most size bytes (not size+extra), since the caller has no
-        * expectation that the extra bytes will be reliably preserved.
-        */
-       copysize = (size < oldsize) ? size : oldsize;
+       copysize = (usize < oldsize) ? usize : oldsize;
        memcpy(ret, ptr, copysize);
-       iqalloc(tsd, ptr, try_tcache_dalloc);
+       isqalloc(tsd, ptr, oldsize, tcache);
        return (ret);
 }
 
 void
-huge_dalloc(void *ptr)
+huge_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache)
 {
-       extent_node_t *node, key;
-
-       malloc_mutex_lock(&huge_mtx);
+       extent_node_t *node;
+       arena_t *arena;
 
-       /* Extract from tree of huge allocations. */
-       key.addr = ptr;
-       node = extent_tree_ad_search(&huge, &key);
-       assert(node != NULL);
-       assert(node->addr == ptr);
-       extent_tree_ad_remove(&huge, node);
+       node = huge_node_get(ptr);
+       arena = extent_node_arena_get(node);
+       huge_node_unset(ptr, node);
+       malloc_mutex_lock(&arena->huge_mtx);
+       ql_remove(&arena->huge, node, ql_link);
+       malloc_mutex_unlock(&arena->huge_mtx);
+
+       huge_dalloc_junk(extent_node_addr_get(node),
+           extent_node_size_get(node));
+       arena_chunk_dalloc_huge(extent_node_arena_get(node),
+           extent_node_addr_get(node), extent_node_size_get(node));
+       idalloctm(tsd, node, tcache, true);
+}
 
-       malloc_mutex_unlock(&huge_mtx);
+arena_t *
+huge_aalloc(const void *ptr)
+{
 
-       huge_dalloc_junk(node->addr, node->size);
-       arena_chunk_dalloc_huge(node->arena, node->addr, node->size);
-       base_node_dalloc(node);
+       return (extent_node_arena_get(huge_node_get(ptr)));
 }
 
 size_t
 huge_salloc(const void *ptr)
 {
-       size_t ret;
-       extent_node_t *node, key;
-
-       malloc_mutex_lock(&huge_mtx);
-
-       /* Extract from tree of huge allocations. */
-       key.addr = __DECONST(void *, ptr);
-       node = extent_tree_ad_search(&huge, &key);
-       assert(node != NULL);
-
-       ret = node->size;
+       size_t size;
+       extent_node_t *node;
+       arena_t *arena;
 
-       malloc_mutex_unlock(&huge_mtx);
+       node = huge_node_get(ptr);
+       arena = extent_node_arena_get(node);
+       malloc_mutex_lock(&arena->huge_mtx);
+       size = extent_node_size_get(node);
+       malloc_mutex_unlock(&arena->huge_mtx);
 
-       return (ret);
+       return (size);
 }
 
 prof_tctx_t *
 huge_prof_tctx_get(const void *ptr)
 {
-       prof_tctx_t *ret;
-       extent_node_t *node, key;
-
-       malloc_mutex_lock(&huge_mtx);
-
-       /* Extract from tree of huge allocations. */
-       key.addr = __DECONST(void *, ptr);
-       node = extent_tree_ad_search(&huge, &key);
-       assert(node != NULL);
-
-       ret = node->prof_tctx;
+       prof_tctx_t *tctx;
+       extent_node_t *node;
+       arena_t *arena;
 
-       malloc_mutex_unlock(&huge_mtx);
+       node = huge_node_get(ptr);
+       arena = extent_node_arena_get(node);
+       malloc_mutex_lock(&arena->huge_mtx);
+       tctx = extent_node_prof_tctx_get(node);
+       malloc_mutex_unlock(&arena->huge_mtx);
 
-       return (ret);
+       return (tctx);
 }
 
 void
 huge_prof_tctx_set(const void *ptr, prof_tctx_t *tctx)
 {
-       extent_node_t *node, key;
-
-       malloc_mutex_lock(&huge_mtx);
-
-       /* Extract from tree of huge allocations. */
-       key.addr = __DECONST(void *, ptr);
-       node = extent_tree_ad_search(&huge, &key);
-       assert(node != NULL);
-
-       node->prof_tctx = tctx;
-
-       malloc_mutex_unlock(&huge_mtx);
-}
-
-bool
-huge_boot(void)
-{
-
-       /* Initialize chunks data. */
-       if (malloc_mutex_init(&huge_mtx))
-               return (true);
-       extent_tree_ad_new(&huge);
-
-       return (false);
-}
-
-void
-huge_prefork(void)
-{
-
-       malloc_mutex_prefork(&huge_mtx);
-}
-
-void
-huge_postfork_parent(void)
-{
+       extent_node_t *node;
+       arena_t *arena;
 
-       malloc_mutex_postfork_parent(&huge_mtx);
+       node = huge_node_get(ptr);
+       arena = extent_node_arena_get(node);
+       malloc_mutex_lock(&arena->huge_mtx);
+       extent_node_prof_tctx_set(node, tctx);
+       malloc_mutex_unlock(&arena->huge_mtx);
 }
 
 void
-huge_postfork_child(void)
+huge_prof_tctx_reset(const void *ptr)
 {
 
-       malloc_mutex_postfork_child(&huge_mtx);
+       huge_prof_tctx_set(ptr, (prof_tctx_t *)(uintptr_t)1U);
 }
index 3490ecdf6c291f4b5f39375099a4f33aa5365197..5a2d324068064abb8a50c4a3d2f688ea21143931 100644 (file)
@@ -4,8 +4,6 @@
 /******************************************************************************/
 /* Data. */
 
-malloc_tsd_data(, arenas, arena_t *, NULL)
-
 /* Runtime configuration options. */
 const char     *je_malloc_conf JEMALLOC_ATTR(weak);
 bool   opt_abort =
@@ -15,13 +13,28 @@ bool        opt_abort =
     false
 #endif
     ;
-bool   opt_junk =
+const char     *opt_junk =
+#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))
+    "true"
+#else
+    "false"
+#endif
+    ;
+bool   opt_junk_alloc =
 #if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))
     true
 #else
     false
 #endif
     ;
+bool   opt_junk_free =
+#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))
+    true
+#else
+    false
+#endif
+    ;
+
 size_t opt_quarantine = ZU(0);
 bool   opt_redzone = false;
 bool   opt_utrace = false;
@@ -34,13 +47,118 @@ bool       in_valgrind;
 
 unsigned       ncpus;
 
-malloc_mutex_t         arenas_lock;
-arena_t                        **arenas;
-unsigned               narenas_total;
-unsigned               narenas_auto;
-
-/* Set to true once the allocator has been initialized. */
-static bool            malloc_initialized = false;
+/* Protects arenas initialization (arenas, narenas_total). */
+static malloc_mutex_t  arenas_lock;
+/*
+ * Arenas that are used to service external requests.  Not all elements of the
+ * arenas array are necessarily used; arenas are created lazily as needed.
+ *
+ * arenas[0..narenas_auto) are used for automatic multiplexing of threads and
+ * arenas.  arenas[narenas_auto..narenas_total) are only used if the application
+ * takes some action to create them and allocate from them.
+ */
+static arena_t         **arenas;
+static unsigned                narenas_total;
+static arena_t         *a0; /* arenas[0]; read-only after initialization. */
+static unsigned                narenas_auto; /* Read-only after initialization. */
+
+typedef enum {
+       malloc_init_uninitialized       = 3,
+       malloc_init_a0_initialized      = 2,
+       malloc_init_recursible          = 1,
+       malloc_init_initialized         = 0 /* Common case --> jnz. */
+} malloc_init_t;
+static malloc_init_t   malloc_init_state = malloc_init_uninitialized;
+
+JEMALLOC_ALIGNED(CACHELINE)
+const size_t   index2size_tab[NSIZES] = {
+#define        SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup) \
+       ((ZU(1)<<lg_grp) + (ZU(ndelta)<<lg_delta)),
+       SIZE_CLASSES
+#undef SC
+};
+
+JEMALLOC_ALIGNED(CACHELINE)
+const uint8_t  size2index_tab[] = {
+#if LG_TINY_MIN == 0
+#warning "Dangerous LG_TINY_MIN"
+#define        S2B_0(i)        i,
+#elif LG_TINY_MIN == 1
+#warning "Dangerous LG_TINY_MIN"
+#define        S2B_1(i)        i,
+#elif LG_TINY_MIN == 2
+#warning "Dangerous LG_TINY_MIN"
+#define        S2B_2(i)        i,
+#elif LG_TINY_MIN == 3
+#define        S2B_3(i)        i,
+#elif LG_TINY_MIN == 4
+#define        S2B_4(i)        i,
+#elif LG_TINY_MIN == 5
+#define        S2B_5(i)        i,
+#elif LG_TINY_MIN == 6
+#define        S2B_6(i)        i,
+#elif LG_TINY_MIN == 7
+#define        S2B_7(i)        i,
+#elif LG_TINY_MIN == 8
+#define        S2B_8(i)        i,
+#elif LG_TINY_MIN == 9
+#define        S2B_9(i)        i,
+#elif LG_TINY_MIN == 10
+#define        S2B_10(i)       i,
+#elif LG_TINY_MIN == 11
+#define        S2B_11(i)       i,
+#else
+#error "Unsupported LG_TINY_MIN"
+#endif
+#if LG_TINY_MIN < 1
+#define        S2B_1(i)        S2B_0(i) S2B_0(i)
+#endif
+#if LG_TINY_MIN < 2
+#define        S2B_2(i)        S2B_1(i) S2B_1(i)
+#endif
+#if LG_TINY_MIN < 3
+#define        S2B_3(i)        S2B_2(i) S2B_2(i)
+#endif
+#if LG_TINY_MIN < 4
+#define        S2B_4(i)        S2B_3(i) S2B_3(i)
+#endif
+#if LG_TINY_MIN < 5
+#define        S2B_5(i)        S2B_4(i) S2B_4(i)
+#endif
+#if LG_TINY_MIN < 6
+#define        S2B_6(i)        S2B_5(i) S2B_5(i)
+#endif
+#if LG_TINY_MIN < 7
+#define        S2B_7(i)        S2B_6(i) S2B_6(i)
+#endif
+#if LG_TINY_MIN < 8
+#define        S2B_8(i)        S2B_7(i) S2B_7(i)
+#endif
+#if LG_TINY_MIN < 9
+#define        S2B_9(i)        S2B_8(i) S2B_8(i)
+#endif
+#if LG_TINY_MIN < 10
+#define        S2B_10(i)       S2B_9(i) S2B_9(i)
+#endif
+#if LG_TINY_MIN < 11
+#define        S2B_11(i)       S2B_10(i) S2B_10(i)
+#endif
+#define        S2B_no(i)
+#define        SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup) \
+       S2B_##lg_delta_lookup(index)
+       SIZE_CLASSES
+#undef S2B_3
+#undef S2B_4
+#undef S2B_5
+#undef S2B_6
+#undef S2B_7
+#undef S2B_8
+#undef S2B_9
+#undef S2B_10
+#undef S2B_11
+#undef S2B_no
+#undef SC
+};
 
 #ifdef JEMALLOC_THREADED_INIT
 /* Used to let the initializing thread recursively allocate. */
@@ -57,14 +175,28 @@ static bool                        malloc_initializer = NO_INITIALIZER;
 
 /* Used to avoid initialization races. */
 #ifdef _WIN32
+#if _WIN32_WINNT >= 0x0600
+static malloc_mutex_t  init_lock = SRWLOCK_INIT;
+#else
 static malloc_mutex_t  init_lock;
+static bool init_lock_initialized = false;
 
 JEMALLOC_ATTR(constructor)
 static void WINAPI
 _init_init_lock(void)
 {
 
-       malloc_mutex_init(&init_lock);
+       /* If another constructor in the same binary is using mallctl to
+        * e.g. setup chunk hooks, it may end up running before this one,
+        * and malloc_init_hard will crash trying to lock the uninitialized
+        * lock. So we force an initialization of the lock in
+        * malloc_init_hard as well. We don't try to care about atomicity
+        * of the accessed to the init_lock_initialized boolean, since it
+        * really only matters early in the process creation, before any
+        * separate thread normally starts doing anything. */
+       if (!init_lock_initialized)
+               malloc_mutex_init(&init_lock);
+       init_lock_initialized = true;
 }
 
 #ifdef _MSC_VER
@@ -72,7 +204,7 @@ _init_init_lock(void)
 JEMALLOC_SECTION(".CRT$XCU") JEMALLOC_ATTR(used)
 static const void (WINAPI *init_init_lock)(void) = _init_init_lock;
 #endif
-
+#endif
 #else
 static malloc_mutex_t  init_lock = MALLOC_MUTEX_INITIALIZER;
 #endif
@@ -105,6 +237,7 @@ typedef struct {
  * definition.
  */
 
+static bool    malloc_init_hard_a0(void);
 static bool    malloc_init_hard(void);
 
 /******************************************************************************/
@@ -112,35 +245,333 @@ static bool      malloc_init_hard(void);
  * Begin miscellaneous support functions.
  */
 
+JEMALLOC_ALWAYS_INLINE_C bool
+malloc_initialized(void)
+{
+
+       return (malloc_init_state == malloc_init_initialized);
+}
+
+JEMALLOC_ALWAYS_INLINE_C void
+malloc_thread_init(void)
+{
+
+       /*
+        * TSD initialization can't be safely done as a side effect of
+        * deallocation, because it is possible for a thread to do nothing but
+        * deallocate its TLS data via free(), in which case writing to TLS
+        * would cause write-after-free memory corruption.  The quarantine
+        * facility *only* gets used as a side effect of deallocation, so make
+        * a best effort attempt at initializing its TSD by hooking all
+        * allocation events.
+        */
+       if (config_fill && unlikely(opt_quarantine))
+               quarantine_alloc_hook();
+}
+
+JEMALLOC_ALWAYS_INLINE_C bool
+malloc_init_a0(void)
+{
+
+       if (unlikely(malloc_init_state == malloc_init_uninitialized))
+               return (malloc_init_hard_a0());
+       return (false);
+}
+
+JEMALLOC_ALWAYS_INLINE_C bool
+malloc_init(void)
+{
+
+       if (unlikely(!malloc_initialized()) && malloc_init_hard())
+               return (true);
+       malloc_thread_init();
+
+       return (false);
+}
+
+/*
+ * The a0*() functions are used instead of i[mcd]alloc() in situations that
+ * cannot tolerate TLS variable access.
+ */
+
+arena_t *
+a0get(void)
+{
+
+       assert(a0 != NULL);
+       return (a0);
+}
+
+static void *
+a0ialloc(size_t size, bool zero, bool is_metadata)
+{
+
+       if (unlikely(malloc_init_a0()))
+               return (NULL);
+
+       return (iallocztm(NULL, size, zero, false, is_metadata, a0get()));
+}
+
+static void
+a0idalloc(void *ptr, bool is_metadata)
+{
+
+       idalloctm(NULL, ptr, false, is_metadata);
+}
+
+void *
+a0malloc(size_t size)
+{
+
+       return (a0ialloc(size, false, true));
+}
+
+void
+a0dalloc(void *ptr)
+{
+
+       a0idalloc(ptr, true);
+}
+
+/*
+ * FreeBSD's libc uses the bootstrap_*() functions in bootstrap-senstive
+ * situations that cannot tolerate TLS variable access (TLS allocation and very
+ * early internal data structure initialization).
+ */
+
+void *
+bootstrap_malloc(size_t size)
+{
+
+       if (unlikely(size == 0))
+               size = 1;
+
+       return (a0ialloc(size, false, false));
+}
+
+void *
+bootstrap_calloc(size_t num, size_t size)
+{
+       size_t num_size;
+
+       num_size = num * size;
+       if (unlikely(num_size == 0)) {
+               assert(num == 0 || size == 0);
+               num_size = 1;
+       }
+
+       return (a0ialloc(num_size, true, false));
+}
+
+void
+bootstrap_free(void *ptr)
+{
+
+       if (unlikely(ptr == NULL))
+               return;
+
+       a0idalloc(ptr, false);
+}
+
 /* Create a new arena and insert it into the arenas array at index ind. */
+static arena_t *
+arena_init_locked(unsigned ind)
+{
+       arena_t *arena;
+
+       /* Expand arenas if necessary. */
+       assert(ind <= narenas_total);
+       if (ind > MALLOCX_ARENA_MAX)
+               return (NULL);
+       if (ind == narenas_total) {
+               unsigned narenas_new = narenas_total + 1;
+               arena_t **arenas_new =
+                   (arena_t **)a0malloc(CACHELINE_CEILING(narenas_new *
+                   sizeof(arena_t *)));
+               if (arenas_new == NULL)
+                       return (NULL);
+               memcpy(arenas_new, arenas, narenas_total * sizeof(arena_t *));
+               arenas_new[ind] = NULL;
+               /*
+                * Deallocate only if arenas came from a0malloc() (not
+                * base_alloc()).
+                */
+               if (narenas_total != narenas_auto)
+                       a0dalloc(arenas);
+               arenas = arenas_new;
+               narenas_total = narenas_new;
+       }
+
+       /*
+        * Another thread may have already initialized arenas[ind] if it's an
+        * auto arena.
+        */
+       arena = arenas[ind];
+       if (arena != NULL) {
+               assert(ind < narenas_auto);
+               return (arena);
+       }
+
+       /* Actually initialize the arena. */
+       arena = arenas[ind] = arena_new(ind);
+       return (arena);
+}
+
 arena_t *
-arenas_extend(unsigned ind)
+arena_init(unsigned ind)
 {
-       arena_t *ret;
+       arena_t *arena;
+
+       malloc_mutex_lock(&arenas_lock);
+       arena = arena_init_locked(ind);
+       malloc_mutex_unlock(&arenas_lock);
+       return (arena);
+}
+
+unsigned
+narenas_total_get(void)
+{
+       unsigned narenas;
+
+       malloc_mutex_lock(&arenas_lock);
+       narenas = narenas_total;
+       malloc_mutex_unlock(&arenas_lock);
+
+       return (narenas);
+}
+
+static void
+arena_bind_locked(tsd_t *tsd, unsigned ind)
+{
+       arena_t *arena;
+
+       arena = arenas[ind];
+       arena->nthreads++;
+
+       if (tsd_nominal(tsd))
+               tsd_arena_set(tsd, arena);
+}
+
+static void
+arena_bind(tsd_t *tsd, unsigned ind)
+{
+
+       malloc_mutex_lock(&arenas_lock);
+       arena_bind_locked(tsd, ind);
+       malloc_mutex_unlock(&arenas_lock);
+}
+
+void
+arena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind)
+{
+       arena_t *oldarena, *newarena;
+
+       malloc_mutex_lock(&arenas_lock);
+       oldarena = arenas[oldind];
+       newarena = arenas[newind];
+       oldarena->nthreads--;
+       newarena->nthreads++;
+       malloc_mutex_unlock(&arenas_lock);
+       tsd_arena_set(tsd, newarena);
+}
+
+unsigned
+arena_nbound(unsigned ind)
+{
+       unsigned nthreads;
+
+       malloc_mutex_lock(&arenas_lock);
+       nthreads = arenas[ind]->nthreads;
+       malloc_mutex_unlock(&arenas_lock);
+       return (nthreads);
+}
+
+static void
+arena_unbind(tsd_t *tsd, unsigned ind)
+{
+       arena_t *arena;
 
-       ret = (arena_t *)base_alloc(sizeof(arena_t));
-       if (ret != NULL && !arena_new(ret, ind)) {
-               arenas[ind] = ret;
-               return (ret);
+       malloc_mutex_lock(&arenas_lock);
+       arena = arenas[ind];
+       arena->nthreads--;
+       malloc_mutex_unlock(&arenas_lock);
+       tsd_arena_set(tsd, NULL);
+}
+
+arena_t *
+arena_get_hard(tsd_t *tsd, unsigned ind, bool init_if_missing)
+{
+       arena_t *arena;
+       arena_t **arenas_cache = tsd_arenas_cache_get(tsd);
+       unsigned narenas_cache = tsd_narenas_cache_get(tsd);
+       unsigned narenas_actual = narenas_total_get();
+
+       /* Deallocate old cache if it's too small. */
+       if (arenas_cache != NULL && narenas_cache < narenas_actual) {
+               a0dalloc(arenas_cache);
+               arenas_cache = NULL;
+               narenas_cache = 0;
+               tsd_arenas_cache_set(tsd, arenas_cache);
+               tsd_narenas_cache_set(tsd, narenas_cache);
+       }
+
+       /* Allocate cache if it's missing. */
+       if (arenas_cache == NULL) {
+               bool *arenas_cache_bypassp = tsd_arenas_cache_bypassp_get(tsd);
+               assert(ind < narenas_actual || !init_if_missing);
+               narenas_cache = (ind < narenas_actual) ? narenas_actual : ind+1;
+
+               if (tsd_nominal(tsd) && !*arenas_cache_bypassp) {
+                       *arenas_cache_bypassp = true;
+                       arenas_cache = (arena_t **)a0malloc(sizeof(arena_t *) *
+                           narenas_cache);
+                       *arenas_cache_bypassp = false;
+               }
+               if (arenas_cache == NULL) {
+                       /*
+                        * This function must always tell the truth, even if
+                        * it's slow, so don't let OOM, thread cleanup (note
+                        * tsd_nominal check), nor recursive allocation
+                        * avoidance (note arenas_cache_bypass check) get in the
+                        * way.
+                        */
+                       if (ind >= narenas_actual)
+                               return (NULL);
+                       malloc_mutex_lock(&arenas_lock);
+                       arena = arenas[ind];
+                       malloc_mutex_unlock(&arenas_lock);
+                       return (arena);
+               }
+               assert(tsd_nominal(tsd) && !*arenas_cache_bypassp);
+               tsd_arenas_cache_set(tsd, arenas_cache);
+               tsd_narenas_cache_set(tsd, narenas_cache);
        }
-       /* Only reached if there is an OOM error. */
 
        /*
-        * OOM here is quite inconvenient to propagate, since dealing with it
-        * would require a check for failure in the fast path.  Instead, punt
-        * by using arenas[0].  In practice, this is an extremely unlikely
-        * failure.
+        * Copy to cache.  It's possible that the actual number of arenas has
+        * increased since narenas_total_get() was called above, but that causes
+        * no correctness issues unless two threads concurrently execute the
+        * arenas.extend mallctl, which we trust mallctl synchronization to
+        * prevent.
         */
-       malloc_write("<jemalloc>: Error initializing arena\n");
-       if (opt_abort)
-               abort();
+       malloc_mutex_lock(&arenas_lock);
+       memcpy(arenas_cache, arenas, sizeof(arena_t *) * narenas_actual);
+       malloc_mutex_unlock(&arenas_lock);
+       if (narenas_cache > narenas_actual) {
+               memset(&arenas_cache[narenas_actual], 0, sizeof(arena_t *) *
+                   (narenas_cache - narenas_actual));
+       }
 
-       return (arenas[0]);
+       /* Read the refreshed cache, and init the arena if necessary. */
+       arena = arenas_cache[ind];
+       if (init_if_missing && arena == NULL)
+               arena = arenas_cache[ind] = arena_init(ind);
+       return (arena);
 }
 
-/* Slow path, called only by choose_arena(). */
+/* Slow path, called only by arena_choose(). */
 arena_t *
-choose_arena_hard(tsd_t *tsd)
+arena_choose_hard(tsd_t *tsd)
 {
        arena_t *ret;
 
@@ -150,7 +581,7 @@ choose_arena_hard(tsd_t *tsd)
                choose = 0;
                first_null = narenas_auto;
                malloc_mutex_lock(&arenas_lock);
-               assert(arenas[0] != NULL);
+               assert(a0get() != NULL);
                for (i = 1; i < narenas_auto; i++) {
                        if (arenas[i] != NULL) {
                                /*
@@ -183,20 +614,20 @@ choose_arena_hard(tsd_t *tsd)
                        ret = arenas[choose];
                } else {
                        /* Initialize a new arena. */
-                       ret = arenas_extend(first_null);
+                       choose = first_null;
+                       ret = arena_init_locked(choose);
+                       if (ret == NULL) {
+                               malloc_mutex_unlock(&arenas_lock);
+                               return (NULL);
+                       }
                }
-               ret->nthreads++;
+               arena_bind_locked(tsd, choose);
                malloc_mutex_unlock(&arenas_lock);
        } else {
-               ret = arenas[0];
-               malloc_mutex_lock(&arenas_lock);
-               ret->nthreads++;
-               malloc_mutex_unlock(&arenas_lock);
+               ret = a0get();
+               arena_bind(tsd, 0);
        }
 
-       if (tsd_nominal(tsd))
-               tsd_arena_set(tsd, ret);
-
        return (ret);
 }
 
@@ -216,6 +647,35 @@ thread_deallocated_cleanup(tsd_t *tsd)
 
 void
 arena_cleanup(tsd_t *tsd)
+{
+       arena_t *arena;
+
+       arena = tsd_arena_get(tsd);
+       if (arena != NULL)
+               arena_unbind(tsd, arena->ind);
+}
+
+void
+arenas_cache_cleanup(tsd_t *tsd)
+{
+       arena_t **arenas_cache;
+
+       arenas_cache = tsd_arenas_cache_get(tsd);
+       if (arenas_cache != NULL) {
+               tsd_arenas_cache_set(tsd, NULL);
+               a0dalloc(arenas_cache);
+       }
+}
+
+void
+narenas_cache_cleanup(tsd_t *tsd)
+{
+
+       /* Do nothing. */
+}
+
+void
+arenas_cache_bypass_cleanup(tsd_t *tsd)
 {
 
        /* Do nothing. */
@@ -265,6 +725,19 @@ stats_print_atexit(void)
  * Begin initialization functions.
  */
 
+#ifndef JEMALLOC_HAVE_SECURE_GETENV
+static char *
+secure_getenv(const char *name)
+{
+
+#  ifdef JEMALLOC_HAVE_ISSETUGID
+       if (issetugid() != 0)
+               return (NULL);
+#  endif
+       return (getenv(name));
+}
+#endif
+
 static unsigned
 malloc_ncpus(void)
 {
@@ -280,44 +753,6 @@ malloc_ncpus(void)
        return ((result == -1) ? 1 : (unsigned)result);
 }
 
-void
-arenas_cleanup(void *arg)
-{
-       arena_t *arena = *(arena_t **)arg;
-
-       malloc_mutex_lock(&arenas_lock);
-       arena->nthreads--;
-       malloc_mutex_unlock(&arenas_lock);
-}
-
-JEMALLOC_ALWAYS_INLINE_C void
-malloc_thread_init(void)
-{
-
-       /*
-        * TSD initialization can't be safely done as a side effect of
-        * deallocation, because it is possible for a thread to do nothing but
-        * deallocate its TLS data via free(), in which case writing to TLS
-        * would cause write-after-free memory corruption.  The quarantine
-        * facility *only* gets used as a side effect of deallocation, so make
-        * a best effort attempt at initializing its TSD by hooking all
-        * allocation events.
-        */
-       if (config_fill && unlikely(opt_quarantine))
-               quarantine_alloc_hook();
-}
-
-JEMALLOC_ALWAYS_INLINE_C bool
-malloc_init(void)
-{
-
-       if (unlikely(!malloc_initialized) && malloc_init_hard())
-               return (true);
-       malloc_thread_init();
-
-       return (false);
-}
-
 static bool
 malloc_conf_next(char const **opts_p, char const **k_p, size_t *klen_p,
     char const **v_p, size_t *vlen_p)
@@ -418,7 +853,9 @@ malloc_conf_init(void)
        if (config_valgrind) {
                in_valgrind = (RUNNING_ON_VALGRIND != 0) ? true : false;
                if (config_fill && unlikely(in_valgrind)) {
-                       opt_junk = false;
+                       opt_junk = "false";
+                       opt_junk_alloc = false;
+                       opt_junk_free = false;
                        assert(!opt_zero);
                        opt_quarantine = JEMALLOC_VALGRIND_QUARANTINE_DEFAULT;
                        opt_redzone = true;
@@ -463,7 +900,7 @@ malloc_conf_init(void)
                        if (linklen == -1) {
                                /* No configuration specified. */
                                linklen = 0;
-                               /* restore errno */
+                               /* Restore errno. */
                                set_errno(saved_errno);
                        }
 #endif
@@ -479,7 +916,7 @@ malloc_conf_init(void)
 #endif
                            ;
 
-                       if ((opts = getenv(envname)) != NULL) {
+                       if ((opts = secure_getenv(envname)) != NULL) {
                                /*
                                 * Do nothing; opts is already initialized to
                                 * the value of the MALLOC_CONF environment
@@ -501,13 +938,13 @@ malloc_conf_init(void)
                    &vlen)) {
 #define        CONF_MATCH(n)                                                   \
        (sizeof(n)-1 == klen && strncmp(n, k, klen) == 0)
+#define        CONF_MATCH_VALUE(n)                                             \
+       (sizeof(n)-1 == vlen && strncmp(n, v, vlen) == 0)
 #define        CONF_HANDLE_BOOL(o, n, cont)                                    \
                        if (CONF_MATCH(n)) {                            \
-                               if (strncmp("true", v, vlen) == 0 &&    \
-                                   vlen == sizeof("true")-1)           \
+                               if (CONF_MATCH_VALUE("true"))           \
                                        o = true;                       \
-                               else if (strncmp("false", v, vlen) ==   \
-                                   0 && vlen == sizeof("false")-1)     \
+                               else if (CONF_MATCH_VALUE("false"))     \
                                        o = false;                      \
                                else {                                  \
                                        malloc_conf_error(              \
@@ -530,15 +967,15 @@ malloc_conf_init(void)
                                            "Invalid conf value",       \
                                            k, klen, v, vlen);          \
                                } else if (clip) {                      \
-                                       if (min != 0 && um < min)       \
-                                               o = min;                \
-                                       else if (um > max)              \
-                                               o = max;                \
+                                       if ((min) != 0 && um < (min))   \
+                                               o = (min);              \
+                                       else if (um > (max))            \
+                                               o = (max);              \
                                        else                            \
                                                o = um;                 \
                                } else {                                \
-                                       if ((min != 0 && um < min) ||   \
-                                           um > max) {                 \
+                                       if (((min) != 0 && um < (min))  \
+                                           || um > (max)) {            \
                                                malloc_conf_error(      \
                                                    "Out-of-range "     \
                                                    "conf value",       \
@@ -560,8 +997,8 @@ malloc_conf_init(void)
                                        malloc_conf_error(              \
                                            "Invalid conf value",       \
                                            k, klen, v, vlen);          \
-                               } else if (l < (ssize_t)min || l >      \
-                                   (ssize_t)max) {                     \
+                               } else if (l < (ssize_t)(min) || l >    \
+                                   (ssize_t)(max)) {                   \
                                        malloc_conf_error(              \
                                            "Out-of-range conf value",  \
                                            k, klen, v, vlen);          \
@@ -581,15 +1018,16 @@ malloc_conf_init(void)
 
                        CONF_HANDLE_BOOL(opt_abort, "abort", true)
                        /*
-                        * Chunks always require at least one header page, plus
-                        * one data page in the absence of redzones, or three
-                        * pages in the presence of redzones.  In order to
-                        * simplify options processing, fix the limit based on
-                        * config_fill.
+                        * Chunks always require at least one header page,
+                        * as many as 2^(LG_SIZE_CLASS_GROUP+1) data pages, and
+                        * possibly an additional page in the presence of
+                        * redzones.  In order to simplify options processing,
+                        * use a conservative bound that accommodates all these
+                        * constraints.
                         */
                        CONF_HANDLE_SIZE_T(opt_lg_chunk, "lg_chunk", LG_PAGE +
-                           (config_fill ? 2 : 1), (sizeof(size_t) << 3) - 1,
-                           true)
+                           LG_SIZE_CLASS_GROUP + (config_fill ? 2 : 1),
+                           (sizeof(size_t) << 3) - 1, true)
                        if (strncmp("dss", k, klen) == 0) {
                                int i;
                                bool match = false;
@@ -620,7 +1058,30 @@ malloc_conf_init(void)
                            -1, (sizeof(size_t) << 3) - 1)
                        CONF_HANDLE_BOOL(opt_stats_print, "stats_print", true)
                        if (config_fill) {
-                               CONF_HANDLE_BOOL(opt_junk, "junk", true)
+                               if (CONF_MATCH("junk")) {
+                                       if (CONF_MATCH_VALUE("true")) {
+                                               opt_junk = "true";
+                                               opt_junk_alloc = opt_junk_free =
+                                                   true;
+                                       } else if (CONF_MATCH_VALUE("false")) {
+                                               opt_junk = "false";
+                                               opt_junk_alloc = opt_junk_free =
+                                                   false;
+                                       } else if (CONF_MATCH_VALUE("alloc")) {
+                                               opt_junk = "alloc";
+                                               opt_junk_alloc = true;
+                                               opt_junk_free = false;
+                                       } else if (CONF_MATCH_VALUE("free")) {
+                                               opt_junk = "free";
+                                               opt_junk_alloc = false;
+                                               opt_junk_free = true;
+                                       } else {
+                                               malloc_conf_error(
+                                                   "Invalid conf value", k,
+                                                   klen, v, vlen);
+                                       }
+                                       continue;
+                               }
                                CONF_HANDLE_SIZE_T(opt_quarantine, "quarantine",
                                    0, SIZE_T_MAX, false)
                                CONF_HANDLE_BOOL(opt_redzone, "redzone", true)
@@ -684,19 +1145,18 @@ malloc_conf_init(void)
        }
 }
 
+/* init_lock must be held. */
 static bool
-malloc_init_hard(void)
+malloc_init_hard_needed(void)
 {
-       arena_t *init_arenas[1];
 
-       malloc_mutex_lock(&init_lock);
-       if (malloc_initialized || IS_INITIALIZER) {
+       if (malloc_initialized() || (IS_INITIALIZER && malloc_init_state ==
+           malloc_init_recursible)) {
                /*
                 * Another thread initialized the allocator before this one
                 * acquired init_lock, or this thread is the initializing
                 * thread, and it is recursively allocating.
                 */
-               malloc_mutex_unlock(&init_lock);
                return (false);
        }
 #ifdef JEMALLOC_THREADED_INIT
@@ -706,23 +1166,23 @@ malloc_init_hard(void)
                        malloc_mutex_unlock(&init_lock);
                        CPU_SPINWAIT;
                        malloc_mutex_lock(&init_lock);
-               } while (!malloc_initialized);
-               malloc_mutex_unlock(&init_lock);
+               } while (!malloc_initialized());
                return (false);
        }
 #endif
-       malloc_initializer = INITIALIZER;
+       return (true);
+}
 
-       if (malloc_tsd_boot()) {
-               malloc_mutex_unlock(&init_lock);
-               return (true);
-       }
+/* init_lock must be held. */
+static bool
+malloc_init_hard_a0_locked(void)
+{
+
+       malloc_initializer = INITIALIZER;
 
        if (config_prof)
                prof_boot0();
-
        malloc_conf_init();
-
        if (opt_stats_print) {
                /* Print statistics at exit. */
                if (atexit(stats_print_atexit) != 0) {
@@ -731,68 +1191,59 @@ malloc_init_hard(void)
                                abort();
                }
        }
-
-       if (base_boot()) {
-               malloc_mutex_unlock(&init_lock);
+       if (base_boot())
                return (true);
-       }
-
-       if (chunk_boot()) {
-               malloc_mutex_unlock(&init_lock);
+       if (chunk_boot())
                return (true);
-       }
-
-       if (ctl_boot()) {
-               malloc_mutex_unlock(&init_lock);
+       if (ctl_boot())
                return (true);
-       }
-
        if (config_prof)
                prof_boot1();
-
-       arena_boot();
-
-       if (config_tcache && tcache_boot()) {
-               malloc_mutex_unlock(&init_lock);
+       if (arena_boot())
                return (true);
-       }
-
-       if (huge_boot()) {
-               malloc_mutex_unlock(&init_lock);
+       if (config_tcache && tcache_boot())
                return (true);
-       }
-
-       if (malloc_mutex_init(&arenas_lock)) {
-               malloc_mutex_unlock(&init_lock);
+       if (malloc_mutex_init(&arenas_lock))
                return (true);
-       }
-
        /*
         * Create enough scaffolding to allow recursive allocation in
         * malloc_ncpus().
         */
        narenas_total = narenas_auto = 1;
-       arenas = init_arenas;
+       arenas = &a0;
        memset(arenas, 0, sizeof(arena_t *) * narenas_auto);
-
        /*
         * Initialize one arena here.  The rest are lazily created in
-        * choose_arena_hard().
+        * arena_choose_hard().
         */
-       arenas_extend(0);
-       if (arenas[0] == NULL) {
-               malloc_mutex_unlock(&init_lock);
+       if (arena_init(0) == NULL)
                return (true);
-       }
+       malloc_init_state = malloc_init_a0_initialized;
+       return (false);
+}
 
-       if (config_prof && prof_boot2()) {
-               malloc_mutex_unlock(&init_lock);
-               return (true);
-       }
+static bool
+malloc_init_hard_a0(void)
+{
+       bool ret;
+
+       malloc_mutex_lock(&init_lock);
+       ret = malloc_init_hard_a0_locked();
+       malloc_mutex_unlock(&init_lock);
+       return (ret);
+}
+
+/*
+ * Initialize data structures which may trigger recursive allocation.
+ *
+ * init_lock must be held.
+ */
+static void
+malloc_init_hard_recursible(void)
+{
 
+       malloc_init_state = malloc_init_recursible;
        malloc_mutex_unlock(&init_lock);
-       /**********************************************************************/
-       /* Recursive allocation may follow. */
 
        ncpus = malloc_ncpus();
 
@@ -806,15 +1257,16 @@ malloc_init_hard(void)
                        abort();
        }
 #endif
-
-       /* Done recursively allocating. */
-       /**********************************************************************/
        malloc_mutex_lock(&init_lock);
+}
 
-       if (mutex_boot()) {
-               malloc_mutex_unlock(&init_lock);
+/* init_lock must be held. */
+static bool
+malloc_init_hard_finish(void)
+{
+
+       if (mutex_boot())
                return (true);
-       }
 
        if (opt_narenas == 0) {
                /*
@@ -841,21 +1293,56 @@ malloc_init_hard(void)
 
        /* Allocate and initialize arenas. */
        arenas = (arena_t **)base_alloc(sizeof(arena_t *) * narenas_total);
-       if (arenas == NULL) {
-               malloc_mutex_unlock(&init_lock);
+       if (arenas == NULL)
                return (true);
-       }
        /*
         * Zero the array.  In practice, this should always be pre-zeroed,
         * since it was just mmap()ed, but let's be sure.
         */
        memset(arenas, 0, sizeof(arena_t *) * narenas_total);
        /* Copy the pointer to the one arena that was already initialized. */
-       arenas[0] = init_arenas[0];
+       arenas[0] = a0;
 
-       malloc_initialized = true;
-       malloc_mutex_unlock(&init_lock);
+       malloc_init_state = malloc_init_initialized;
+       return (false);
+}
+
+static bool
+malloc_init_hard(void)
+{
+
+#if defined(_WIN32) && _WIN32_WINNT < 0x0600
+       _init_init_lock();
+#endif
+       malloc_mutex_lock(&init_lock);
+       if (!malloc_init_hard_needed()) {
+               malloc_mutex_unlock(&init_lock);
+               return (false);
+       }
+
+       if (malloc_init_state != malloc_init_a0_initialized &&
+           malloc_init_hard_a0_locked()) {
+               malloc_mutex_unlock(&init_lock);
+               return (true);
+       }
+       if (malloc_tsd_boot0()) {
+               malloc_mutex_unlock(&init_lock);
+               return (true);
+       }
+       if (config_prof && prof_boot2()) {
+               malloc_mutex_unlock(&init_lock);
+               return (true);
+       }
+
+       malloc_init_hard_recursible();
+
+       if (malloc_init_hard_finish()) {
+               malloc_mutex_unlock(&init_lock);
+               return (true);
+       }
 
+       malloc_mutex_unlock(&init_lock);
+       malloc_tsd_boot1();
        return (false);
 }
 
@@ -891,12 +1378,12 @@ imalloc_prof(tsd_t *tsd, size_t usize)
        void *p;
        prof_tctx_t *tctx;
 
-       tctx = prof_alloc_prep(tsd, usize, true);
+       tctx = prof_alloc_prep(tsd, usize, prof_active_get_unlocked(), true);
        if (unlikely((uintptr_t)tctx != (uintptr_t)1U))
                p = imalloc_prof_sample(tsd, usize, tctx);
        else
                p = imalloc(tsd, usize);
-       if (p == NULL) {
+       if (unlikely(p == NULL)) {
                prof_alloc_rollback(tsd, tctx, true);
                return (NULL);
        }
@@ -915,6 +1402,8 @@ imalloc_body(size_t size, tsd_t **tsd, size_t *usize)
 
        if (config_prof && opt_prof) {
                *usize = s2u(size);
+               if (unlikely(*usize == 0))
+                       return (NULL);
                return (imalloc_prof(*tsd, *usize));
        }
 
@@ -923,7 +1412,9 @@ imalloc_body(size_t size, tsd_t **tsd, size_t *usize)
        return (imalloc(*tsd, size));
 }
 
-void *
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+void JEMALLOC_NOTHROW *
+JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)
 je_malloc(size_t size)
 {
        void *ret;
@@ -961,7 +1452,7 @@ imemalign_prof_sample(tsd_t *tsd, size_t alignment, size_t usize,
                return (NULL);
        if (usize <= SMALL_MAXCLASS) {
                assert(sa2u(LARGE_MINCLASS, alignment) == LARGE_MINCLASS);
-               p = imalloc(tsd, LARGE_MINCLASS);
+               p = ipalloc(tsd, LARGE_MINCLASS, alignment, false);
                if (p == NULL)
                        return (NULL);
                arena_prof_promoted(p, usize);
@@ -977,12 +1468,12 @@ imemalign_prof(tsd_t *tsd, size_t alignment, size_t usize)
        void *p;
        prof_tctx_t *tctx;
 
-       tctx = prof_alloc_prep(tsd, usize, true);
+       tctx = prof_alloc_prep(tsd, usize, prof_active_get_unlocked(), true);
        if (unlikely((uintptr_t)tctx != (uintptr_t)1U))
                p = imemalign_prof_sample(tsd, alignment, usize, tctx);
        else
                p = ipalloc(tsd, usize, alignment, false);
-       if (p == NULL) {
+       if (unlikely(p == NULL)) {
                prof_alloc_rollback(tsd, tctx, true);
                return (NULL);
        }
@@ -1005,38 +1496,38 @@ imemalign(void **memptr, size_t alignment, size_t size, size_t min_alignment)
        if (unlikely(malloc_init())) {
                result = NULL;
                goto label_oom;
-       } else {
-               tsd = tsd_fetch();
-               if (size == 0)
-                       size = 1;
-
-               /* Make sure that alignment is a large enough power of 2. */
-               if (unlikely(((alignment - 1) & alignment) != 0
-                   || (alignment < min_alignment))) {
-                       if (config_xmalloc && unlikely(opt_xmalloc)) {
-                               malloc_write("<jemalloc>: Error allocating "
-                                   "aligned memory: invalid alignment\n");
-                               abort();
-                       }
-                       result = NULL;
-                       ret = EINVAL;
-                       goto label_return;
-               }
+       }
+       tsd = tsd_fetch();
+       if (size == 0)
+               size = 1;
 
-               usize = sa2u(size, alignment);
-               if (unlikely(usize == 0)) {
-                       result = NULL;
-                       goto label_oom;
+       /* Make sure that alignment is a large enough power of 2. */
+       if (unlikely(((alignment - 1) & alignment) != 0
+           || (alignment < min_alignment))) {
+               if (config_xmalloc && unlikely(opt_xmalloc)) {
+                       malloc_write("<jemalloc>: Error allocating "
+                           "aligned memory: invalid alignment\n");
+                       abort();
                }
+               result = NULL;
+               ret = EINVAL;
+               goto label_return;
+       }
 
-               if (config_prof && opt_prof)
-                       result = imemalign_prof(tsd, alignment, usize);
-               else
-                       result = ipalloc(tsd, usize, alignment, false);
-               if (unlikely(result == NULL))
-                       goto label_oom;
+       usize = sa2u(size, alignment);
+       if (unlikely(usize == 0)) {
+               result = NULL;
+               goto label_oom;
        }
 
+       if (config_prof && opt_prof)
+               result = imemalign_prof(tsd, alignment, usize);
+       else
+               result = ipalloc(tsd, usize, alignment, false);
+       if (unlikely(result == NULL))
+               goto label_oom;
+       assert(((uintptr_t)result & (alignment - 1)) == ZU(0));
+
        *memptr = result;
        ret = 0;
 label_return:
@@ -1057,7 +1548,8 @@ label_oom:
        goto label_return;
 }
 
-int
+JEMALLOC_EXPORT int JEMALLOC_NOTHROW
+JEMALLOC_ATTR(nonnull(1))
 je_posix_memalign(void **memptr, size_t alignment, size_t size)
 {
        int ret = imemalign(memptr, alignment, size, sizeof(void *));
@@ -1066,7 +1558,9 @@ je_posix_memalign(void **memptr, size_t alignment, size_t size)
        return (ret);
 }
 
-void *
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+void JEMALLOC_NOTHROW *
+JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(2)
 je_aligned_alloc(size_t alignment, size_t size)
 {
        void *ret;
@@ -1105,12 +1599,12 @@ icalloc_prof(tsd_t *tsd, size_t usize)
        void *p;
        prof_tctx_t *tctx;
 
-       tctx = prof_alloc_prep(tsd, usize, true);
+       tctx = prof_alloc_prep(tsd, usize, prof_active_get_unlocked(), true);
        if (unlikely((uintptr_t)tctx != (uintptr_t)1U))
                p = icalloc_prof_sample(tsd, usize, tctx);
        else
                p = icalloc(tsd, usize);
-       if (p == NULL) {
+       if (unlikely(p == NULL)) {
                prof_alloc_rollback(tsd, tctx, true);
                return (NULL);
        }
@@ -1119,7 +1613,9 @@ icalloc_prof(tsd_t *tsd, size_t usize)
        return (p);
 }
 
-void *
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+void JEMALLOC_NOTHROW *
+JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2)
 je_calloc(size_t num, size_t size)
 {
        void *ret;
@@ -1156,6 +1652,10 @@ je_calloc(size_t num, size_t size)
 
        if (config_prof && opt_prof) {
                usize = s2u(num_size);
+               if (unlikely(usize == 0)) {
+                       ret = NULL;
+                       goto label_return;
+               }
                ret = icalloc_prof(tsd, usize);
        } else {
                if (config_stats || (config_valgrind && unlikely(in_valgrind)))
@@ -1182,50 +1682,56 @@ label_return:
 }
 
 static void *
-irealloc_prof_sample(tsd_t *tsd, void *oldptr, size_t usize, prof_tctx_t *tctx)
+irealloc_prof_sample(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,
+    prof_tctx_t *tctx)
 {
        void *p;
 
        if (tctx == NULL)
                return (NULL);
        if (usize <= SMALL_MAXCLASS) {
-               p = iralloc(tsd, oldptr, LARGE_MINCLASS, 0, false);
+               p = iralloc(tsd, old_ptr, old_usize, LARGE_MINCLASS, 0, false);
                if (p == NULL)
                        return (NULL);
                arena_prof_promoted(p, usize);
        } else
-               p = iralloc(tsd, oldptr, usize, 0, false);
+               p = iralloc(tsd, old_ptr, old_usize, usize, 0, false);
 
        return (p);
 }
 
 JEMALLOC_ALWAYS_INLINE_C void *
-irealloc_prof(tsd_t *tsd, void *oldptr, size_t old_usize, size_t usize)
+irealloc_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize)
 {
        void *p;
+       bool prof_active;
        prof_tctx_t *old_tctx, *tctx;
 
-       old_tctx = prof_tctx_get(oldptr);
-       tctx = prof_alloc_prep(tsd, usize, true);
+       prof_active = prof_active_get_unlocked();
+       old_tctx = prof_tctx_get(old_ptr);
+       tctx = prof_alloc_prep(tsd, usize, prof_active, true);
        if (unlikely((uintptr_t)tctx != (uintptr_t)1U))
-               p = irealloc_prof_sample(tsd, oldptr, usize, tctx);
+               p = irealloc_prof_sample(tsd, old_ptr, old_usize, usize, tctx);
        else
-               p = iralloc(tsd, oldptr, usize, 0, false);
-       if (p == NULL)
+               p = iralloc(tsd, old_ptr, old_usize, usize, 0, false);
+       if (unlikely(p == NULL)) {
+               prof_alloc_rollback(tsd, tctx, true);
                return (NULL);
-       prof_realloc(tsd, p, usize, tctx, true, old_usize, old_tctx);
+       }
+       prof_realloc(tsd, p, usize, tctx, prof_active, true, old_ptr, old_usize,
+           old_tctx);
 
        return (p);
 }
 
 JEMALLOC_INLINE_C void
-ifree(tsd_t *tsd, void *ptr, bool try_tcache)
+ifree(tsd_t *tsd, void *ptr, tcache_t *tcache)
 {
        size_t usize;
        UNUSED size_t rzsize JEMALLOC_CC_SILENCE_INIT(0);
 
        assert(ptr != NULL);
-       assert(malloc_initialized || IS_INITIALIZER);
+       assert(malloc_initialized() || IS_INITIALIZER);
 
        if (config_prof && opt_prof) {
                usize = isalloc(ptr, config_prof);
@@ -1236,17 +1742,17 @@ ifree(tsd_t *tsd, void *ptr, bool try_tcache)
                *tsd_thread_deallocatedp_get(tsd) += usize;
        if (config_valgrind && unlikely(in_valgrind))
                rzsize = p2rz(ptr);
-       iqalloc(tsd, ptr, try_tcache);
+       iqalloc(tsd, ptr, tcache);
        JEMALLOC_VALGRIND_FREE(ptr, rzsize);
 }
 
 JEMALLOC_INLINE_C void
-isfree(tsd_t *tsd, void *ptr, size_t usize, bool try_tcache)
+isfree(tsd_t *tsd, void *ptr, size_t usize, tcache_t *tcache)
 {
        UNUSED size_t rzsize JEMALLOC_CC_SILENCE_INIT(0);
 
        assert(ptr != NULL);
-       assert(malloc_initialized || IS_INITIALIZER);
+       assert(malloc_initialized() || IS_INITIALIZER);
 
        if (config_prof && opt_prof)
                prof_free(tsd, ptr, usize);
@@ -1254,11 +1760,13 @@ isfree(tsd_t *tsd, void *ptr, size_t usize, bool try_tcache)
                *tsd_thread_deallocatedp_get(tsd) += usize;
        if (config_valgrind && unlikely(in_valgrind))
                rzsize = p2rz(ptr);
-       isqalloc(tsd, ptr, usize, try_tcache);
+       isqalloc(tsd, ptr, usize, tcache);
        JEMALLOC_VALGRIND_FREE(ptr, rzsize);
 }
 
-void *
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+void JEMALLOC_NOTHROW *
+JEMALLOC_ALLOC_SIZE(2)
 je_realloc(void *ptr, size_t size)
 {
        void *ret;
@@ -1272,31 +1780,30 @@ je_realloc(void *ptr, size_t size)
                        /* realloc(ptr, 0) is equivalent to free(ptr). */
                        UTRACE(ptr, 0, 0);
                        tsd = tsd_fetch();
-                       ifree(tsd, ptr, true);
+                       ifree(tsd, ptr, tcache_get(tsd, false));
                        return (NULL);
                }
                size = 1;
        }
 
        if (likely(ptr != NULL)) {
-               assert(malloc_initialized || IS_INITIALIZER);
+               assert(malloc_initialized() || IS_INITIALIZER);
                malloc_thread_init();
                tsd = tsd_fetch();
 
-               if ((config_prof && opt_prof) || config_stats ||
-                   (config_valgrind && unlikely(in_valgrind)))
-                       old_usize = isalloc(ptr, config_prof);
+               old_usize = isalloc(ptr, config_prof);
                if (config_valgrind && unlikely(in_valgrind))
                        old_rzsize = config_prof ? p2rz(ptr) : u2rz(old_usize);
 
                if (config_prof && opt_prof) {
                        usize = s2u(size);
-                       ret = irealloc_prof(tsd, ptr, old_usize, usize);
+                       ret = unlikely(usize == 0) ? NULL : irealloc_prof(tsd,
+                           ptr, old_usize, usize);
                } else {
                        if (config_stats || (config_valgrind &&
                            unlikely(in_valgrind)))
                                usize = s2u(size);
-                       ret = iralloc(tsd, ptr, size, 0, false);
+                       ret = iralloc(tsd, ptr, old_usize, size, 0, false);
                }
        } else {
                /* realloc(NULL, size) is equivalent to malloc(size). */
@@ -1322,13 +1829,15 @@ je_realloc(void *ptr, size_t size)
        return (ret);
 }
 
-void
+JEMALLOC_EXPORT void JEMALLOC_NOTHROW
 je_free(void *ptr)
 {
 
        UTRACE(ptr, 0, 0);
-       if (likely(ptr != NULL))
-               ifree(tsd_fetch(), ptr, true);
+       if (likely(ptr != NULL)) {
+               tsd_t *tsd = tsd_fetch();
+               ifree(tsd, ptr, tcache_get(tsd, false));
+       }
 }
 
 /*
@@ -1340,22 +1849,28 @@ je_free(void *ptr)
  */
 
 #ifdef JEMALLOC_OVERRIDE_MEMALIGN
-void *
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+void JEMALLOC_NOTHROW *
+JEMALLOC_ATTR(malloc)
 je_memalign(size_t alignment, size_t size)
 {
        void *ret JEMALLOC_CC_SILENCE_INIT(NULL);
-       imemalign(&ret, alignment, size, 1);
+       if (unlikely(imemalign(&ret, alignment, size, 1) != 0))
+               ret = NULL;
        JEMALLOC_VALGRIND_MALLOC(ret != NULL, ret, size, false);
        return (ret);
 }
 #endif
 
 #ifdef JEMALLOC_OVERRIDE_VALLOC
-void *
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+void JEMALLOC_NOTHROW *
+JEMALLOC_ATTR(malloc)
 je_valloc(size_t size)
 {
        void *ret JEMALLOC_CC_SILENCE_INIT(NULL);
-       imemalign(&ret, PAGE, size, 1);
+       if (unlikely(imemalign(&ret, PAGE, size, 1) != 0))
+               ret = NULL;
        JEMALLOC_VALGRIND_MALLOC(ret != NULL, ret, size, false);
        return (ret);
 }
@@ -1396,9 +1911,9 @@ JEMALLOC_EXPORT void *(*__memalign_hook)(size_t alignment, size_t size) =
  * Begin non-standard functions.
  */
 
-JEMALLOC_ALWAYS_INLINE_C void
-imallocx_flags_decode_hard(size_t size, int flags, size_t *usize,
-    size_t *alignment, bool *zero, bool *try_tcache, arena_t **arena)
+JEMALLOC_ALWAYS_INLINE_C bool
+imallocx_flags_decode_hard(tsd_t *tsd, size_t size, int flags, size_t *usize,
+    size_t *alignment, bool *zero, tcache_t **tcache, arena_t **arena)
 {
 
        if ((flags & MALLOCX_LG_ALIGN_MASK) == 0) {
@@ -1408,76 +1923,72 @@ imallocx_flags_decode_hard(size_t size, int flags, size_t *usize,
                *alignment = MALLOCX_ALIGN_GET_SPECIFIED(flags);
                *usize = sa2u(size, *alignment);
        }
+       assert(*usize != 0);
        *zero = MALLOCX_ZERO_GET(flags);
+       if ((flags & MALLOCX_TCACHE_MASK) != 0) {
+               if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE)
+                       *tcache = NULL;
+               else
+                       *tcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));
+       } else
+               *tcache = tcache_get(tsd, true);
        if ((flags & MALLOCX_ARENA_MASK) != 0) {
                unsigned arena_ind = MALLOCX_ARENA_GET(flags);
-               *try_tcache = false;
-               *arena = arenas[arena_ind];
-       } else {
-               *try_tcache = true;
+               *arena = arena_get(tsd, arena_ind, true, true);
+               if (unlikely(*arena == NULL))
+                       return (true);
+       } else
                *arena = NULL;
-       }
+       return (false);
 }
 
-JEMALLOC_ALWAYS_INLINE_C void
-imallocx_flags_decode(size_t size, int flags, size_t *usize, size_t *alignment,
-    bool *zero, bool *try_tcache, arena_t **arena)
+JEMALLOC_ALWAYS_INLINE_C bool
+imallocx_flags_decode(tsd_t *tsd, size_t size, int flags, size_t *usize,
+    size_t *alignment, bool *zero, tcache_t **tcache, arena_t **arena)
 {
 
        if (likely(flags == 0)) {
                *usize = s2u(size);
-               assert(usize != 0);
+               assert(*usize != 0);
                *alignment = 0;
                *zero = false;
-               *try_tcache = true;
+               *tcache = tcache_get(tsd, true);
                *arena = NULL;
+               return (false);
        } else {
-               imallocx_flags_decode_hard(size, flags, usize, alignment, zero,
-                   try_tcache, arena);
+               return (imallocx_flags_decode_hard(tsd, size, flags, usize,
+                   alignment, zero, tcache, arena));
        }
 }
 
 JEMALLOC_ALWAYS_INLINE_C void *
 imallocx_flags(tsd_t *tsd, size_t usize, size_t alignment, bool zero,
-    bool try_tcache, arena_t *arena)
-{
-
-       if (alignment != 0) {
-               return (ipalloct(tsd, usize, alignment, zero, try_tcache,
-                   arena));
-       }
-       if (zero)
-               return (icalloct(tsd, usize, try_tcache, arena));
-       return (imalloct(tsd, usize, try_tcache, arena));
-}
-
-JEMALLOC_ALWAYS_INLINE_C void *
-imallocx_maybe_flags(tsd_t *tsd, size_t size, int flags, size_t usize,
-    size_t alignment, bool zero, bool try_tcache, arena_t *arena)
+    tcache_t *tcache, arena_t *arena)
 {
 
-       if (likely(flags == 0))
-               return (imalloc(tsd, size));
-       return (imallocx_flags(tsd, usize, alignment, zero, try_tcache, arena));
+       if (unlikely(alignment != 0))
+               return (ipalloct(tsd, usize, alignment, zero, tcache, arena));
+       if (unlikely(zero))
+               return (icalloct(tsd, usize, tcache, arena));
+       return (imalloct(tsd, usize, tcache, arena));
 }
 
 static void *
-imallocx_prof_sample(tsd_t *tsd, size_t size, int flags, size_t usize,
-    size_t alignment, bool zero, bool try_tcache, arena_t *arena)
+imallocx_prof_sample(tsd_t *tsd, size_t usize, size_t alignment, bool zero,
+    tcache_t *tcache, arena_t *arena)
 {
        void *p;
 
        if (usize <= SMALL_MAXCLASS) {
                assert(((alignment == 0) ? s2u(LARGE_MINCLASS) :
                    sa2u(LARGE_MINCLASS, alignment)) == LARGE_MINCLASS);
-               p = imalloct(tsd, LARGE_MINCLASS, try_tcache, arena);
+               p = imallocx_flags(tsd, LARGE_MINCLASS, alignment, zero, tcache,
+                   arena);
                if (p == NULL)
                        return (NULL);
                arena_prof_promoted(p, usize);
-       } else {
-               p = imallocx_maybe_flags(tsd, size, flags, usize, alignment,
-                   zero, try_tcache, arena);
-       }
+       } else
+               p = imallocx_flags(tsd, usize, alignment, zero, tcache, arena);
 
        return (p);
 }
@@ -1488,19 +1999,19 @@ imallocx_prof(tsd_t *tsd, size_t size, int flags, size_t *usize)
        void *p;
        size_t alignment;
        bool zero;
-       bool try_tcache;
+       tcache_t *tcache;
        arena_t *arena;
        prof_tctx_t *tctx;
 
-       imallocx_flags_decode(size, flags, usize, &alignment, &zero,
-           &try_tcache, &arena);
-       tctx = prof_alloc_prep(tsd, *usize, true);
-       if (likely((uintptr_t)tctx == (uintptr_t)1U)) {
-               p = imallocx_maybe_flags(tsd, size, flags, *usize, alignment,
-                   zero, try_tcache, arena);
-       else if ((uintptr_t)tctx > (uintptr_t)1U) {
-               p = imallocx_prof_sample(tsd, size, flags, *usize, alignment,
-                   zero, try_tcache, arena);
+       if (unlikely(imallocx_flags_decode(tsd, size, flags, usize, &alignment,
+           &zero, &tcache, &arena)))
+               return (NULL);
+       tctx = prof_alloc_prep(tsd, *usize, prof_active_get_unlocked(), true);
+       if (likely((uintptr_t)tctx == (uintptr_t)1U))
+               p = imallocx_flags(tsd, *usize, alignment, zero, tcache, arena);
+       else if ((uintptr_t)tctx > (uintptr_t)1U) {
+               p = imallocx_prof_sample(tsd, *usize, alignment, zero, tcache,
+                   arena);
        } else
                p = NULL;
        if (unlikely(p == NULL)) {
@@ -1509,15 +2020,17 @@ imallocx_prof(tsd_t *tsd, size_t size, int flags, size_t *usize)
        }
        prof_malloc(p, *usize, tctx);
 
+       assert(alignment == 0 || ((uintptr_t)p & (alignment - 1)) == ZU(0));
        return (p);
 }
 
 JEMALLOC_ALWAYS_INLINE_C void *
 imallocx_no_prof(tsd_t *tsd, size_t size, int flags, size_t *usize)
 {
+       void *p;
        size_t alignment;
        bool zero;
-       bool try_tcache;
+       tcache_t *tcache;
        arena_t *arena;
 
        if (likely(flags == 0)) {
@@ -1526,13 +2039,17 @@ imallocx_no_prof(tsd_t *tsd, size_t size, int flags, size_t *usize)
                return (imalloc(tsd, size));
        }
 
-       imallocx_flags_decode_hard(size, flags, usize, &alignment, &zero,
-           &try_tcache, &arena);
-       return (imallocx_flags(tsd, *usize, alignment, zero, try_tcache,
-           arena));
+       if (unlikely(imallocx_flags_decode_hard(tsd, size, flags, usize,
+           &alignment, &zero, &tcache, &arena)))
+               return (NULL);
+       p = imallocx_flags(tsd, *usize, alignment, zero, tcache, arena);
+       assert(alignment == 0 || ((uintptr_t)p & (alignment - 1)) == ZU(0));
+       return (p);
 }
 
-void *
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+void JEMALLOC_NOTHROW *
+JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)
 je_mallocx(size_t size, int flags)
 {
        tsd_t *tsd;
@@ -1569,51 +2086,53 @@ label_oom:
 }
 
 static void *
-irallocx_prof_sample(tsd_t *tsd, void *oldptr, size_t size, size_t alignment,
-    size_t usize, bool zero, bool try_tcache_alloc, bool try_tcache_dalloc,
-    arena_t *arena, prof_tctx_t *tctx)
+irallocx_prof_sample(tsd_t *tsd, void *old_ptr, size_t old_usize,
+    size_t usize, size_t alignment, bool zero, tcache_t *tcache, arena_t *arena,
+    prof_tctx_t *tctx)
 {
        void *p;
 
        if (tctx == NULL)
                return (NULL);
        if (usize <= SMALL_MAXCLASS) {
-               p = iralloct(tsd, oldptr, LARGE_MINCLASS, alignment, zero,
-                   try_tcache_alloc, try_tcache_dalloc, arena);
+               p = iralloct(tsd, old_ptr, old_usize, LARGE_MINCLASS, alignment,
+                   zero, tcache, arena);
                if (p == NULL)
                        return (NULL);
                arena_prof_promoted(p, usize);
        } else {
-               p = iralloct(tsd, oldptr, size, alignment, zero,
-                   try_tcache_alloc, try_tcache_dalloc, arena);
+               p = iralloct(tsd, old_ptr, old_usize, usize, alignment, zero,
+                   tcache, arena);
        }
 
        return (p);
 }
 
 JEMALLOC_ALWAYS_INLINE_C void *
-irallocx_prof(tsd_t *tsd, void *oldptr, size_t old_usize, size_t size,
-    size_t alignment, size_t *usize, bool zero, bool try_tcache_alloc,
-    bool try_tcache_dalloc, arena_t *arena)
+irallocx_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t size,
+    size_t alignment, size_t *usize, bool zero, tcache_t *tcache,
+    arena_t *arena)
 {
        void *p;
+       bool prof_active;
        prof_tctx_t *old_tctx, *tctx;
 
-       old_tctx = prof_tctx_get(oldptr);
-       tctx = prof_alloc_prep(tsd, *usize, false);
+       prof_active = prof_active_get_unlocked();
+       old_tctx = prof_tctx_get(old_ptr);
+       tctx = prof_alloc_prep(tsd, *usize, prof_active, true);
        if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
-               p = irallocx_prof_sample(tsd, oldptr, size, alignment, *usize,
-                   zero, try_tcache_alloc, try_tcache_dalloc, arena, tctx);
+               p = irallocx_prof_sample(tsd, old_ptr, old_usize, *usize,
+                   alignment, zero, tcache, arena, tctx);
        } else {
-               p = iralloct(tsd, oldptr, size, alignment, zero,
-                   try_tcache_alloc, try_tcache_dalloc, arena);
+               p = iralloct(tsd, old_ptr, old_usize, size, alignment, zero,
+                   tcache, arena);
        }
        if (unlikely(p == NULL)) {
-               prof_alloc_rollback(tsd, tctx, false);
+               prof_alloc_rollback(tsd, tctx, true);
                return (NULL);
        }
 
-       if (p == oldptr && alignment != 0) {
+       if (p == old_ptr && alignment != 0) {
                /*
                 * The allocation did not move, so it is possible that the size
                 * class is smaller than would guarantee the requested
@@ -1624,47 +2143,50 @@ irallocx_prof(tsd_t *tsd, void *oldptr, size_t old_usize, size_t size,
                 */
                *usize = isalloc(p, config_prof);
        }
-       prof_realloc(tsd, p, *usize, tctx, false, old_usize, old_tctx);
+       prof_realloc(tsd, p, *usize, tctx, prof_active, true, old_ptr,
+           old_usize, old_tctx);
 
        return (p);
 }
 
-void *
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+void JEMALLOC_NOTHROW *
+JEMALLOC_ALLOC_SIZE(2)
 je_rallocx(void *ptr, size_t size, int flags)
 {
        void *p;
        tsd_t *tsd;
        size_t usize;
-       UNUSED size_t old_usize JEMALLOC_CC_SILENCE_INIT(0);
+       size_t old_usize;
        UNUSED size_t old_rzsize JEMALLOC_CC_SILENCE_INIT(0);
        size_t alignment = MALLOCX_ALIGN_GET(flags);
        bool zero = flags & MALLOCX_ZERO;
-       bool try_tcache_alloc, try_tcache_dalloc;
        arena_t *arena;
+       tcache_t *tcache;
 
        assert(ptr != NULL);
        assert(size != 0);
-       assert(malloc_initialized || IS_INITIALIZER);
+       assert(malloc_initialized() || IS_INITIALIZER);
        malloc_thread_init();
        tsd = tsd_fetch();
 
        if (unlikely((flags & MALLOCX_ARENA_MASK) != 0)) {
                unsigned arena_ind = MALLOCX_ARENA_GET(flags);
-               arena_chunk_t *chunk;
-               try_tcache_alloc = false;
-               chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-               try_tcache_dalloc = (chunk == ptr || chunk->arena !=
-                   arenas[arena_ind]);
-               arena = arenas[arena_ind];
-       } else {
-               try_tcache_alloc = true;
-               try_tcache_dalloc = true;
+               arena = arena_get(tsd, arena_ind, true, true);
+               if (unlikely(arena == NULL))
+                       goto label_oom;
+       } else
                arena = NULL;
-       }
 
-       if ((config_prof && opt_prof) || config_stats ||
-           ((config_valgrind && unlikely(in_valgrind))))
-               old_usize = isalloc(ptr, config_prof);
+       if (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) {
+               if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE)
+                       tcache = NULL;
+               else
+                       tcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));
+       } else
+               tcache = tcache_get(tsd, true);
+
+       old_usize = isalloc(ptr, config_prof);
        if (config_valgrind && unlikely(in_valgrind))
                old_rzsize = u2rz(old_usize);
 
@@ -1672,17 +2194,18 @@ je_rallocx(void *ptr, size_t size, int flags)
                usize = (alignment == 0) ? s2u(size) : sa2u(size, alignment);
                assert(usize != 0);
                p = irallocx_prof(tsd, ptr, old_usize, size, alignment, &usize,
-                   zero, try_tcache_alloc, try_tcache_dalloc, arena);
+                   zero, tcache, arena);
                if (unlikely(p == NULL))
                        goto label_oom;
        } else {
-               p = iralloct(tsd, ptr, size, alignment, zero, try_tcache_alloc,
-                   try_tcache_dalloc, arena);
+               p = iralloct(tsd, ptr, old_usize, size, alignment, zero,
+                    tcache, arena);
                if (unlikely(p == NULL))
                        goto label_oom;
                if (config_stats || (config_valgrind && unlikely(in_valgrind)))
                        usize = isalloc(p, config_prof);
        }
+       assert(alignment == 0 || ((uintptr_t)p & (alignment - 1)) == ZU(0));
 
        if (config_stats) {
                *tsd_thread_allocatedp_get(tsd) += usize;
@@ -1703,11 +2226,11 @@ label_oom:
 
 JEMALLOC_ALWAYS_INLINE_C size_t
 ixallocx_helper(void *ptr, size_t old_usize, size_t size, size_t extra,
-    size_t alignment, bool zero, arena_t *arena)
+    size_t alignment, bool zero)
 {
        size_t usize;
 
-       if (ixalloc(ptr, size, extra, alignment, zero))
+       if (ixalloc(ptr, old_usize, size, extra, alignment, zero))
                return (old_usize);
        usize = isalloc(ptr, config_prof);
 
@@ -1716,38 +2239,26 @@ ixallocx_helper(void *ptr, size_t old_usize, size_t size, size_t extra,
 
 static size_t
 ixallocx_prof_sample(void *ptr, size_t old_usize, size_t size, size_t extra,
-    size_t alignment, size_t max_usize, bool zero, arena_t *arena,
-    prof_tctx_t *tctx)
+    size_t alignment, bool zero, prof_tctx_t *tctx)
 {
        size_t usize;
 
        if (tctx == NULL)
                return (old_usize);
-       /* Use minimum usize to determine whether promotion may happen. */
-       if (((alignment == 0) ? s2u(size) : sa2u(size, alignment)) <=
-           SMALL_MAXCLASS) {
-               if (ixalloc(ptr, SMALL_MAXCLASS+1, (SMALL_MAXCLASS+1 >=
-                   size+extra) ? 0 : size+extra - (SMALL_MAXCLASS+1),
-                   alignment, zero))
-                       return (old_usize);
-               usize = isalloc(ptr, config_prof);
-               if (max_usize < PAGE)
-                       arena_prof_promoted(ptr, usize);
-       } else {
-               usize = ixallocx_helper(ptr, old_usize, size, extra, alignment,
-                   zero, arena);
-       }
+       usize = ixallocx_helper(ptr, old_usize, size, extra, alignment, zero);
 
        return (usize);
 }
 
 JEMALLOC_ALWAYS_INLINE_C size_t
 ixallocx_prof(tsd_t *tsd, void *ptr, size_t old_usize, size_t size,
-    size_t extra, size_t alignment, bool zero, arena_t *arena)
+    size_t extra, size_t alignment, bool zero)
 {
-       size_t max_usize, usize;
+       size_t usize_max, usize;
+       bool prof_active;
        prof_tctx_t *old_tctx, *tctx;
 
+       prof_active = prof_active_get_unlocked();
        old_tctx = prof_tctx_get(ptr);
        /*
         * usize isn't knowable before ixalloc() returns when extra is non-zero.
@@ -1755,26 +2266,28 @@ ixallocx_prof(tsd_t *tsd, void *ptr, size_t old_usize, size_t size,
         * prof_alloc_prep() to decide whether to capture a backtrace.
         * prof_realloc() will use the actual usize to decide whether to sample.
         */
-       max_usize = (alignment == 0) ? s2u(size+extra) : sa2u(size+extra,
+       usize_max = (alignment == 0) ? s2u(size+extra) : sa2u(size+extra,
            alignment);
-       tctx = prof_alloc_prep(tsd, max_usize, false);
+       assert(usize_max != 0);
+       tctx = prof_alloc_prep(tsd, usize_max, prof_active, false);
        if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
                usize = ixallocx_prof_sample(ptr, old_usize, size, extra,
-                   alignment, zero, max_usize, arena, tctx);
+                   alignment, zero, tctx);
        } else {
                usize = ixallocx_helper(ptr, old_usize, size, extra, alignment,
-                   zero, arena);
+                   zero);
        }
-       if (unlikely(usize == old_usize)) {
+       if (usize == old_usize) {
                prof_alloc_rollback(tsd, tctx, false);
                return (usize);
        }
-       prof_realloc(tsd, ptr, usize, tctx, false, old_usize, old_tctx);
+       prof_realloc(tsd, ptr, usize, tctx, prof_active, false, ptr, old_usize,
+           old_tctx);
 
        return (usize);
 }
 
-size_t
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
 je_xallocx(void *ptr, size_t size, size_t extra, int flags)
 {
        tsd_t *tsd;
@@ -1782,31 +2295,35 @@ je_xallocx(void *ptr, size_t size, size_t extra, int flags)
        UNUSED size_t old_rzsize JEMALLOC_CC_SILENCE_INIT(0);
        size_t alignment = MALLOCX_ALIGN_GET(flags);
        bool zero = flags & MALLOCX_ZERO;
-       arena_t *arena;
 
        assert(ptr != NULL);
        assert(size != 0);
        assert(SIZE_T_MAX - size >= extra);
-       assert(malloc_initialized || IS_INITIALIZER);
+       assert(malloc_initialized() || IS_INITIALIZER);
        malloc_thread_init();
        tsd = tsd_fetch();
 
-       if (unlikely((flags & MALLOCX_ARENA_MASK) != 0)) {
-               unsigned arena_ind = MALLOCX_ARENA_GET(flags);
-               arena = arenas[arena_ind];
-       } else
-               arena = NULL;
-
        old_usize = isalloc(ptr, config_prof);
+
+       /* Clamp extra if necessary to avoid (size + extra) overflow. */
+       if (unlikely(size + extra > HUGE_MAXCLASS)) {
+               /* Check for size overflow. */
+               if (unlikely(size > HUGE_MAXCLASS)) {
+                       usize = old_usize;
+                       goto label_not_resized;
+               }
+               extra = HUGE_MAXCLASS - size;
+       }
+
        if (config_valgrind && unlikely(in_valgrind))
                old_rzsize = u2rz(old_usize);
 
        if (config_prof && opt_prof) {
                usize = ixallocx_prof(tsd, ptr, old_usize, size, extra,
-                   alignment, zero, arena);
+                   alignment, zero);
        } else {
                usize = ixallocx_helper(ptr, old_usize, size, extra, alignment,
-                   zero, arena);
+                   zero);
        }
        if (unlikely(usize == old_usize))
                goto label_not_resized;
@@ -1822,42 +2339,43 @@ label_not_resized:
        return (usize);
 }
 
-size_t
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
+JEMALLOC_ATTR(pure)
 je_sallocx(const void *ptr, int flags)
 {
        size_t usize;
 
-       assert(malloc_initialized || IS_INITIALIZER);
+       assert(malloc_initialized() || IS_INITIALIZER);
        malloc_thread_init();
 
        if (config_ivsalloc)
                usize = ivsalloc(ptr, config_prof);
-       else {
-               assert(ptr != NULL);
+       else
                usize = isalloc(ptr, config_prof);
-       }
 
        return (usize);
 }
 
-void
+JEMALLOC_EXPORT void JEMALLOC_NOTHROW
 je_dallocx(void *ptr, int flags)
 {
-       bool try_tcache;
+       tsd_t *tsd;
+       tcache_t *tcache;
 
        assert(ptr != NULL);
-       assert(malloc_initialized || IS_INITIALIZER);
+       assert(malloc_initialized() || IS_INITIALIZER);
 
-       if (unlikely((flags & MALLOCX_ARENA_MASK) != 0)) {
-               unsigned arena_ind = MALLOCX_ARENA_GET(flags);
-               arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-               try_tcache = (chunk == ptr || chunk->arena !=
-                   arenas[arena_ind]);
+       tsd = tsd_fetch();
+       if (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) {
+               if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE)
+                       tcache = NULL;
+               else
+                       tcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));
        } else
-               try_tcache = true;
+               tcache = tcache_get(tsd, false);
 
        UTRACE(ptr, 0, 0);
-       ifree(tsd_fetch(), ptr, try_tcache);
+       ifree(tsd_fetch(), ptr, tcache);
 }
 
 JEMALLOC_ALWAYS_INLINE_C size_t
@@ -1873,30 +2391,33 @@ inallocx(size_t size, int flags)
        return (usize);
 }
 
-void
+JEMALLOC_EXPORT void JEMALLOC_NOTHROW
 je_sdallocx(void *ptr, size_t size, int flags)
 {
-       bool try_tcache;
+       tsd_t *tsd;
+       tcache_t *tcache;
        size_t usize;
 
        assert(ptr != NULL);
-       assert(malloc_initialized || IS_INITIALIZER);
+       assert(malloc_initialized() || IS_INITIALIZER);
        usize = inallocx(size, flags);
        assert(usize == isalloc(ptr, config_prof));
 
-       if (unlikely((flags & MALLOCX_ARENA_MASK) != 0)) {
-               unsigned arena_ind = MALLOCX_ARENA_GET(flags);
-               arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-               try_tcache = (chunk == ptr || chunk->arena !=
-                   arenas[arena_ind]);
+       tsd = tsd_fetch();
+       if (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) {
+               if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE)
+                       tcache = NULL;
+               else
+                       tcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));
        } else
-               try_tcache = true;
+               tcache = tcache_get(tsd, false);
 
        UTRACE(ptr, 0, 0);
-       isfree(tsd_fetch(), ptr, usize, try_tcache);
+       isfree(tsd, ptr, usize, tcache);
 }
 
-size_t
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
+JEMALLOC_ATTR(pure)
 je_nallocx(size_t size, int flags)
 {
 
@@ -1908,7 +2429,7 @@ je_nallocx(size_t size, int flags)
        return (inallocx(size, flags));
 }
 
-int
+JEMALLOC_EXPORT int JEMALLOC_NOTHROW
 je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp,
     size_t newlen)
 {
@@ -1919,7 +2440,7 @@ je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp,
        return (ctl_byname(name, oldp, oldlenp, newp, newlen));
 }
 
-int
+JEMALLOC_EXPORT int JEMALLOC_NOTHROW
 je_mallctlnametomib(const char *name, size_t *mibp, size_t *miblenp)
 {
 
@@ -1929,7 +2450,7 @@ je_mallctlnametomib(const char *name, size_t *mibp, size_t *miblenp)
        return (ctl_nametomib(name, mibp, miblenp));
 }
 
-int
+JEMALLOC_EXPORT int JEMALLOC_NOTHROW
 je_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
   void *newp, size_t newlen)
 {
@@ -1940,7 +2461,7 @@ je_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
        return (ctl_bymib(mib, miblen, oldp, oldlenp, newp, newlen));
 }
 
-void
+JEMALLOC_EXPORT void JEMALLOC_NOTHROW
 je_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
     const char *opts)
 {
@@ -1948,18 +2469,18 @@ je_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
        stats_print(write_cb, cbopaque, opts);
 }
 
-size_t
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
 je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)
 {
        size_t ret;
 
-       assert(malloc_initialized || IS_INITIALIZER);
+       assert(malloc_initialized() || IS_INITIALIZER);
        malloc_thread_init();
 
        if (config_ivsalloc)
                ret = ivsalloc(ptr, config_prof);
        else
-               ret = (ptr != NULL) ? isalloc(ptr, config_prof) : 0;
+               ret = (ptr == NULL) ? 0 : isalloc(ptr, config_prof);
 
        return (ret);
 }
@@ -1982,9 +2503,9 @@ je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)
  * fork/malloc races via the following functions it registers during
  * initialization using pthread_atfork(), but of course that does no good if
  * the allocator isn't fully initialized at fork time.  The following library
- * constructor is a partial solution to this problem.  It may still possible to
- * trigger the deadlock described above, but doing so would involve forking via
- * a library constructor that runs before jemalloc's runs.
+ * constructor is a partial solution to this problem.  It may still be possible
+ * to trigger the deadlock described above, but doing so would involve forking
+ * via a library constructor that runs before jemalloc's runs.
  */
 JEMALLOC_ATTR(constructor)
 static void
@@ -2005,10 +2526,10 @@ _malloc_prefork(void)
        unsigned i;
 
 #ifdef JEMALLOC_MUTEX_INIT_CB
-       if (!malloc_initialized)
+       if (!malloc_initialized())
                return;
 #endif
-       assert(malloc_initialized);
+       assert(malloc_initialized());
 
        /* Acquire all mutexes in a safe order. */
        ctl_prefork();
@@ -2020,7 +2541,6 @@ _malloc_prefork(void)
        }
        chunk_prefork();
        base_prefork();
-       huge_prefork();
 }
 
 #ifndef JEMALLOC_MUTEX_INIT_CB
@@ -2034,13 +2554,12 @@ _malloc_postfork(void)
        unsigned i;
 
 #ifdef JEMALLOC_MUTEX_INIT_CB
-       if (!malloc_initialized)
+       if (!malloc_initialized())
                return;
 #endif
-       assert(malloc_initialized);
+       assert(malloc_initialized());
 
        /* Release all mutexes, now that fork() has completed. */
-       huge_postfork_parent();
        base_postfork_parent();
        chunk_postfork_parent();
        for (i = 0; i < narenas_total; i++) {
@@ -2057,10 +2576,9 @@ jemalloc_postfork_child(void)
 {
        unsigned i;
 
-       assert(malloc_initialized);
+       assert(malloc_initialized());
 
        /* Release all mutexes, now that fork() has completed. */
-       huge_postfork_child();
        base_postfork_child();
        chunk_postfork_child();
        for (i = 0; i < narenas_total; i++) {
@@ -2073,55 +2591,3 @@ jemalloc_postfork_child(void)
 }
 
 /******************************************************************************/
-/*
- * The following functions are used for TLS allocation/deallocation in static
- * binaries on FreeBSD.  The primary difference between these and i[mcd]alloc()
- * is that these avoid accessing TLS variables.
- */
-
-static void *
-a0alloc(size_t size, bool zero)
-{
-
-       if (unlikely(malloc_init()))
-               return (NULL);
-
-       if (size == 0)
-               size = 1;
-
-       if (size <= arena_maxclass)
-               return (arena_malloc(NULL, arenas[0], size, zero, false));
-       else
-               return (huge_malloc(NULL, arenas[0], size, zero));
-}
-
-void *
-a0malloc(size_t size)
-{
-
-       return (a0alloc(size, false));
-}
-
-void *
-a0calloc(size_t num, size_t size)
-{
-
-       return (a0alloc(num * size, true));
-}
-
-void
-a0free(void *ptr)
-{
-       arena_chunk_t *chunk;
-
-       if (ptr == NULL)
-               return;
-
-       chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-       if (chunk != ptr)
-               arena_dalloc(NULL, chunk, ptr, false);
-       else
-               huge_dalloc(ptr);
-}
-
-/******************************************************************************/
index 788eca387032fcd55e81caa56846bf290800f256..2d47af976c58b9208f0356e329150c6c0f09a7fa 100644 (file)
@@ -73,9 +73,13 @@ malloc_mutex_init(malloc_mutex_t *mutex)
 {
 
 #ifdef _WIN32
+#  if _WIN32_WINNT >= 0x0600
+       InitializeSRWLock(&mutex->lock);
+#  else
        if (!InitializeCriticalSectionAndSpinCount(&mutex->lock,
            _CRT_SPINCOUNT))
                return (true);
+#  endif
 #elif (defined(JEMALLOC_OSSPIN))
        mutex->lock = 0;
 #elif (defined(JEMALLOC_MUTEX_INIT_CB))
@@ -83,8 +87,8 @@ malloc_mutex_init(malloc_mutex_t *mutex)
                mutex->postponed_next = postponed_mutexes;
                postponed_mutexes = mutex;
        } else {
-               if (_pthread_mutex_init_calloc_cb(&mutex->lock, base_calloc) !=
-                   0)
+               if (_pthread_mutex_init_calloc_cb(&mutex->lock,
+                   bootstrap_calloc) != 0)
                        return (true);
        }
 #else
@@ -140,7 +144,7 @@ mutex_boot(void)
        postpone_init = false;
        while (postponed_mutexes != NULL) {
                if (_pthread_mutex_init_calloc_cb(&postponed_mutexes->lock,
-                   base_calloc) != 0)
+                   bootstrap_calloc) != 0)
                        return (true);
                postponed_mutexes = postponed_mutexes->postponed_next;
        }
diff --git a/src/jemalloc/src/pages.c b/src/jemalloc/src/pages.c
new file mode 100644 (file)
index 0000000..83a167f
--- /dev/null
@@ -0,0 +1,173 @@
+#define        JEMALLOC_PAGES_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+
+/******************************************************************************/
+
+void *
+pages_map(void *addr, size_t size)
+{
+       void *ret;
+
+       assert(size != 0);
+
+#ifdef _WIN32
+       /*
+        * If VirtualAlloc can't allocate at the given address when one is
+        * given, it fails and returns NULL.
+        */
+       ret = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE,
+           PAGE_READWRITE);
+#else
+       /*
+        * We don't use MAP_FIXED here, because it can cause the *replacement*
+        * of existing mappings, and we only want to create new mappings.
+        */
+       ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
+           -1, 0);
+       assert(ret != NULL);
+
+       if (ret == MAP_FAILED)
+               ret = NULL;
+       else if (addr != NULL && ret != addr) {
+               /*
+                * We succeeded in mapping memory, but not in the right place.
+                */
+               pages_unmap(ret, size);
+               ret = NULL;
+       }
+#endif
+       assert(ret == NULL || (addr == NULL && ret != addr)
+           || (addr != NULL && ret == addr));
+       return (ret);
+}
+
+void
+pages_unmap(void *addr, size_t size)
+{
+
+#ifdef _WIN32
+       if (VirtualFree(addr, 0, MEM_RELEASE) == 0)
+#else
+       if (munmap(addr, size) == -1)
+#endif
+       {
+               char buf[BUFERROR_BUF];
+
+               buferror(get_errno(), buf, sizeof(buf));
+               malloc_printf("<jemalloc>: Error in "
+#ifdef _WIN32
+                             "VirtualFree"
+#else
+                             "munmap"
+#endif
+                             "(): %s\n", buf);
+               if (opt_abort)
+                       abort();
+       }
+}
+
+void *
+pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size)
+{
+       void *ret = (void *)((uintptr_t)addr + leadsize);
+
+       assert(alloc_size >= leadsize + size);
+#ifdef _WIN32
+       {
+               void *new_addr;
+
+               pages_unmap(addr, alloc_size);
+               new_addr = pages_map(ret, size);
+               if (new_addr == ret)
+                       return (ret);
+               if (new_addr)
+                       pages_unmap(new_addr, size);
+               return (NULL);
+       }
+#else
+       {
+               size_t trailsize = alloc_size - leadsize - size;
+
+               if (leadsize != 0)
+                       pages_unmap(addr, leadsize);
+               if (trailsize != 0)
+                       pages_unmap((void *)((uintptr_t)ret + size), trailsize);
+               return (ret);
+       }
+#endif
+}
+
+static bool
+pages_commit_impl(void *addr, size_t size, bool commit)
+{
+
+#ifndef _WIN32
+       /*
+        * The following decommit/commit implementation is functional, but
+        * always disabled because it doesn't add value beyong improved
+        * debugging (at the cost of extra system calls) on systems that
+        * overcommit.
+        */
+       if (false) {
+               int prot = commit ? (PROT_READ | PROT_WRITE) : PROT_NONE;
+               void *result = mmap(addr, size, prot, MAP_PRIVATE | MAP_ANON |
+                   MAP_FIXED, -1, 0);
+               if (result == MAP_FAILED)
+                       return (true);
+               if (result != addr) {
+                       /*
+                        * We succeeded in mapping memory, but not in the right
+                        * place.
+                        */
+                       pages_unmap(result, size);
+                       return (true);
+               }
+               return (false);
+       }
+#endif
+       return (true);
+}
+
+bool
+pages_commit(void *addr, size_t size)
+{
+
+       return (pages_commit_impl(addr, size, true));
+}
+
+bool
+pages_decommit(void *addr, size_t size)
+{
+
+       return (pages_commit_impl(addr, size, false));
+}
+
+bool
+pages_purge(void *addr, size_t size)
+{
+       bool unzeroed;
+
+#ifdef _WIN32
+       VirtualAlloc(addr, size, MEM_RESET, PAGE_READWRITE);
+       unzeroed = true;
+#elif defined(JEMALLOC_HAVE_MADVISE)
+#  ifdef JEMALLOC_PURGE_MADVISE_DONTNEED
+#    define JEMALLOC_MADV_PURGE MADV_DONTNEED
+#    define JEMALLOC_MADV_ZEROS true
+#  elif defined(JEMALLOC_PURGE_MADVISE_FREE)
+#    define JEMALLOC_MADV_PURGE MADV_FREE
+#    define JEMALLOC_MADV_ZEROS false
+#  else
+#    error "No madvise(2) flag defined for purging unused dirty pages."
+#  endif
+       int err = madvise(addr, size, JEMALLOC_MADV_PURGE);
+       unzeroed = (!JEMALLOC_MADV_ZEROS || err != 0);
+#  undef JEMALLOC_MADV_PURGE
+#  undef JEMALLOC_MADV_ZEROS
+#else
+       /* Last resort no-op. */
+       unzeroed = true;
+#endif
+       return (unzeroed);
+}
+
index a6cea92fd833ee20e43545b558500da3d55b522c..5d2b9598fdb4f9b410f168cfdf9130d38c78c80a 100644 (file)
@@ -20,7 +20,7 @@ bool          opt_prof_thread_active_init = true;
 size_t         opt_lg_prof_sample = LG_PROF_SAMPLE_DEFAULT;
 ssize_t                opt_lg_prof_interval = LG_PROF_INTERVAL_DEFAULT;
 bool           opt_prof_gdump = false;
-bool           opt_prof_final = true;
+bool           opt_prof_final = false;
 bool           opt_prof_leak = false;
 bool           opt_prof_accum = false;
 char           opt_prof_prefix[
@@ -44,6 +44,13 @@ static malloc_mutex_t        prof_active_mtx;
 static bool            prof_thread_active_init;
 static malloc_mutex_t  prof_thread_active_init_mtx;
 
+/*
+ * Initialized as opt_prof_gdump, and accessed via
+ * prof_gdump_[gs]et{_unlocked,}().
+ */
+bool                   prof_gdump_val;
+static malloc_mutex_t  prof_gdump_mtx;
+
 uint64_t       prof_interval = 0;
 
 size_t         lg_prof_sample;
@@ -128,10 +135,22 @@ static char       *prof_thread_name_alloc(tsd_t *tsd, const char *thread_name);
 JEMALLOC_INLINE_C int
 prof_tctx_comp(const prof_tctx_t *a, const prof_tctx_t *b)
 {
-       uint64_t a_uid = a->tdata->thr_uid;
-       uint64_t b_uid = b->tdata->thr_uid;
-
-       return ((a_uid > b_uid) - (a_uid < b_uid));
+       uint64_t a_thr_uid = a->thr_uid;
+       uint64_t b_thr_uid = b->thr_uid;
+       int ret = (a_thr_uid > b_thr_uid) - (a_thr_uid < b_thr_uid);
+       if (ret == 0) {
+               uint64_t a_thr_discrim = a->thr_discrim;
+               uint64_t b_thr_discrim = b->thr_discrim;
+               ret = (a_thr_discrim > b_thr_discrim) - (a_thr_discrim <
+                   b_thr_discrim);
+               if (ret == 0) {
+                       uint64_t a_tctx_uid = a->tctx_uid;
+                       uint64_t b_tctx_uid = b->tctx_uid;
+                       ret = (a_tctx_uid > b_tctx_uid) - (a_tctx_uid <
+                           b_tctx_uid);
+               }
+       }
+       return (ret);
 }
 
 rb_gen(static UNUSED, tctx_tree_, prof_tctx_tree_t, prof_tctx_t,
@@ -190,7 +209,7 @@ prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated)
                 */
                tdata = prof_tdata_get(tsd, true);
                if (tdata != NULL)
-                       prof_sample_threshold_update(tctx->tdata);
+                       prof_sample_threshold_update(tdata);
        }
 
        if ((uintptr_t)tctx > (uintptr_t)1U) {
@@ -204,8 +223,10 @@ prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated)
 }
 
 void
-prof_malloc_sample_object(const void *ptr, size_t usize, prof_tctx_t *tctx) {
-       prof_tctx_set(ptr, tctx);
+prof_malloc_sample_object(const void *ptr, size_t usize, prof_tctx_t *tctx)
+{
+
+       prof_tctx_set(ptr, usize, tctx);
 
        malloc_mutex_lock(tctx->tdata->lock);
        tctx->cnts.curobjs++;
@@ -244,38 +265,45 @@ bt_init(prof_bt_t *bt, void **vec)
        bt->len = 0;
 }
 
-static inline void
-prof_enter(prof_tdata_t *tdata)
+JEMALLOC_INLINE_C void
+prof_enter(tsd_t *tsd, prof_tdata_t *tdata)
 {
 
        cassert(config_prof);
+       assert(tdata == prof_tdata_get(tsd, false));
 
-       assert(!tdata->enq);
-       tdata->enq = true;
+       if (tdata != NULL) {
+               assert(!tdata->enq);
+               tdata->enq = true;
+       }
 
        malloc_mutex_lock(&bt2gctx_mtx);
 }
 
-static inline void
-prof_leave(prof_tdata_t *tdata)
+JEMALLOC_INLINE_C void
+prof_leave(tsd_t *tsd, prof_tdata_t *tdata)
 {
-       bool idump, gdump;
 
        cassert(config_prof);
+       assert(tdata == prof_tdata_get(tsd, false));
 
        malloc_mutex_unlock(&bt2gctx_mtx);
 
-       assert(tdata->enq);
-       tdata->enq = false;
-       idump = tdata->enq_idump;
-       tdata->enq_idump = false;
-       gdump = tdata->enq_gdump;
-       tdata->enq_gdump = false;
+       if (tdata != NULL) {
+               bool idump, gdump;
 
-       if (idump)
-               prof_idump();
-       if (gdump)
-               prof_gdump();
+               assert(tdata->enq);
+               tdata->enq = false;
+               idump = tdata->enq_idump;
+               tdata->enq_idump = false;
+               gdump = tdata->enq_gdump;
+               tdata->enq_gdump = false;
+
+               if (idump)
+                       prof_idump();
+               if (gdump)
+                       prof_gdump();
+       }
 }
 
 #ifdef JEMALLOC_PROF_LIBUNWIND
@@ -523,8 +551,9 @@ prof_gctx_create(tsd_t *tsd, prof_bt_t *bt)
        /*
         * Create a single allocation that has space for vec of length bt->len.
         */
-       prof_gctx_t *gctx = (prof_gctx_t *)imalloc(tsd, offsetof(prof_gctx_t,
-           vec) + (bt->len * sizeof(void *)));
+       prof_gctx_t *gctx = (prof_gctx_t *)iallocztm(tsd, offsetof(prof_gctx_t,
+           vec) + (bt->len * sizeof(void *)), false, tcache_get(tsd, true),
+           true, NULL);
        if (gctx == NULL)
                return (NULL);
        gctx->lock = prof_gctx_mutex_choose();
@@ -542,7 +571,8 @@ prof_gctx_create(tsd_t *tsd, prof_bt_t *bt)
 }
 
 static void
-prof_gctx_try_destroy(tsd_t *tsd, prof_gctx_t *gctx, prof_tdata_t *tdata)
+prof_gctx_try_destroy(tsd_t *tsd, prof_tdata_t *tdata_self, prof_gctx_t *gctx,
+    prof_tdata_t *tdata)
 {
 
        cassert(config_prof);
@@ -554,17 +584,17 @@ prof_gctx_try_destroy(tsd_t *tsd, prof_gctx_t *gctx, prof_tdata_t *tdata)
         * avoid a race between the main body of prof_tctx_destroy() and entry
         * into this function.
         */
-       prof_enter(tdata);
+       prof_enter(tsd, tdata_self);
        malloc_mutex_lock(gctx->lock);
        assert(gctx->nlimbo != 0);
        if (tctx_tree_empty(&gctx->tctxs) && gctx->nlimbo == 1) {
                /* Remove gctx from bt2gctx. */
                if (ckh_remove(tsd, &bt2gctx, &gctx->bt, NULL, NULL))
                        not_reached();
-               prof_leave(tdata);
+               prof_leave(tsd, tdata_self);
                /* Destroy gctx. */
                malloc_mutex_unlock(gctx->lock);
-               idalloc(tsd, gctx);
+               idalloctm(tsd, gctx, tcache_get(tsd, false), true);
        } else {
                /*
                 * Compensate for increment in prof_tctx_destroy() or
@@ -572,7 +602,7 @@ prof_gctx_try_destroy(tsd_t *tsd, prof_gctx_t *gctx, prof_tdata_t *tdata)
                 */
                gctx->nlimbo--;
                malloc_mutex_unlock(gctx->lock);
-               prof_leave(tdata);
+               prof_leave(tsd, tdata_self);
        }
 }
 
@@ -609,7 +639,7 @@ prof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx)
 {
        prof_tdata_t *tdata = tctx->tdata;
        prof_gctx_t *gctx = tctx->gctx;
-       bool destroy_tdata, destroy_gctx;
+       bool destroy_tdata, destroy_tctx, destroy_gctx;
 
        assert(tctx->cnts.curobjs == 0);
        assert(tctx->cnts.curbytes == 0);
@@ -622,33 +652,56 @@ prof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx)
        malloc_mutex_unlock(tdata->lock);
 
        malloc_mutex_lock(gctx->lock);
-       tctx_tree_remove(&gctx->tctxs, tctx);
-       if (prof_gctx_should_destroy(gctx)) {
+       switch (tctx->state) {
+       case prof_tctx_state_nominal:
+               tctx_tree_remove(&gctx->tctxs, tctx);
+               destroy_tctx = true;
+               if (prof_gctx_should_destroy(gctx)) {
+                       /*
+                        * Increment gctx->nlimbo in order to keep another
+                        * thread from winning the race to destroy gctx while
+                        * this one has gctx->lock dropped.  Without this, it
+                        * would be possible for another thread to:
+                        *
+                        * 1) Sample an allocation associated with gctx.
+                        * 2) Deallocate the sampled object.
+                        * 3) Successfully prof_gctx_try_destroy(gctx).
+                        *
+                        * The result would be that gctx no longer exists by the
+                        * time this thread accesses it in
+                        * prof_gctx_try_destroy().
+                        */
+                       gctx->nlimbo++;
+                       destroy_gctx = true;
+               } else
+                       destroy_gctx = false;
+               break;
+       case prof_tctx_state_dumping:
                /*
-                * Increment gctx->nlimbo in order to keep another thread from
-                * winning the race to destroy gctx while this one has
-                * gctx->lock dropped.  Without this, it would be possible for
-                * another thread to:
-                *
-                * 1) Sample an allocation associated with gctx.
-                * 2) Deallocate the sampled object.
-                * 3) Successfully prof_gctx_try_destroy(gctx).
-                *
-                * The result would be that gctx no longer exists by the time
-                * this thread accesses it in prof_gctx_try_destroy().
+                * A dumping thread needs tctx to remain valid until dumping
+                * has finished.  Change state such that the dumping thread will
+                * complete destruction during a late dump iteration phase.
                 */
-               gctx->nlimbo++;
-               destroy_gctx = true;
-       } else
+               tctx->state = prof_tctx_state_purgatory;
+               destroy_tctx = false;
+               destroy_gctx = false;
+               break;
+       default:
+               not_reached();
+               destroy_tctx = false;
                destroy_gctx = false;
+       }
        malloc_mutex_unlock(gctx->lock);
-       if (destroy_gctx)
-               prof_gctx_try_destroy(tsd, gctx, tdata);
+       if (destroy_gctx) {
+               prof_gctx_try_destroy(tsd, prof_tdata_get(tsd, false), gctx,
+                   tdata);
+       }
 
        if (destroy_tdata)
                prof_tdata_destroy(tsd, tdata, false);
 
-       idalloc(tsd, tctx);
+       if (destroy_tctx)
+               idalloctm(tsd, tctx, tcache_get(tsd, false), true);
 }
 
 static bool
@@ -665,19 +718,19 @@ prof_lookup_global(tsd_t *tsd, prof_bt_t *bt, prof_tdata_t *tdata,
        } btkey;
        bool new_gctx;
 
-       prof_enter(tdata);
+       prof_enter(tsd, tdata);
        if (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {
                /* bt has never been seen before.  Insert it. */
                gctx.p = prof_gctx_create(tsd, bt);
                if (gctx.v == NULL) {
-                       prof_leave(tdata);
+                       prof_leave(tsd, tdata);
                        return (true);
                }
                btkey.p = &gctx.p->bt;
                if (ckh_insert(tsd, &bt2gctx, btkey.v, gctx.v)) {
                        /* OOM. */
-                       prof_leave(tdata);
-                       idalloc(tsd, gctx.v);
+                       prof_leave(tsd, tdata);
+                       idalloctm(tsd, gctx.v, tcache_get(tsd, false), true);
                        return (true);
                }
                new_gctx = true;
@@ -691,7 +744,7 @@ prof_lookup_global(tsd_t *tsd, prof_bt_t *bt, prof_tdata_t *tdata,
                malloc_mutex_unlock(gctx.p->lock);
                new_gctx = false;
        }
-       prof_leave(tdata);
+       prof_leave(tsd, tdata);
 
        *p_btkey = btkey.v;
        *p_gctx = gctx.p;
@@ -721,6 +774,7 @@ prof_lookup(tsd_t *tsd, prof_bt_t *bt)
                ret.p->prepared = true;
        malloc_mutex_unlock(tdata->lock);
        if (not_found) {
+               tcache_t *tcache;
                void *btkey;
                prof_gctx_t *gctx;
                bool new_gctx, error;
@@ -734,15 +788,20 @@ prof_lookup(tsd_t *tsd, prof_bt_t *bt)
                        return (NULL);
 
                /* Link a prof_tctx_t into gctx for this thread. */
-               ret.v = imalloc(tsd, sizeof(prof_tctx_t));
+               tcache = tcache_get(tsd, true);
+               ret.v = iallocztm(tsd, sizeof(prof_tctx_t), false, tcache, true,
+                   NULL);
                if (ret.p == NULL) {
                        if (new_gctx)
-                               prof_gctx_try_destroy(tsd, gctx, tdata);
+                               prof_gctx_try_destroy(tsd, tdata, gctx, tdata);
                        return (NULL);
                }
                ret.p->tdata = tdata;
+               ret.p->thr_uid = tdata->thr_uid;
+               ret.p->thr_discrim = tdata->thr_discrim;
                memset(&ret.p->cnts, 0, sizeof(prof_cnt_t));
                ret.p->gctx = gctx;
+               ret.p->tctx_uid = tdata->tctx_uid_next++;
                ret.p->prepared = true;
                ret.p->state = prof_tctx_state_initializing;
                malloc_mutex_lock(tdata->lock);
@@ -750,8 +809,8 @@ prof_lookup(tsd_t *tsd, prof_bt_t *bt)
                malloc_mutex_unlock(tdata->lock);
                if (error) {
                        if (new_gctx)
-                               prof_gctx_try_destroy(tsd, gctx, tdata);
-                       idalloc(tsd, ret.v);
+                               prof_gctx_try_destroy(tsd, tdata, gctx, tdata);
+                       idalloctm(tsd, ret.v, tcache, true);
                        return (NULL);
                }
                malloc_mutex_lock(gctx->lock);
@@ -857,9 +916,9 @@ prof_bt_count(void)
        if (tdata == NULL)
                return (0);
 
-       prof_enter(tdata);
+       malloc_mutex_lock(&bt2gctx_mtx);
        bt_count = ckh_count(&bt2gctx);
-       prof_leave(tdata);
+       malloc_mutex_unlock(&bt2gctx_mtx);
 
        return (bt_count);
 }
@@ -956,7 +1015,7 @@ prof_dump_write(bool propagate_err, const char *s)
        return (false);
 }
 
-JEMALLOC_ATTR(format(printf, 2, 3))
+JEMALLOC_FORMAT_PRINTF(2, 3)
 static bool
 prof_dump_printf(bool propagate_err, const char *format, ...)
 {
@@ -978,21 +1037,29 @@ prof_tctx_merge_tdata(prof_tctx_t *tctx, prof_tdata_t *tdata)
 {
 
        malloc_mutex_lock(tctx->gctx->lock);
-       if (tctx->state == prof_tctx_state_initializing) {
+
+       switch (tctx->state) {
+       case prof_tctx_state_initializing:
                malloc_mutex_unlock(tctx->gctx->lock);
                return;
-       }
-       assert(tctx->state == prof_tctx_state_nominal);
-       tctx->state = prof_tctx_state_dumping;
-       malloc_mutex_unlock(tctx->gctx->lock);
+       case prof_tctx_state_nominal:
+               tctx->state = prof_tctx_state_dumping;
+               malloc_mutex_unlock(tctx->gctx->lock);
 
-       memcpy(&tctx->dump_cnts, &tctx->cnts, sizeof(prof_cnt_t));
+               memcpy(&tctx->dump_cnts, &tctx->cnts, sizeof(prof_cnt_t));
 
-       tdata->cnt_summed.curobjs += tctx->dump_cnts.curobjs;
-       tdata->cnt_summed.curbytes += tctx->dump_cnts.curbytes;
-       if (opt_prof_accum) {
-               tdata->cnt_summed.accumobjs += tctx->dump_cnts.accumobjs;
-               tdata->cnt_summed.accumbytes += tctx->dump_cnts.accumbytes;
+               tdata->cnt_summed.curobjs += tctx->dump_cnts.curobjs;
+               tdata->cnt_summed.curbytes += tctx->dump_cnts.curbytes;
+               if (opt_prof_accum) {
+                       tdata->cnt_summed.accumobjs +=
+                           tctx->dump_cnts.accumobjs;
+                       tdata->cnt_summed.accumbytes +=
+                           tctx->dump_cnts.accumbytes;
+               }
+               break;
+       case prof_tctx_state_dumping:
+       case prof_tctx_state_purgatory:
+               not_reached();
        }
 }
 
@@ -1035,12 +1102,23 @@ prof_tctx_dump_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg)
 {
        bool propagate_err = *(bool *)arg;
 
-       if (prof_dump_printf(propagate_err,
-           "  t%"PRIu64": %"PRIu64": %"PRIu64" [%"PRIu64": %"PRIu64"]\n",
-           tctx->tdata->thr_uid, tctx->dump_cnts.curobjs,
-           tctx->dump_cnts.curbytes, tctx->dump_cnts.accumobjs,
-           tctx->dump_cnts.accumbytes))
-               return (tctx);
+       switch (tctx->state) {
+       case prof_tctx_state_initializing:
+       case prof_tctx_state_nominal:
+               /* Not captured by this dump. */
+               break;
+       case prof_tctx_state_dumping:
+       case prof_tctx_state_purgatory:
+               if (prof_dump_printf(propagate_err,
+                   "  t%"FMTu64": %"FMTu64": %"FMTu64" [%"FMTu64": "
+                   "%"FMTu64"]\n", tctx->thr_uid, tctx->dump_cnts.curobjs,
+                   tctx->dump_cnts.curbytes, tctx->dump_cnts.accumobjs,
+                   tctx->dump_cnts.accumbytes))
+                       return (tctx);
+               break;
+       default:
+               not_reached();
+       }
        return (NULL);
 }
 
@@ -1132,7 +1210,8 @@ prof_gctx_finish(tsd_t *tsd, prof_gctx_tree_t *gctxs)
                                            to_destroy);
                                        tctx_tree_remove(&gctx->tctxs,
                                            to_destroy);
-                                       idalloc(tsd, to_destroy);
+                                       idalloctm(tsd, to_destroy,
+                                           tcache_get(tsd, false), true);
                                } else
                                        next = NULL;
                        } while (next != NULL);
@@ -1141,7 +1220,7 @@ prof_gctx_finish(tsd_t *tsd, prof_gctx_tree_t *gctxs)
                if (prof_gctx_should_destroy(gctx)) {
                        gctx->nlimbo++;
                        malloc_mutex_unlock(gctx->lock);
-                       prof_gctx_try_destroy(tsd, gctx, tdata);
+                       prof_gctx_try_destroy(tsd, tdata, gctx, tdata);
                } else
                        malloc_mutex_unlock(gctx->lock);
        }
@@ -1188,7 +1267,7 @@ prof_tdata_dump_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata, void *arg)
                return (NULL);
 
        if (prof_dump_printf(propagate_err,
-           "  t%"PRIu64": %"PRIu64": %"PRIu64" [%"PRIu64": %"PRIu64"]%s%s\n",
+           "  t%"FMTu64": %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]%s%s\n",
            tdata->thr_uid, tdata->cnt_summed.curobjs,
            tdata->cnt_summed.curbytes, tdata->cnt_summed.accumobjs,
            tdata->cnt_summed.accumbytes,
@@ -1208,8 +1287,8 @@ prof_dump_header(bool propagate_err, const prof_cnt_t *cnt_all)
        bool ret;
 
        if (prof_dump_printf(propagate_err,
-           "heap_v2/%"PRIu64"\n"
-           "  t*: %"PRIu64": %"PRIu64" [%"PRIu64": %"PRIu64"]\n",
+           "heap_v2/%"FMTu64"\n"
+           "  t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n",
            ((uint64_t)1U << lg_prof_sample), cnt_all->curobjs,
            cnt_all->curbytes, cnt_all->accumobjs, cnt_all->accumbytes))
                return (true);
@@ -1252,7 +1331,7 @@ prof_dump_gctx(bool propagate_err, prof_gctx_t *gctx, const prof_bt_t *bt,
                goto label_return;
        }
        for (i = 0; i < bt->len; i++) {
-               if (prof_dump_printf(propagate_err, " %#"PRIxPTR,
+               if (prof_dump_printf(propagate_err, " %#"FMTxPTR,
                    (uintptr_t)bt->vec[i])) {
                        ret = true;
                        goto label_return;
@@ -1261,7 +1340,7 @@ prof_dump_gctx(bool propagate_err, prof_gctx_t *gctx, const prof_bt_t *bt,
 
        if (prof_dump_printf(propagate_err,
            "\n"
-           "  t*: %"PRIu64": %"PRIu64" [%"PRIu64": %"PRIu64"]\n",
+           "  t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n",
            gctx->cnt_summed.curobjs, gctx->cnt_summed.curbytes,
            gctx->cnt_summed.accumobjs, gctx->cnt_summed.accumbytes)) {
                ret = true;
@@ -1279,21 +1358,40 @@ label_return:
        return (ret);
 }
 
+JEMALLOC_FORMAT_PRINTF(1, 2)
+static int
+prof_open_maps(const char *format, ...)
+{
+       int mfd;
+       va_list ap;
+       char filename[PATH_MAX + 1];
+
+       va_start(ap, format);
+       malloc_vsnprintf(filename, sizeof(filename), format, ap);
+       va_end(ap);
+       mfd = open(filename, O_RDONLY);
+
+       return (mfd);
+}
+
 static bool
 prof_dump_maps(bool propagate_err)
 {
        bool ret;
        int mfd;
-       char filename[PATH_MAX + 1];
 
        cassert(config_prof);
 #ifdef __FreeBSD__
-       malloc_snprintf(filename, sizeof(filename), "/proc/curproc/map");
+       mfd = prof_open_maps("/proc/curproc/map");
 #else
-       malloc_snprintf(filename, sizeof(filename), "/proc/%d/maps",
-           (int)getpid());
+       {
+               int pid = getpid();
+
+               mfd = prof_open_maps("/proc/%d/task/%d/maps", pid, pid);
+               if (mfd == -1)
+                       mfd = prof_open_maps("/proc/%d/maps", pid);
+       }
 #endif
-       mfd = open(filename, O_RDONLY);
        if (mfd != -1) {
                ssize_t nread;
 
@@ -1334,13 +1432,13 @@ prof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_ngctx,
 {
 
        if (cnt_all->curbytes != 0) {
-               malloc_printf("<jemalloc>: Leak summary: %"PRIu64" byte%s, %"
-                   PRIu64" object%s, %zu context%s\n",
+               malloc_printf("<jemalloc>: Leak summary: %"FMTu64" byte%s, %"
+                   FMTu64" object%s, %zu context%s\n",
                    cnt_all->curbytes, (cnt_all->curbytes != 1) ? "s" : "",
                    cnt_all->curobjs, (cnt_all->curobjs != 1) ? "s" : "",
                    leak_ngctx, (leak_ngctx != 1) ? "s" : "");
                malloc_printf(
-                   "<jemalloc>: Run pprof on \"%s\" for leak detail\n",
+                   "<jemalloc>: Run jeprof on \"%s\" for leak detail\n",
                    filename);
        }
 }
@@ -1384,7 +1482,7 @@ prof_dump(tsd_t *tsd, bool propagate_err, const char *filename, bool leakcheck)
                return (true);
 
        malloc_mutex_lock(&prof_dump_mtx);
-       prof_enter(tdata);
+       prof_enter(tsd, tdata);
 
        /*
         * Put gctx's in limbo and clear their counters in preparation for
@@ -1407,7 +1505,7 @@ prof_dump(tsd_t *tsd, bool propagate_err, const char *filename, bool leakcheck)
        leak_ngctx = 0;
        gctx_tree_iter(&gctxs, NULL, prof_gctx_merge_iter, (void *)&leak_ngctx);
 
-       prof_leave(tdata);
+       prof_leave(tsd, tdata);
 
        /* Create dump file. */
        if ((prof_dump_fd = prof_dump_open(propagate_err, filename)) == -1)
@@ -1455,12 +1553,12 @@ prof_dump_filename(char *filename, char v, uint64_t vseq)
        if (vseq != VSEQ_INVALID) {
                /* "<prefix>.<pid>.<seq>.v<vseq>.heap" */
                malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,
-                   "%s.%d.%"PRIu64".%c%"PRIu64".heap",
+                   "%s.%d.%"FMTu64".%c%"FMTu64".heap",
                    opt_prof_prefix, (int)getpid(), prof_dump_seq, v, vseq);
        } else {
                /* "<prefix>.<pid>.<seq>.<v>.heap" */
                malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,
-                   "%s.%d.%"PRIu64".%c.heap",
+                   "%s.%d.%"FMTu64".%c.heap",
                    opt_prof_prefix, (int)getpid(), prof_dump_seq, v);
        }
        prof_dump_seq++;
@@ -1473,17 +1571,17 @@ prof_fdump(void)
        char filename[DUMP_FILENAME_BUFSIZE];
 
        cassert(config_prof);
+       assert(opt_prof_final);
+       assert(opt_prof_prefix[0] != '\0');
 
        if (!prof_booted)
                return;
        tsd = tsd_fetch();
 
-       if (opt_prof_final && opt_prof_prefix[0] != '\0') {
-               malloc_mutex_lock(&prof_dump_seq_mtx);
-               prof_dump_filename(filename, 'f', VSEQ_INVALID);
-               malloc_mutex_unlock(&prof_dump_seq_mtx);
-               prof_dump(tsd, false, filename, opt_prof_leak);
-       }
+       malloc_mutex_lock(&prof_dump_seq_mtx);
+       prof_dump_filename(filename, 'f', VSEQ_INVALID);
+       malloc_mutex_unlock(&prof_dump_seq_mtx);
+       prof_dump(tsd, false, filename, opt_prof_leak);
 }
 
 void
@@ -1491,7 +1589,6 @@ prof_idump(void)
 {
        tsd_t *tsd;
        prof_tdata_t *tdata;
-       char filename[PATH_MAX + 1];
 
        cassert(config_prof);
 
@@ -1507,6 +1604,7 @@ prof_idump(void)
        }
 
        if (opt_prof_prefix[0] != '\0') {
+               char filename[PATH_MAX + 1];
                malloc_mutex_lock(&prof_dump_seq_mtx);
                prof_dump_filename(filename, 'i', prof_dump_iseq);
                prof_dump_iseq++;
@@ -1545,7 +1643,6 @@ prof_gdump(void)
 {
        tsd_t *tsd;
        prof_tdata_t *tdata;
-       char filename[DUMP_FILENAME_BUFSIZE];
 
        cassert(config_prof);
 
@@ -1561,6 +1658,7 @@ prof_gdump(void)
        }
 
        if (opt_prof_prefix[0] != '\0') {
+               char filename[DUMP_FILENAME_BUFSIZE];
                malloc_mutex_lock(&prof_dump_seq_mtx);
                prof_dump_filename(filename, 'u', prof_dump_useq);
                prof_dump_useq++;
@@ -1610,11 +1708,14 @@ prof_tdata_init_impl(tsd_t *tsd, uint64_t thr_uid, uint64_t thr_discrim,
     char *thread_name, bool active)
 {
        prof_tdata_t *tdata;
+       tcache_t *tcache;
 
        cassert(config_prof);
 
        /* Initialize an empty cache for this thread. */
-       tdata = (prof_tdata_t *)imalloc(tsd, sizeof(prof_tdata_t));
+       tcache = tcache_get(tsd, true);
+       tdata = (prof_tdata_t *)iallocztm(tsd, sizeof(prof_tdata_t), false,
+           tcache, true, NULL);
        if (tdata == NULL)
                return (NULL);
 
@@ -1624,10 +1725,11 @@ prof_tdata_init_impl(tsd_t *tsd, uint64_t thr_uid, uint64_t thr_discrim,
        tdata->thread_name = thread_name;
        tdata->attached = true;
        tdata->expired = false;
+       tdata->tctx_uid_next = 0;
 
        if (ckh_new(tsd, &tdata->bt2tctx, PROF_CKH_MINITEMS,
            prof_bt_hash, prof_bt_keycomp)) {
-               idalloc(tsd, tdata);
+               idalloctm(tsd, tdata, tcache, true);
                return (NULL);
        }
 
@@ -1673,16 +1775,18 @@ static void
 prof_tdata_destroy_locked(tsd_t *tsd, prof_tdata_t *tdata,
     bool even_if_attached)
 {
+       tcache_t *tcache;
 
        assert(prof_tdata_should_destroy(tdata, even_if_attached));
        assert(tsd_prof_tdata_get(tsd) != tdata);
 
        tdata_tree_remove(&tdatas, tdata);
 
+       tcache = tcache_get(tsd, false);
        if (tdata->thread_name != NULL)
-               idalloc(tsd, tdata->thread_name);
+               idalloctm(tsd, tdata->thread_name, tcache, true);
        ckh_delete(tsd, &tdata->bt2tctx);
-       idalloc(tsd, tdata);
+       idalloctm(tsd, tdata, tcache, true);
 }
 
 static void
@@ -1843,7 +1947,7 @@ prof_thread_name_alloc(tsd_t *tsd, const char *thread_name)
        if (size == 1)
                return ("");
 
-       ret = imalloc(tsd, size);
+       ret = iallocztm(tsd, size, false, tcache_get(tsd, true), true, NULL);
        if (ret == NULL)
                return (NULL);
        memcpy(ret, thread_name, size);
@@ -1875,7 +1979,8 @@ prof_thread_name_set(tsd_t *tsd, const char *thread_name)
                return (EAGAIN);
 
        if (tdata->thread_name != NULL) {
-               idalloc(tsd, tdata->thread_name);
+               idalloctm(tsd, tdata->thread_name, tcache_get(tsd, false),
+                   true);
                tdata->thread_name = NULL;
        }
        if (strlen(s) > 0)
@@ -1933,6 +2038,29 @@ prof_thread_active_init_set(bool active_init)
        return (active_init_old);
 }
 
+bool
+prof_gdump_get(void)
+{
+       bool prof_gdump_current;
+
+       malloc_mutex_lock(&prof_gdump_mtx);
+       prof_gdump_current = prof_gdump_val;
+       malloc_mutex_unlock(&prof_gdump_mtx);
+       return (prof_gdump_current);
+}
+
+bool
+prof_gdump_set(bool gdump)
+{
+       bool prof_gdump_old;
+
+       malloc_mutex_lock(&prof_gdump_mtx);
+       prof_gdump_old = prof_gdump_val;
+       prof_gdump_val = gdump;
+       malloc_mutex_unlock(&prof_gdump_mtx);
+       return (prof_gdump_old);
+}
+
 void
 prof_boot0(void)
 {
@@ -1985,6 +2113,10 @@ prof_boot2(void)
                if (malloc_mutex_init(&prof_active_mtx))
                        return (true);
 
+               prof_gdump_val = opt_prof_gdump;
+               if (malloc_mutex_init(&prof_gdump_mtx))
+                       return (true);
+
                prof_thread_active_init = opt_prof_thread_active_init;
                if (malloc_mutex_init(&prof_thread_active_init_mtx))
                        return (true);
@@ -2009,7 +2141,8 @@ prof_boot2(void)
                if (malloc_mutex_init(&prof_dump_mtx))
                        return (true);
 
-               if (atexit(prof_fdump) != 0) {
+               if (opt_prof_final && opt_prof_prefix[0] != '\0' &&
+                   atexit(prof_fdump) != 0) {
                        malloc_write("<jemalloc>: Error in atexit()\n");
                        if (opt_abort)
                                abort();
index 1301b4793aee1291d63e6852451ed0431cb20531..6c43dfcaa3afffbb3b90e56c8cb3e528f5156a62 100644 (file)
@@ -2,7 +2,7 @@
 #include "jemalloc/internal/jemalloc_internal.h"
 
 /*
- * quarantine pointers close to NULL are used to encode state information that
+ * Quarantine pointers close to NULL are used to encode state information that
  * is used for cleaning up during thread shutdown.
  */
 #define        QUARANTINE_STATE_REINCARNATED   ((quarantine_t *)(uintptr_t)1)
@@ -19,13 +19,16 @@ static void quarantine_drain(tsd_t *tsd, quarantine_t *quarantine,
 
 /******************************************************************************/
 
-quarantine_t *
+static quarantine_t *
 quarantine_init(tsd_t *tsd, size_t lg_maxobjs)
 {
        quarantine_t *quarantine;
 
-       quarantine = (quarantine_t *)imalloc(tsd, offsetof(quarantine_t, objs) +
-           ((ZU(1) << lg_maxobjs) * sizeof(quarantine_obj_t)));
+       assert(tsd_nominal(tsd));
+
+       quarantine = (quarantine_t *)iallocztm(tsd, offsetof(quarantine_t, objs)
+           + ((ZU(1) << lg_maxobjs) * sizeof(quarantine_obj_t)), false,
+           tcache_get(tsd, true), true, NULL);
        if (quarantine == NULL)
                return (NULL);
        quarantine->curbytes = 0;
@@ -36,6 +39,25 @@ quarantine_init(tsd_t *tsd, size_t lg_maxobjs)
        return (quarantine);
 }
 
+void
+quarantine_alloc_hook_work(tsd_t *tsd)
+{
+       quarantine_t *quarantine;
+
+       if (!tsd_nominal(tsd))
+               return;
+
+       quarantine = quarantine_init(tsd, LG_MAXOBJS_INIT);
+       /*
+        * Check again whether quarantine has been initialized, because
+        * quarantine_init() may have triggered recursive initialization.
+        */
+       if (tsd_quarantine_get(tsd) == NULL)
+               tsd_quarantine_set(tsd, quarantine);
+       else
+               idalloctm(tsd, quarantine, tcache_get(tsd, false), true);
+}
+
 static quarantine_t *
 quarantine_grow(tsd_t *tsd, quarantine_t *quarantine)
 {
@@ -65,8 +87,9 @@ quarantine_grow(tsd_t *tsd, quarantine_t *quarantine)
                memcpy(&ret->objs[ncopy_a], quarantine->objs, ncopy_b *
                    sizeof(quarantine_obj_t));
        }
-       idalloc(tsd, quarantine);
+       idalloctm(tsd, quarantine, tcache_get(tsd, false), true);
 
+       tsd_quarantine_set(tsd, ret);
        return (ret);
 }
 
@@ -75,7 +98,7 @@ quarantine_drain_one(tsd_t *tsd, quarantine_t *quarantine)
 {
        quarantine_obj_t *obj = &quarantine->objs[quarantine->first];
        assert(obj->usize == isalloc(obj->ptr, config_prof));
-       idalloc(tsd, obj->ptr);
+       idalloctm(tsd, obj->ptr, NULL, false);
        quarantine->curbytes -= obj->usize;
        quarantine->curobjs--;
        quarantine->first = (quarantine->first + 1) & ((ZU(1) <<
@@ -100,7 +123,7 @@ quarantine(tsd_t *tsd, void *ptr)
        assert(opt_quarantine);
 
        if ((quarantine = tsd_quarantine_get(tsd)) == NULL) {
-               idalloc(tsd, ptr);
+               idalloctm(tsd, ptr, NULL, false);
                return;
        }
        /*
@@ -126,7 +149,7 @@ quarantine(tsd_t *tsd, void *ptr)
                obj->usize = usize;
                quarantine->curbytes += usize;
                quarantine->curobjs++;
-               if (config_fill && unlikely(opt_junk)) {
+               if (config_fill && unlikely(opt_junk_free)) {
                        /*
                         * Only do redzone validation if Valgrind isn't in
                         * operation.
@@ -139,7 +162,7 @@ quarantine(tsd_t *tsd, void *ptr)
                }
        } else {
                assert(quarantine->curbytes == 0);
-               idalloc(tsd, ptr);
+               idalloctm(tsd, ptr, NULL, false);
        }
 }
 
@@ -154,7 +177,7 @@ quarantine_cleanup(tsd_t *tsd)
        quarantine = tsd_quarantine_get(tsd);
        if (quarantine != NULL) {
                quarantine_drain(tsd, quarantine, 0);
-               idalloc(tsd, quarantine);
+               idalloctm(tsd, quarantine, tcache_get(tsd, false), true);
                tsd_quarantine_set(tsd, NULL);
        }
 }
index 2ff93dbe75ce12bf1e1f2543ce07b7ac22e9b479..af0d97e753018d2da0dba40a73bc06e4ffee4499 100644 (file)
@@ -1,75 +1,74 @@
 #define        JEMALLOC_RTREE_C_
 #include "jemalloc/internal/jemalloc_internal.h"
 
-rtree_t *
-rtree_new(unsigned bits, rtree_alloc_t *alloc, rtree_dalloc_t *dalloc)
+static unsigned
+hmin(unsigned ha, unsigned hb)
 {
-       rtree_t *ret;
-       unsigned bits_per_level, bits_in_leaf, height, i;
+
+       return (ha < hb ? ha : hb);
+}
+
+/* Only the most significant bits of keys passed to rtree_[gs]et() are used. */
+bool
+rtree_new(rtree_t *rtree, unsigned bits, rtree_node_alloc_t *alloc,
+    rtree_node_dalloc_t *dalloc)
+{
+       unsigned bits_in_leaf, height, i;
 
        assert(bits > 0 && bits <= (sizeof(uintptr_t) << 3));
 
-       bits_per_level = jemalloc_ffs(pow2_ceil((RTREE_NODESIZE / sizeof(void
-           *)))) - 1;
-       bits_in_leaf = jemalloc_ffs(pow2_ceil((RTREE_NODESIZE /
-           sizeof(uint8_t)))) - 1;
+       bits_in_leaf = (bits % RTREE_BITS_PER_LEVEL) == 0 ? RTREE_BITS_PER_LEVEL
+           : (bits % RTREE_BITS_PER_LEVEL);
        if (bits > bits_in_leaf) {
-               height = 1 + (bits - bits_in_leaf) / bits_per_level;
-               if ((height-1) * bits_per_level + bits_in_leaf != bits)
+               height = 1 + (bits - bits_in_leaf) / RTREE_BITS_PER_LEVEL;
+               if ((height-1) * RTREE_BITS_PER_LEVEL + bits_in_leaf != bits)
                        height++;
-       } else {
+       } else
                height = 1;
+       assert((height-1) * RTREE_BITS_PER_LEVEL + bits_in_leaf == bits);
+
+       rtree->alloc = alloc;
+       rtree->dalloc = dalloc;
+       rtree->height = height;
+
+       /* Root level. */
+       rtree->levels[0].subtree = NULL;
+       rtree->levels[0].bits = (height > 1) ? RTREE_BITS_PER_LEVEL :
+           bits_in_leaf;
+       rtree->levels[0].cumbits = rtree->levels[0].bits;
+       /* Interior levels. */
+       for (i = 1; i < height-1; i++) {
+               rtree->levels[i].subtree = NULL;
+               rtree->levels[i].bits = RTREE_BITS_PER_LEVEL;
+               rtree->levels[i].cumbits = rtree->levels[i-1].cumbits +
+                   RTREE_BITS_PER_LEVEL;
        }
-       assert((height-1) * bits_per_level + bits_in_leaf >= bits);
-
-       ret = (rtree_t*)alloc(offsetof(rtree_t, level2bits) +
-           (sizeof(unsigned) * height));
-       if (ret == NULL)
-               return (NULL);
-       memset(ret, 0, offsetof(rtree_t, level2bits) + (sizeof(unsigned) *
-           height));
-
-       ret->alloc = alloc;
-       ret->dalloc = dalloc;
-       if (malloc_mutex_init(&ret->mutex)) {
-               if (dalloc != NULL)
-                       dalloc(ret);
-               return (NULL);
-       }
-       ret->height = height;
+       /* Leaf level. */
        if (height > 1) {
-               if ((height-1) * bits_per_level + bits_in_leaf > bits) {
-                       ret->level2bits[0] = (bits - bits_in_leaf) %
-                           bits_per_level;
-               } else
-                       ret->level2bits[0] = bits_per_level;
-               for (i = 1; i < height-1; i++)
-                       ret->level2bits[i] = bits_per_level;
-               ret->level2bits[height-1] = bits_in_leaf;
-       } else
-               ret->level2bits[0] = bits;
+               rtree->levels[height-1].subtree = NULL;
+               rtree->levels[height-1].bits = bits_in_leaf;
+               rtree->levels[height-1].cumbits = bits;
+       }
 
-       ret->root = (void**)alloc(sizeof(void *) << ret->level2bits[0]);
-       if (ret->root == NULL) {
-               if (dalloc != NULL)
-                       dalloc(ret);
-               return (NULL);
+       /* Compute lookup table to be used by rtree_start_level(). */
+       for (i = 0; i < RTREE_HEIGHT_MAX; i++) {
+               rtree->start_level[i] = hmin(RTREE_HEIGHT_MAX - 1 - i, height -
+                   1);
        }
-       memset(ret->root, 0, sizeof(void *) << ret->level2bits[0]);
 
-       return (ret);
+       return (false);
 }
 
 static void
-rtree_delete_subtree(rtree_t *rtree, void **node, unsigned level)
+rtree_delete_subtree(rtree_t *rtree, rtree_node_elm_t *node, unsigned level)
 {
 
-       if (level < rtree->height - 1) {
+       if (level + 1 < rtree->height) {
                size_t nchildren, i;
 
-               nchildren = ZU(1) << rtree->level2bits[level];
+               nchildren = ZU(1) << rtree->levels[level].bits;
                for (i = 0; i < nchildren; i++) {
-                       void **child = (void **)node[i];
+                       rtree_node_elm_t *child = node[i].child;
                        if (child != NULL)
                                rtree_delete_subtree(rtree, child, level + 1);
                }
@@ -80,28 +79,49 @@ rtree_delete_subtree(rtree_t *rtree, void **node, unsigned level)
 void
 rtree_delete(rtree_t *rtree)
 {
+       unsigned i;
 
-       rtree_delete_subtree(rtree, rtree->root, 0);
-       rtree->dalloc(rtree);
+       for (i = 0; i < rtree->height; i++) {
+               rtree_node_elm_t *subtree = rtree->levels[i].subtree;
+               if (subtree != NULL)
+                       rtree_delete_subtree(rtree, subtree, i);
+       }
 }
 
-void
-rtree_prefork(rtree_t *rtree)
+static rtree_node_elm_t *
+rtree_node_init(rtree_t *rtree, unsigned level, rtree_node_elm_t **elmp)
 {
+       rtree_node_elm_t *node;
+
+       if (atomic_cas_p((void **)elmp, NULL, RTREE_NODE_INITIALIZING)) {
+               /*
+                * Another thread is already in the process of initializing.
+                * Spin-wait until initialization is complete.
+                */
+               do {
+                       CPU_SPINWAIT;
+                       node = atomic_read_p((void **)elmp);
+               } while (node == RTREE_NODE_INITIALIZING);
+       } else {
+               node = rtree->alloc(ZU(1) << rtree->levels[level].bits);
+               if (node == NULL)
+                       return (NULL);
+               atomic_write_p((void **)elmp, node);
+       }
 
-       malloc_mutex_prefork(&rtree->mutex);
+       return (node);
 }
 
-void
-rtree_postfork_parent(rtree_t *rtree)
+rtree_node_elm_t *
+rtree_subtree_read_hard(rtree_t *rtree, unsigned level)
 {
 
-       malloc_mutex_postfork_parent(&rtree->mutex);
+       return (rtree_node_init(rtree, level, &rtree->levels[level].subtree));
 }
 
-void
-rtree_postfork_child(rtree_t *rtree)
+rtree_node_elm_t *
+rtree_child_read_hard(rtree_t *rtree, rtree_node_elm_t *elm, unsigned level)
 {
 
-       malloc_mutex_postfork_child(&rtree->mutex);
+       return (rtree_node_init(rtree, level, &elm->child));
 }
index 5c3d701753519c753013bcaa26ab9541a37336b7..154c3e74cd36c217ba2434080908064cef43f1e0 100644 (file)
@@ -6,31 +6,22 @@
        xmallctl(n, v, &sz, NULL, 0);                                   \
 } while (0)
 
-#define        CTL_I_GET(n, v, t) do {                                         \
+#define        CTL_M2_GET(n, i, v, t) do {                                     \
        size_t mib[6];                                                  \
        size_t miblen = sizeof(mib) / sizeof(size_t);                   \
        size_t sz = sizeof(t);                                          \
        xmallctlnametomib(n, mib, &miblen);                             \
-       mib[2] = i;                                                     \
+       mib[2] = (i);                                                   \
        xmallctlbymib(mib, miblen, v, &sz, NULL, 0);                    \
 } while (0)
 
-#define        CTL_J_GET(n, v, t) do {                                         \
+#define        CTL_M2_M4_GET(n, i, j, v, t) do {                               \
        size_t mib[6];                                                  \
        size_t miblen = sizeof(mib) / sizeof(size_t);                   \
        size_t sz = sizeof(t);                                          \
        xmallctlnametomib(n, mib, &miblen);                             \
-       mib[2] = j;                                                     \
-       xmallctlbymib(mib, miblen, v, &sz, NULL, 0);                    \
-} while (0)
-
-#define        CTL_IJ_GET(n, v, t) do {                                        \
-       size_t mib[6];                                                  \
-       size_t miblen = sizeof(mib) / sizeof(size_t);                   \
-       size_t sz = sizeof(t);                                          \
-       xmallctlnametomib(n, mib, &miblen);                             \
-       mib[2] = i;                                                     \
-       mib[4] = j;                                                     \
+       mib[2] = (i);                                                   \
+       mib[4] = (j);                                                   \
        xmallctlbymib(mib, miblen, v, &sz, NULL, 0);                    \
 } while (0)
 
@@ -48,8 +39,10 @@ static void  stats_arena_bins_print(void (*write_cb)(void *, const char *),
     void *cbopaque, unsigned i);
 static void    stats_arena_lruns_print(void (*write_cb)(void *, const char *),
     void *cbopaque, unsigned i);
+static void    stats_arena_hchunks_print(
+    void (*write_cb)(void *, const char *), void *cbopaque, unsigned i);
 static void    stats_arena_print(void (*write_cb)(void *, const char *),
-    void *cbopaque, unsigned i, bool bins, bool large);
+    void *cbopaque, unsigned i, bool bins, bool large, bool huge);
 
 /******************************************************************************/
 
@@ -58,100 +51,109 @@ stats_arena_bins_print(void (*write_cb)(void *, const char *), void *cbopaque,
     unsigned i)
 {
        size_t page;
-       bool config_tcache;
-       unsigned nbins, j, gap_start;
+       bool config_tcache, in_gap;
+       unsigned nbins, j;
 
        CTL_GET("arenas.page", &page, size_t);
 
        CTL_GET("config.tcache", &config_tcache, bool);
        if (config_tcache) {
                malloc_cprintf(write_cb, cbopaque,
-                   "bins:     bin  size regs pgs    allocated      nmalloc"
-                   "      ndalloc    nrequests       nfills     nflushes"
-                   "      newruns       reruns      curruns\n");
+                   "bins:           size ind    allocated      nmalloc"
+                   "      ndalloc    nrequests      curregs      curruns regs"
+                   " pgs  util       nfills     nflushes      newruns"
+                   "       reruns\n");
        } else {
                malloc_cprintf(write_cb, cbopaque,
-                   "bins:     bin  size regs pgs    allocated      nmalloc"
-                   "      ndalloc      newruns       reruns      curruns\n");
+                   "bins:           size ind    allocated      nmalloc"
+                   "      ndalloc    nrequests      curregs      curruns regs"
+                   " pgs  util      newruns       reruns\n");
        }
        CTL_GET("arenas.nbins", &nbins, unsigned);
-       for (j = 0, gap_start = UINT_MAX; j < nbins; j++) {
+       for (j = 0, in_gap = false; j < nbins; j++) {
                uint64_t nruns;
 
-               CTL_IJ_GET("stats.arenas.0.bins.0.nruns", &nruns, uint64_t);
-               if (nruns == 0) {
-                       if (gap_start == UINT_MAX)
-                               gap_start = j;
-               } else {
-                       size_t reg_size, run_size, allocated;
+               CTL_M2_M4_GET("stats.arenas.0.bins.0.nruns", i, j, &nruns,
+                   uint64_t);
+               if (nruns == 0)
+                       in_gap = true;
+               else {
+                       size_t reg_size, run_size, curregs, availregs, milli;
+                       size_t curruns;
                        uint32_t nregs;
                        uint64_t nmalloc, ndalloc, nrequests, nfills, nflushes;
                        uint64_t reruns;
-                       size_t curruns;
+                       char util[6]; /* "x.yyy". */
 
-                       if (gap_start != UINT_MAX) {
-                               if (j > gap_start + 1) {
-                                       /* Gap of more than one size class. */
-                                       malloc_cprintf(write_cb, cbopaque,
-                                           "[%u..%u]\n", gap_start,
-                                           j - 1);
-                               } else {
-                                       /* Gap of one size class. */
-                                       malloc_cprintf(write_cb, cbopaque,
-                                           "[%u]\n", gap_start);
-                               }
-                               gap_start = UINT_MAX;
+                       if (in_gap) {
+                               malloc_cprintf(write_cb, cbopaque,
+                                   "                     ---\n");
+                               in_gap = false;
                        }
-                       CTL_J_GET("arenas.bin.0.size", &reg_size, size_t);
-                       CTL_J_GET("arenas.bin.0.nregs", &nregs, uint32_t);
-                       CTL_J_GET("arenas.bin.0.run_size", &run_size, size_t);
-                       CTL_IJ_GET("stats.arenas.0.bins.0.allocated",
-                           &allocated, size_t);
-                       CTL_IJ_GET("stats.arenas.0.bins.0.nmalloc",
+                       CTL_M2_GET("arenas.bin.0.size", j, &reg_size, size_t);
+                       CTL_M2_GET("arenas.bin.0.nregs", j, &nregs, uint32_t);
+                       CTL_M2_GET("arenas.bin.0.run_size", j, &run_size,
+                           size_t);
+                       CTL_M2_M4_GET("stats.arenas.0.bins.0.nmalloc", i, j,
                            &nmalloc, uint64_t);
-                       CTL_IJ_GET("stats.arenas.0.bins.0.ndalloc",
+                       CTL_M2_M4_GET("stats.arenas.0.bins.0.ndalloc", i, j,
                            &ndalloc, uint64_t);
+                       CTL_M2_M4_GET("stats.arenas.0.bins.0.curregs", i, j,
+                           &curregs, size_t);
+                       CTL_M2_M4_GET("stats.arenas.0.bins.0.nrequests", i, j,
+                           &nrequests, uint64_t);
                        if (config_tcache) {
-                               CTL_IJ_GET("stats.arenas.0.bins.0.nrequests",
-                                   &nrequests, uint64_t);
-                               CTL_IJ_GET("stats.arenas.0.bins.0.nfills",
-                                   &nfills, uint64_t);
-                               CTL_IJ_GET("stats.arenas.0.bins.0.nflushes",
-                                   &nflushes, uint64_t);
+                               CTL_M2_M4_GET("stats.arenas.0.bins.0.nfills", i,
+                                   j, &nfills, uint64_t);
+                               CTL_M2_M4_GET("stats.arenas.0.bins.0.nflushes",
+                                   i, j, &nflushes, uint64_t);
                        }
-                       CTL_IJ_GET("stats.arenas.0.bins.0.nreruns", &reruns,
-                           uint64_t);
-                       CTL_IJ_GET("stats.arenas.0.bins.0.curruns", &curruns,
-                           size_t);
+                       CTL_M2_M4_GET("stats.arenas.0.bins.0.nreruns", i, j,
+                           &reruns, uint64_t);
+                       CTL_M2_M4_GET("stats.arenas.0.bins.0.curruns", i, j,
+                           &curruns, size_t);
+
+                       availregs = nregs * curruns;
+                       milli = (availregs != 0) ? (1000 * curregs) / availregs
+                           : 1000;
+                       assert(milli <= 1000);
+                       if (milli < 10) {
+                               malloc_snprintf(util, sizeof(util),
+                                   "0.00%zu", milli);
+                       } else if (milli < 100) {
+                               malloc_snprintf(util, sizeof(util), "0.0%zu",
+                                   milli);
+                       } else if (milli < 1000) {
+                               malloc_snprintf(util, sizeof(util), "0.%zu",
+                                   milli);
+                       } else
+                               malloc_snprintf(util, sizeof(util), "1");
+
                        if (config_tcache) {
                                malloc_cprintf(write_cb, cbopaque,
-                                   "%13u %5zu %4u %3zu %12zu %12"PRIu64
-                                   " %12"PRIu64" %12"PRIu64" %12"PRIu64
-                                   " %12"PRIu64" %12"PRIu64" %12"PRIu64
-                                   " %12zu\n",
-                                   j, reg_size, nregs, run_size / page,
-                                   allocated, nmalloc, ndalloc, nrequests,
-                                   nfills, nflushes, nruns, reruns, curruns);
+                                   "%20zu %3u %12zu %12"FMTu64
+                                   " %12"FMTu64" %12"FMTu64" %12zu"
+                                   " %12zu %4u %3zu %-5s %12"FMTu64
+                                   " %12"FMTu64" %12"FMTu64" %12"FMTu64"\n",
+                                   reg_size, j, curregs * reg_size, nmalloc,
+                                   ndalloc, nrequests, curregs, curruns, nregs,
+                                   run_size / page, util, nfills, nflushes,
+                                   nruns, reruns);
                        } else {
                                malloc_cprintf(write_cb, cbopaque,
-                                   "%13u %5zu %4u %3zu %12zu %12"PRIu64
-                                   " %12"PRIu64" %12"PRIu64" %12"PRIu64
-                                   " %12zu\n",
-                                   j, reg_size, nregs, run_size / page,
-                                   allocated, nmalloc, ndalloc, nruns, reruns,
-                                   curruns);
+                                   "%20zu %3u %12zu %12"FMTu64
+                                   " %12"FMTu64" %12"FMTu64" %12zu"
+                                   " %12zu %4u %3zu %-5s %12"FMTu64
+                                   " %12"FMTu64"\n",
+                                   reg_size, j, curregs * reg_size, nmalloc,
+                                   ndalloc, nrequests, curregs, curruns, nregs,
+                                   run_size / page, util, nruns, reruns);
                        }
                }
        }
-       if (gap_start != UINT_MAX) {
-               if (j > gap_start + 1) {
-                       /* Gap of more than one size class. */
-                       malloc_cprintf(write_cb, cbopaque, "[%u..%u]\n",
-                           gap_start, j - 1);
-               } else {
-                       /* Gap of one size class. */
-                       malloc_cprintf(write_cb, cbopaque, "[%u]\n", gap_start);
-               }
+       if (in_gap) {
+               malloc_cprintf(write_cb, cbopaque,
+                   "                     ---\n");
        }
 }
 
@@ -159,55 +161,106 @@ static void
 stats_arena_lruns_print(void (*write_cb)(void *, const char *), void *cbopaque,
     unsigned i)
 {
-       size_t page, nlruns, j;
-       ssize_t gap_start;
-
-       CTL_GET("arenas.page", &page, size_t);
+       unsigned nbins, nlruns, j;
+       bool in_gap;
 
        malloc_cprintf(write_cb, cbopaque,
-           "large:   size pages      nmalloc      ndalloc    nrequests"
-           "      curruns\n");
-       CTL_GET("arenas.nlruns", &nlruns, size_t);
-       for (j = 0, gap_start = -1; j < nlruns; j++) {
+           "large:          size ind    allocated      nmalloc      ndalloc"
+           "    nrequests      curruns\n");
+       CTL_GET("arenas.nbins", &nbins, unsigned);
+       CTL_GET("arenas.nlruns", &nlruns, unsigned);
+       for (j = 0, in_gap = false; j < nlruns; j++) {
                uint64_t nmalloc, ndalloc, nrequests;
                size_t run_size, curruns;
 
-               CTL_IJ_GET("stats.arenas.0.lruns.0.nmalloc", &nmalloc,
+               CTL_M2_M4_GET("stats.arenas.0.lruns.0.nmalloc", i, j, &nmalloc,
                    uint64_t);
-               CTL_IJ_GET("stats.arenas.0.lruns.0.ndalloc", &ndalloc,
+               CTL_M2_M4_GET("stats.arenas.0.lruns.0.ndalloc", i, j, &ndalloc,
                    uint64_t);
-               CTL_IJ_GET("stats.arenas.0.lruns.0.nrequests", &nrequests,
-                   uint64_t);
-               if (nrequests == 0) {
-                       if (gap_start == -1)
-                               gap_start = j;
-               } else {
-                       CTL_J_GET("arenas.lrun.0.size", &run_size, size_t);
-                       CTL_IJ_GET("stats.arenas.0.lruns.0.curruns", &curruns,
+               CTL_M2_M4_GET("stats.arenas.0.lruns.0.nrequests", i, j,
+                   &nrequests, uint64_t);
+               if (nrequests == 0)
+                       in_gap = true;
+               else {
+                       CTL_M2_GET("arenas.lrun.0.size", j, &run_size, size_t);
+                       CTL_M2_M4_GET("stats.arenas.0.lruns.0.curruns", i, j,
+                           &curruns, size_t);
+                       if (in_gap) {
+                               malloc_cprintf(write_cb, cbopaque,
+                                   "                     ---\n");
+                               in_gap = false;
+                       }
+                       malloc_cprintf(write_cb, cbopaque,
+                           "%20zu %3u %12zu %12"FMTu64" %12"FMTu64
+                           " %12"FMTu64" %12zu\n",
+                           run_size, nbins + j, curruns * run_size, nmalloc,
+                           ndalloc, nrequests, curruns);
+               }
+       }
+       if (in_gap) {
+               malloc_cprintf(write_cb, cbopaque,
+                   "                     ---\n");
+       }
+}
+
+static void
+stats_arena_hchunks_print(void (*write_cb)(void *, const char *),
+    void *cbopaque, unsigned i)
+{
+       unsigned nbins, nlruns, nhchunks, j;
+       bool in_gap;
+
+       malloc_cprintf(write_cb, cbopaque,
+           "huge:           size ind    allocated      nmalloc      ndalloc"
+           "    nrequests   curhchunks\n");
+       CTL_GET("arenas.nbins", &nbins, unsigned);
+       CTL_GET("arenas.nlruns", &nlruns, unsigned);
+       CTL_GET("arenas.nhchunks", &nhchunks, unsigned);
+       for (j = 0, in_gap = false; j < nhchunks; j++) {
+               uint64_t nmalloc, ndalloc, nrequests;
+               size_t hchunk_size, curhchunks;
+
+               CTL_M2_M4_GET("stats.arenas.0.hchunks.0.nmalloc", i, j,
+                   &nmalloc, uint64_t);
+               CTL_M2_M4_GET("stats.arenas.0.hchunks.0.ndalloc", i, j,
+                   &ndalloc, uint64_t);
+               CTL_M2_M4_GET("stats.arenas.0.hchunks.0.nrequests", i, j,
+                   &nrequests, uint64_t);
+               if (nrequests == 0)
+                       in_gap = true;
+               else {
+                       CTL_M2_GET("arenas.hchunk.0.size", j, &hchunk_size,
                            size_t);
-                       if (gap_start != -1) {
-                               malloc_cprintf(write_cb, cbopaque, "[%zu]\n",
-                                   j - gap_start);
-                               gap_start = -1;
+                       CTL_M2_M4_GET("stats.arenas.0.hchunks.0.curhchunks", i,
+                           j, &curhchunks, size_t);
+                       if (in_gap) {
+                               malloc_cprintf(write_cb, cbopaque,
+                                   "                     ---\n");
+                               in_gap = false;
                        }
                        malloc_cprintf(write_cb, cbopaque,
-                           "%13zu %5zu %12"PRIu64" %12"PRIu64" %12"PRIu64
-                           " %12zu\n",
-                           run_size, run_size / page, nmalloc, ndalloc,
-                           nrequests, curruns);
+                           "%20zu %3u %12zu %12"FMTu64" %12"FMTu64
+                           " %12"FMTu64" %12zu\n",
+                           hchunk_size, nbins + nlruns + j,
+                           curhchunks * hchunk_size, nmalloc, ndalloc,
+                           nrequests, curhchunks);
                }
        }
-       if (gap_start != -1)
-               malloc_cprintf(write_cb, cbopaque, "[%zu]\n", j - gap_start);
+       if (in_gap) {
+               malloc_cprintf(write_cb, cbopaque,
+                   "                     ---\n");
+       }
 }
 
 static void
 stats_arena_print(void (*write_cb)(void *, const char *), void *cbopaque,
-    unsigned i, bool bins, bool large)
+    unsigned i, bool bins, bool large, bool huge)
 {
        unsigned nthreads;
        const char *dss;
+       ssize_t lg_dirty_mult;
        size_t page, pactive, pdirty, mapped;
+       size_t metadata_mapped, metadata_allocated;
        uint64_t npurge, nmadvise, purged;
        size_t small_allocated;
        uint64_t small_nmalloc, small_ndalloc, small_nrequests;
@@ -218,60 +271,89 @@ stats_arena_print(void (*write_cb)(void *, const char *), void *cbopaque,
 
        CTL_GET("arenas.page", &page, size_t);
 
-       CTL_I_GET("stats.arenas.0.nthreads", &nthreads, unsigned);
+       CTL_M2_GET("stats.arenas.0.nthreads", i, &nthreads, unsigned);
        malloc_cprintf(write_cb, cbopaque,
            "assigned threads: %u\n", nthreads);
-       CTL_I_GET("stats.arenas.0.dss", &dss, const char *);
+       CTL_M2_GET("stats.arenas.0.dss", i, &dss, const char *);
        malloc_cprintf(write_cb, cbopaque, "dss allocation precedence: %s\n",
            dss);
-       CTL_I_GET("stats.arenas.0.pactive", &pactive, size_t);
-       CTL_I_GET("stats.arenas.0.pdirty", &pdirty, size_t);
-       CTL_I_GET("stats.arenas.0.npurge", &npurge, uint64_t);
-       CTL_I_GET("stats.arenas.0.nmadvise", &nmadvise, uint64_t);
-       CTL_I_GET("stats.arenas.0.purged", &purged, uint64_t);
+       CTL_M2_GET("stats.arenas.0.lg_dirty_mult", i, &lg_dirty_mult, ssize_t);
+       if (lg_dirty_mult >= 0) {
+               malloc_cprintf(write_cb, cbopaque,
+                   "min active:dirty page ratio: %u:1\n",
+                   (1U << lg_dirty_mult));
+       } else {
+               malloc_cprintf(write_cb, cbopaque,
+                   "min active:dirty page ratio: N/A\n");
+       }
+       CTL_M2_GET("stats.arenas.0.pactive", i, &pactive, size_t);
+       CTL_M2_GET("stats.arenas.0.pdirty", i, &pdirty, size_t);
+       CTL_M2_GET("stats.arenas.0.npurge", i, &npurge, uint64_t);
+       CTL_M2_GET("stats.arenas.0.nmadvise", i, &nmadvise, uint64_t);
+       CTL_M2_GET("stats.arenas.0.purged", i, &purged, uint64_t);
        malloc_cprintf(write_cb, cbopaque,
-           "dirty pages: %zu:%zu active:dirty, %"PRIu64" sweep%s,"
-           " %"PRIu64" madvise%s, %"PRIu64" purged\n",
-           pactive, pdirty, npurge, npurge == 1 ? "" : "s",
-           nmadvise, nmadvise == 1 ? "" : "s", purged);
+           "dirty pages: %zu:%zu active:dirty, %"FMTu64" sweep%s, %"FMTu64
+           " madvise%s, %"FMTu64" purged\n", pactive, pdirty, npurge, npurge ==
+           1 ? "" : "s", nmadvise, nmadvise == 1 ? "" : "s", purged);
 
        malloc_cprintf(write_cb, cbopaque,
-           "            allocated      nmalloc      ndalloc    nrequests\n");
-       CTL_I_GET("stats.arenas.0.small.allocated", &small_allocated, size_t);
-       CTL_I_GET("stats.arenas.0.small.nmalloc", &small_nmalloc, uint64_t);
-       CTL_I_GET("stats.arenas.0.small.ndalloc", &small_ndalloc, uint64_t);
-       CTL_I_GET("stats.arenas.0.small.nrequests", &small_nrequests, uint64_t);
+           "                            allocated      nmalloc      ndalloc"
+           "    nrequests\n");
+       CTL_M2_GET("stats.arenas.0.small.allocated", i, &small_allocated,
+           size_t);
+       CTL_M2_GET("stats.arenas.0.small.nmalloc", i, &small_nmalloc, uint64_t);
+       CTL_M2_GET("stats.arenas.0.small.ndalloc", i, &small_ndalloc, uint64_t);
+       CTL_M2_GET("stats.arenas.0.small.nrequests", i, &small_nrequests,
+           uint64_t);
        malloc_cprintf(write_cb, cbopaque,
-           "small:   %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n",
+           "small:                   %12zu %12"FMTu64" %12"FMTu64
+           " %12"FMTu64"\n",
            small_allocated, small_nmalloc, small_ndalloc, small_nrequests);
-       CTL_I_GET("stats.arenas.0.large.allocated", &large_allocated, size_t);
-       CTL_I_GET("stats.arenas.0.large.nmalloc", &large_nmalloc, uint64_t);
-       CTL_I_GET("stats.arenas.0.large.ndalloc", &large_ndalloc, uint64_t);
-       CTL_I_GET("stats.arenas.0.large.nrequests", &large_nrequests, uint64_t);
+       CTL_M2_GET("stats.arenas.0.large.allocated", i, &large_allocated,
+           size_t);
+       CTL_M2_GET("stats.arenas.0.large.nmalloc", i, &large_nmalloc, uint64_t);
+       CTL_M2_GET("stats.arenas.0.large.ndalloc", i, &large_ndalloc, uint64_t);
+       CTL_M2_GET("stats.arenas.0.large.nrequests", i, &large_nrequests,
+           uint64_t);
        malloc_cprintf(write_cb, cbopaque,
-           "large:   %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n",
+           "large:                   %12zu %12"FMTu64" %12"FMTu64
+           " %12"FMTu64"\n",
            large_allocated, large_nmalloc, large_ndalloc, large_nrequests);
-       CTL_I_GET("stats.arenas.0.huge.allocated", &huge_allocated, size_t);
-       CTL_I_GET("stats.arenas.0.huge.nmalloc", &huge_nmalloc, uint64_t);
-       CTL_I_GET("stats.arenas.0.huge.ndalloc", &huge_ndalloc, uint64_t);
-       CTL_I_GET("stats.arenas.0.huge.nrequests", &huge_nrequests, uint64_t);
+       CTL_M2_GET("stats.arenas.0.huge.allocated", i, &huge_allocated, size_t);
+       CTL_M2_GET("stats.arenas.0.huge.nmalloc", i, &huge_nmalloc, uint64_t);
+       CTL_M2_GET("stats.arenas.0.huge.ndalloc", i, &huge_ndalloc, uint64_t);
+       CTL_M2_GET("stats.arenas.0.huge.nrequests", i, &huge_nrequests,
+           uint64_t);
        malloc_cprintf(write_cb, cbopaque,
-           "huge:    %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n",
+           "huge:                    %12zu %12"FMTu64" %12"FMTu64
+           " %12"FMTu64"\n",
            huge_allocated, huge_nmalloc, huge_ndalloc, huge_nrequests);
        malloc_cprintf(write_cb, cbopaque,
-           "total:   %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n",
+           "total:                   %12zu %12"FMTu64" %12"FMTu64
+           " %12"FMTu64"\n",
            small_allocated + large_allocated + huge_allocated,
            small_nmalloc + large_nmalloc + huge_nmalloc,
            small_ndalloc + large_ndalloc + huge_ndalloc,
            small_nrequests + large_nrequests + huge_nrequests);
-       malloc_cprintf(write_cb, cbopaque, "active:  %12zu\n", pactive * page);
-       CTL_I_GET("stats.arenas.0.mapped", &mapped, size_t);
-       malloc_cprintf(write_cb, cbopaque, "mapped:  %12zu\n", mapped);
+       malloc_cprintf(write_cb, cbopaque,
+           "active:                  %12zu\n", pactive * page);
+       CTL_M2_GET("stats.arenas.0.mapped", i, &mapped, size_t);
+       malloc_cprintf(write_cb, cbopaque,
+           "mapped:                  %12zu\n", mapped);
+       CTL_M2_GET("stats.arenas.0.metadata.mapped", i, &metadata_mapped,
+           size_t);
+       CTL_M2_GET("stats.arenas.0.metadata.allocated", i, &metadata_allocated,
+           size_t);
+       malloc_cprintf(write_cb, cbopaque,
+           "metadata: mapped: %zu, allocated: %zu\n",
+           metadata_mapped, metadata_allocated);
 
        if (bins)
                stats_arena_bins_print(write_cb, cbopaque, i);
        if (large)
                stats_arena_lruns_print(write_cb, cbopaque, i);
+       if (huge)
+               stats_arena_hchunks_print(write_cb, cbopaque, i);
 }
 
 void
@@ -286,6 +368,7 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
        bool unmerged = true;
        bool bins = true;
        bool large = true;
+       bool huge = true;
 
        /*
         * Refresh stats, in case mallctl() was called by the application.
@@ -328,6 +411,9 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
                        case 'l':
                                large = false;
                                break;
+                       case 'h':
+                               huge = false;
+                               break;
                        default:;
                        }
                }
@@ -377,6 +463,15 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
                        malloc_cprintf(write_cb, cbopaque,              \
                            "  opt."#n": %zd\n", ssv);                  \
                }
+#define        OPT_WRITE_SSIZE_T_MUTABLE(n, m) {                               \
+               ssize_t ssv2;                                           \
+               if (je_mallctl("opt."#n, &ssv, &sssz, NULL, 0) == 0 &&  \
+                   je_mallctl(#m, &ssv2, &sssz, NULL, 0) == 0) {       \
+                       malloc_cprintf(write_cb, cbopaque,              \
+                           "  opt."#n": %zd ("#m": %zd)\n",            \
+                           ssv, ssv2);                                 \
+               }                                                       \
+}
 #define        OPT_WRITE_CHAR_P(n)                                             \
                if (je_mallctl("opt."#n, &cpv, &cpsz, NULL, 0) == 0) {  \
                        malloc_cprintf(write_cb, cbopaque,              \
@@ -389,9 +484,9 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
                OPT_WRITE_SIZE_T(lg_chunk)
                OPT_WRITE_CHAR_P(dss)
                OPT_WRITE_SIZE_T(narenas)
-               OPT_WRITE_SSIZE_T(lg_dirty_mult)
+               OPT_WRITE_SSIZE_T_MUTABLE(lg_dirty_mult, arenas.lg_dirty_mult)
                OPT_WRITE_BOOL(stats_print)
-               OPT_WRITE_BOOL(junk)
+               OPT_WRITE_CHAR_P(junk)
                OPT_WRITE_SIZE_T(quarantine)
                OPT_WRITE_BOOL(redzone)
                OPT_WRITE_BOOL(zero)
@@ -427,12 +522,13 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
                    sizeof(void *));
 
                CTL_GET("arenas.quantum", &sv, size_t);
-               malloc_cprintf(write_cb, cbopaque, "Quantum size: %zu\n", sv);
+               malloc_cprintf(write_cb, cbopaque, "Quantum size: %zu\n",
+                   sv);
 
                CTL_GET("arenas.page", &sv, size_t);
                malloc_cprintf(write_cb, cbopaque, "Page size: %zu\n", sv);
 
-               CTL_GET("opt.lg_dirty_mult", &ssv, ssize_t);
+               CTL_GET("arenas.lg_dirty_mult", &ssv, ssize_t);
                if (ssv >= 0) {
                        malloc_cprintf(write_cb, cbopaque,
                            "Min active:dirty page ratio per arena: %u:1\n",
@@ -448,13 +544,13 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
                if (je_mallctl("opt.prof", &bv, &bsz, NULL, 0) == 0 && bv) {
                        CTL_GET("prof.lg_sample", &sv, size_t);
                        malloc_cprintf(write_cb, cbopaque,
-                           "Average profile sample interval: %"PRIu64
+                           "Average profile sample interval: %"FMTu64
                            " (2^%zu)\n", (((uint64_t)1U) << sv), sv);
 
                        CTL_GET("opt.lg_prof_interval", &ssv, ssize_t);
                        if (ssv >= 0) {
                                malloc_cprintf(write_cb, cbopaque,
-                                   "Average profile dump interval: %"PRIu64
+                                   "Average profile dump interval: %"FMTu64
                                    " (2^%zd)\n",
                                    (((uint64_t)1U) << ssv), ssv);
                        } else {
@@ -463,35 +559,27 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
                        }
                }
                CTL_GET("opt.lg_chunk", &sv, size_t);
-               malloc_cprintf(write_cb, cbopaque, "Chunk size: %zu (2^%zu)\n",
-                   (ZU(1) << sv), sv);
+               malloc_cprintf(write_cb, cbopaque,
+                   "Chunk size: %zu (2^%zu)\n", (ZU(1) << sv), sv);
        }
 
        if (config_stats) {
                size_t *cactive;
-               size_t allocated, active, mapped;
-               size_t chunks_current, chunks_high;
-               uint64_t chunks_total;
+               size_t allocated, active, metadata, resident, mapped;
 
                CTL_GET("stats.cactive", &cactive, size_t *);
                CTL_GET("stats.allocated", &allocated, size_t);
                CTL_GET("stats.active", &active, size_t);
+               CTL_GET("stats.metadata", &metadata, size_t);
+               CTL_GET("stats.resident", &resident, size_t);
                CTL_GET("stats.mapped", &mapped, size_t);
                malloc_cprintf(write_cb, cbopaque,
-                   "Allocated: %zu, active: %zu, mapped: %zu\n",
-                   allocated, active, mapped);
-               malloc_cprintf(write_cb, cbopaque,
-                   "Current active ceiling: %zu\n", atomic_read_z(cactive));
-
-               /* Print chunk stats. */
-               CTL_GET("stats.chunks.total", &chunks_total, uint64_t);
-               CTL_GET("stats.chunks.high", &chunks_high, size_t);
-               CTL_GET("stats.chunks.current", &chunks_current, size_t);
-               malloc_cprintf(write_cb, cbopaque, "chunks: nchunks   "
-                   "highchunks    curchunks\n");
+                   "Allocated: %zu, active: %zu, metadata: %zu,"
+                   " resident: %zu, mapped: %zu\n",
+                   allocated, active, metadata, resident, mapped);
                malloc_cprintf(write_cb, cbopaque,
-                   "  %13"PRIu64" %12zu %12zu\n",
-                   chunks_total, chunks_high, chunks_current);
+                   "Current active ceiling: %zu\n",
+                   atomic_read_z(cactive));
 
                if (merged) {
                        unsigned narenas;
@@ -515,7 +603,7 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
                                        malloc_cprintf(write_cb, cbopaque,
                                            "\nMerged arenas stats:\n");
                                        stats_arena_print(write_cb, cbopaque,
-                                           narenas, bins, large);
+                                           narenas, bins, large, huge);
                                }
                        }
                }
@@ -541,7 +629,8 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
                                                    cbopaque,
                                                    "\narenas[%u]:\n", i);
                                                stats_arena_print(write_cb,
-                                                   cbopaque, i, bins, large);
+                                                   cbopaque, i, bins, large,
+                                                   huge);
                                        }
                                }
                        }
index 07167b6d3c318b92676e5b3d5f4b7f7d47b65c3c..fdafd0c620a6b09a916c93a8200f2ae5e6329293 100644 (file)
@@ -13,6 +13,14 @@ static unsigned              stack_nelms; /* Total stack elms per tcache. */
 size_t                 nhbins;
 size_t                 tcache_maxclass;
 
+tcaches_t              *tcaches;
+
+/* Index of first element within tcaches that has never been used. */
+static unsigned                tcaches_past;
+
+/* Head of singly linked list tracking available tcaches elements. */
+static tcaches_t       *tcaches_avail;
+
 /******************************************************************************/
 
 size_t tcache_salloc(const void *ptr)
@@ -22,9 +30,9 @@ size_t        tcache_salloc(const void *ptr)
 }
 
 void
-tcache_event_hard(tcache_t *tcache)
+tcache_event_hard(tsd_t *tsd, tcache_t *tcache)
 {
-       size_t binind = tcache->next_gc_bin;
+       szind_t binind = tcache->next_gc_bin;
        tcache_bin_t *tbin = &tcache->tbins[binind];
        tcache_bin_info_t *tbin_info = &tcache_bin_info[binind];
 
@@ -33,11 +41,12 @@ tcache_event_hard(tcache_t *tcache)
                 * Flush (ceiling) 3/4 of the objects below the low water mark.
                 */
                if (binind < NBINS) {
-                       tcache_bin_flush_small(tbin, binind, tbin->ncached -
-                           tbin->low_water + (tbin->low_water >> 2), tcache);
+                       tcache_bin_flush_small(tsd, tcache, tbin, binind,
+                           tbin->ncached - tbin->low_water + (tbin->low_water
+                           >> 2));
                } else {
-                       tcache_bin_flush_large(tbin, binind, tbin->ncached -
-                           tbin->low_water + (tbin->low_water >> 2), tcache);
+                       tcache_bin_flush_large(tsd, tbin, binind, tbin->ncached
+                           tbin->low_water + (tbin->low_water >> 2), tcache);
                }
                /*
                 * Reduce fill count by 2X.  Limit lg_fill_div such that the
@@ -62,12 +71,13 @@ tcache_event_hard(tcache_t *tcache)
 }
 
 void *
-tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin, size_t binind)
+tcache_alloc_small_hard(tsd_t *tsd, arena_t *arena, tcache_t *tcache,
+    tcache_bin_t *tbin, szind_t binind)
 {
        void *ret;
 
-       arena_tcache_fill_small(tcache->arena, tbin, binind,
-           config_prof ? tcache->prof_accumbytes : 0);
+       arena_tcache_fill_small(arena, tbin, binind, config_prof ?
+           tcache->prof_accumbytes : 0);
        if (config_prof)
                tcache->prof_accumbytes = 0;
        ret = tcache_alloc_easy(tbin);
@@ -76,9 +86,10 @@ tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin, size_t binind)
 }
 
 void
-tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem,
-    tcache_t *tcache)
+tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, tcache_bin_t *tbin,
+    szind_t binind, unsigned rem)
 {
+       arena_t *arena;
        void *ptr;
        unsigned i, nflush, ndeferred;
        bool merged_stats = false;
@@ -86,21 +97,23 @@ tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem,
        assert(binind < NBINS);
        assert(rem <= tbin->ncached);
 
+       arena = arena_choose(tsd, NULL);
+       assert(arena != NULL);
        for (nflush = tbin->ncached - rem; nflush > 0; nflush = ndeferred) {
                /* Lock the arena bin associated with the first object. */
                arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(
                    tbin->avail[0]);
-               arena_t *arena = chunk->arena;
-               arena_bin_t *bin = &arena->bins[binind];
+               arena_t *bin_arena = extent_node_arena_get(&chunk->node);
+               arena_bin_t *bin = &bin_arena->bins[binind];
 
-               if (config_prof && arena == tcache->arena) {
+               if (config_prof && bin_arena == arena) {
                        if (arena_prof_accum(arena, tcache->prof_accumbytes))
                                prof_idump();
                        tcache->prof_accumbytes = 0;
                }
 
                malloc_mutex_lock(&bin->lock);
-               if (config_stats && arena == tcache->arena) {
+               if (config_stats && bin_arena == arena) {
                        assert(!merged_stats);
                        merged_stats = true;
                        bin->stats.nflushes++;
@@ -112,13 +125,13 @@ tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem,
                        ptr = tbin->avail[i];
                        assert(ptr != NULL);
                        chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-                       if (chunk->arena == arena) {
+                       if (extent_node_arena_get(&chunk->node) == bin_arena) {
                                size_t pageind = ((uintptr_t)ptr -
                                    (uintptr_t)chunk) >> LG_PAGE;
                                arena_chunk_map_bits_t *bitselm =
                                    arena_bitselm_get(chunk, pageind);
-                               arena_dalloc_bin_locked(arena, chunk, ptr,
-                                   bitselm);
+                               arena_dalloc_bin_junked_locked(bin_arena, chunk,
+                                   ptr, bitselm);
                        } else {
                                /*
                                 * This object was allocated via a different
@@ -137,7 +150,7 @@ tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem,
                 * The flush loop didn't happen to flush to this thread's
                 * arena, so the stats didn't get merged.  Manually do so now.
                 */
-               arena_bin_t *bin = &tcache->arena->bins[binind];
+               arena_bin_t *bin = &arena->bins[binind];
                malloc_mutex_lock(&bin->lock);
                bin->stats.nflushes++;
                bin->stats.nrequests += tbin->tstats.nrequests;
@@ -153,9 +166,10 @@ tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem,
 }
 
 void
-tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem,
-    tcache_t *tcache)
+tcache_bin_flush_large(tsd_t *tsd, tcache_bin_t *tbin, szind_t binind,
+    unsigned rem, tcache_t *tcache)
 {
+       arena_t *arena;
        void *ptr;
        unsigned i, nflush, ndeferred;
        bool merged_stats = false;
@@ -163,17 +177,19 @@ tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem,
        assert(binind < nhbins);
        assert(rem <= tbin->ncached);
 
+       arena = arena_choose(tsd, NULL);
+       assert(arena != NULL);
        for (nflush = tbin->ncached - rem; nflush > 0; nflush = ndeferred) {
                /* Lock the arena associated with the first object. */
                arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(
                    tbin->avail[0]);
-               arena_t *arena = chunk->arena;
+               arena_t *locked_arena = extent_node_arena_get(&chunk->node);
                UNUSED bool idump;
 
                if (config_prof)
                        idump = false;
-               malloc_mutex_lock(&arena->lock);
-               if ((config_prof || config_stats) && arena == tcache->arena) {
+               malloc_mutex_lock(&locked_arena->lock);
+               if ((config_prof || config_stats) && locked_arena == arena) {
                        if (config_prof) {
                                idump = arena_prof_accum_locked(arena,
                                    tcache->prof_accumbytes);
@@ -193,9 +209,11 @@ tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem,
                        ptr = tbin->avail[i];
                        assert(ptr != NULL);
                        chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
-                       if (chunk->arena == arena)
-                               arena_dalloc_large_locked(arena, chunk, ptr);
-                       else {
+                       if (extent_node_arena_get(&chunk->node) ==
+                           locked_arena) {
+                               arena_dalloc_large_junked_locked(locked_arena,
+                                   chunk, ptr);
+                       } else {
                                /*
                                 * This object was allocated via a different
                                 * arena than the one that is currently locked.
@@ -206,7 +224,7 @@ tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem,
                                ndeferred++;
                        }
                }
-               malloc_mutex_unlock(&arena->lock);
+               malloc_mutex_unlock(&locked_arena->lock);
                if (config_prof && idump)
                        prof_idump();
        }
@@ -215,7 +233,6 @@ tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem,
                 * The flush loop didn't happen to flush to this thread's
                 * arena, so the stats didn't get merged.  Manually do so now.
                 */
-               arena_t *arena = tcache->arena;
                malloc_mutex_lock(&arena->lock);
                arena->stats.nrequests_large += tbin->tstats.nrequests;
                arena->stats.lstats[binind - NBINS].nrequests +=
@@ -242,36 +259,58 @@ tcache_arena_associate(tcache_t *tcache, arena_t *arena)
                ql_tail_insert(&arena->tcache_ql, tcache, link);
                malloc_mutex_unlock(&arena->lock);
        }
-       tcache->arena = arena;
 }
 
 void
-tcache_arena_dissociate(tcache_t *tcache)
+tcache_arena_reassociate(tcache_t *tcache, arena_t *oldarena, arena_t *newarena)
+{
+
+       tcache_arena_dissociate(tcache, oldarena);
+       tcache_arena_associate(tcache, newarena);
+}
+
+void
+tcache_arena_dissociate(tcache_t *tcache, arena_t *arena)
 {
 
        if (config_stats) {
                /* Unlink from list of extant tcaches. */
-               malloc_mutex_lock(&tcache->arena->lock);
-               ql_remove(&tcache->arena->tcache_ql, tcache, link);
-               tcache_stats_merge(tcache, tcache->arena);
-               malloc_mutex_unlock(&tcache->arena->lock);
+               malloc_mutex_lock(&arena->lock);
+               if (config_debug) {
+                       bool in_ql = false;
+                       tcache_t *iter;
+                       ql_foreach(iter, &arena->tcache_ql, link) {
+                               if (iter == tcache) {
+                                       in_ql = true;
+                                       break;
+                               }
+                       }
+                       assert(in_ql);
+               }
+               ql_remove(&arena->tcache_ql, tcache, link);
+               tcache_stats_merge(tcache, arena);
+               malloc_mutex_unlock(&arena->lock);
        }
 }
 
 tcache_t *
 tcache_get_hard(tsd_t *tsd)
 {
+       arena_t *arena;
 
        if (!tcache_enabled_get()) {
                if (tsd_nominal(tsd))
                        tcache_enabled_set(false); /* Memoize. */
                return (NULL);
        }
-       return (tcache_create(choose_arena(tsd, NULL)));
+       arena = arena_choose(tsd, NULL);
+       if (unlikely(arena == NULL))
+               return (NULL);
+       return (tcache_create(tsd, arena));
 }
 
 tcache_t *
-tcache_create(arena_t *arena)
+tcache_create(tsd_t *tsd, arena_t *arena)
 {
        tcache_t *tcache;
        size_t size, stack_offset;
@@ -282,23 +321,10 @@ tcache_create(arena_t *arena)
        size = PTR_CEILING(size);
        stack_offset = size;
        size += stack_nelms * sizeof(void *);
-       /*
-        * Round up to the nearest multiple of the cacheline size, in order to
-        * avoid the possibility of false cacheline sharing.
-        *
-        * That this works relies on the same logic as in ipalloc(), but we
-        * cannot directly call ipalloc() here due to tcache bootstrapping
-        * issues.
-        */
-       size = (size + CACHELINE_MASK) & (-CACHELINE);
-
-       if (size <= SMALL_MAXCLASS)
-               tcache = (tcache_t *)arena_malloc_small(arena, size, true);
-       else if (size <= tcache_maxclass)
-               tcache = (tcache_t *)arena_malloc_large(arena, size, true);
-       else
-               tcache = (tcache_t *)icalloct(NULL, size, false, arena);
+       /* Avoid false cacheline sharing. */
+       size = sa2u(size, CACHELINE);
 
+       tcache = ipallocztm(tsd, size, CACHELINE, true, false, true, a0get());
        if (tcache == NULL)
                return (NULL);
 
@@ -318,17 +344,17 @@ tcache_create(arena_t *arena)
 static void
 tcache_destroy(tsd_t *tsd, tcache_t *tcache)
 {
+       arena_t *arena;
        unsigned i;
-       size_t tcache_size;
 
-       tcache_arena_dissociate(tcache);
+       arena = arena_choose(tsd, NULL);
+       tcache_arena_dissociate(tcache, arena);
 
        for (i = 0; i < NBINS; i++) {
                tcache_bin_t *tbin = &tcache->tbins[i];
-               tcache_bin_flush_small(tbin, i, 0, tcache);
+               tcache_bin_flush_small(tsd, tcache, tbin, i, 0);
 
                if (config_stats && tbin->tstats.nrequests != 0) {
-                       arena_t *arena = tcache->arena;
                        arena_bin_t *bin = &arena->bins[i];
                        malloc_mutex_lock(&bin->lock);
                        bin->stats.nrequests += tbin->tstats.nrequests;
@@ -338,10 +364,9 @@ tcache_destroy(tsd_t *tsd, tcache_t *tcache)
 
        for (; i < nhbins; i++) {
                tcache_bin_t *tbin = &tcache->tbins[i];
-               tcache_bin_flush_large(tbin, i, 0, tcache);
+               tcache_bin_flush_large(tsd, tbin, i, 0, tcache);
 
                if (config_stats && tbin->tstats.nrequests != 0) {
-                       arena_t *arena = tcache->arena;
                        malloc_mutex_lock(&arena->lock);
                        arena->stats.nrequests_large += tbin->tstats.nrequests;
                        arena->stats.lstats[i - NBINS].nrequests +=
@@ -351,26 +376,10 @@ tcache_destroy(tsd_t *tsd, tcache_t *tcache)
        }
 
        if (config_prof && tcache->prof_accumbytes > 0 &&
-           arena_prof_accum(tcache->arena, tcache->prof_accumbytes))
+           arena_prof_accum(arena, tcache->prof_accumbytes))
                prof_idump();
 
-       tcache_size = arena_salloc(tcache, false);
-       if (tcache_size <= SMALL_MAXCLASS) {
-               arena_chunk_t *chunk = CHUNK_ADDR2BASE(tcache);
-               arena_t *arena = chunk->arena;
-               size_t pageind = ((uintptr_t)tcache - (uintptr_t)chunk) >>
-                   LG_PAGE;
-               arena_chunk_map_bits_t *bitselm = arena_bitselm_get(chunk,
-                   pageind);
-
-               arena_dalloc_bin(arena, chunk, tcache, pageind, bitselm);
-       } else if (tcache_size <= tcache_maxclass) {
-               arena_chunk_t *chunk = CHUNK_ADDR2BASE(tcache);
-               arena_t *arena = chunk->arena;
-
-               arena_dalloc_large(arena, chunk, tcache);
-       } else
-               idalloct(tsd, tcache, false);
+       idalloctm(tsd, tcache, false, true);
 }
 
 void
@@ -421,23 +430,83 @@ tcache_stats_merge(tcache_t *tcache, arena_t *arena)
        }
 }
 
+bool
+tcaches_create(tsd_t *tsd, unsigned *r_ind)
+{
+       tcache_t *tcache;
+       tcaches_t *elm;
+
+       if (tcaches == NULL) {
+               tcaches = base_alloc(sizeof(tcache_t *) *
+                   (MALLOCX_TCACHE_MAX+1));
+               if (tcaches == NULL)
+                       return (true);
+       }
+
+       if (tcaches_avail == NULL && tcaches_past > MALLOCX_TCACHE_MAX)
+               return (true);
+       tcache = tcache_create(tsd, a0get());
+       if (tcache == NULL)
+               return (true);
+
+       if (tcaches_avail != NULL) {
+               elm = tcaches_avail;
+               tcaches_avail = tcaches_avail->next;
+               elm->tcache = tcache;
+               *r_ind = elm - tcaches;
+       } else {
+               elm = &tcaches[tcaches_past];
+               elm->tcache = tcache;
+               *r_ind = tcaches_past;
+               tcaches_past++;
+       }
+
+       return (false);
+}
+
+static void
+tcaches_elm_flush(tsd_t *tsd, tcaches_t *elm)
+{
+
+       if (elm->tcache == NULL)
+               return;
+       tcache_destroy(tsd, elm->tcache);
+       elm->tcache = NULL;
+}
+
+void
+tcaches_flush(tsd_t *tsd, unsigned ind)
+{
+
+       tcaches_elm_flush(tsd, &tcaches[ind]);
+}
+
+void
+tcaches_destroy(tsd_t *tsd, unsigned ind)
+{
+       tcaches_t *elm = &tcaches[ind];
+       tcaches_elm_flush(tsd, elm);
+       elm->next = tcaches_avail;
+       tcaches_avail = elm;
+}
+
 bool
 tcache_boot(void)
 {
        unsigned i;
 
        /*
-        * If necessary, clamp opt_lg_tcache_max, now that arena_maxclass is
+        * If necessary, clamp opt_lg_tcache_max, now that large_maxclass is
         * known.
         */
        if (opt_lg_tcache_max < 0 || (1U << opt_lg_tcache_max) < SMALL_MAXCLASS)
                tcache_maxclass = SMALL_MAXCLASS;
-       else if ((1U << opt_lg_tcache_max) > arena_maxclass)
-               tcache_maxclass = arena_maxclass;
+       else if ((1U << opt_lg_tcache_max) > large_maxclass)
+               tcache_maxclass = large_maxclass;
        else
                tcache_maxclass = (1U << opt_lg_tcache_max);
 
-       nhbins = NBINS + (tcache_maxclass >> LG_PAGE);
+       nhbins = size2index(tcache_maxclass) + 1;
 
        /* Initialize tcache_bin_info. */
        tcache_bin_info = (tcache_bin_info_t *)base_alloc(nhbins *
@@ -446,7 +515,11 @@ tcache_boot(void)
                return (true);
        stack_nelms = 0;
        for (i = 0; i < NBINS; i++) {
-               if ((arena_bin_info[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MAX) {
+               if ((arena_bin_info[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MIN) {
+                       tcache_bin_info[i].ncached_max =
+                           TCACHE_NSLOTS_SMALL_MIN;
+               } else if ((arena_bin_info[i].nregs << 1) <=
+                   TCACHE_NSLOTS_SMALL_MAX) {
                        tcache_bin_info[i].ncached_max =
                            (arena_bin_info[i].nregs << 1);
                } else {
index cbc64e44eaba030325a74b5d1f8023789225a978..9ffe9afef7a07230fe02bd3d7afc8d21ef9ad7df 100644 (file)
@@ -15,16 +15,14 @@ void *
 malloc_tsd_malloc(size_t size)
 {
 
-       /* Avoid choose_arena() in order to dodge bootstrapping issues. */
-       return (arena_malloc(NULL, arenas[0], CACHELINE_CEILING(size), false,
-           false));
+       return (a0malloc(CACHELINE_CEILING(size)));
 }
 
 void
 malloc_tsd_dalloc(void *wrapper)
 {
 
-       idalloct(NULL, wrapper, false);
+       a0dalloc(wrapper);
 }
 
 void
@@ -75,6 +73,9 @@ tsd_cleanup(void *arg)
        tsd_t *tsd = (tsd_t *)arg;
 
        switch (tsd->state) {
+       case tsd_state_uninitialized:
+               /* Do nothing. */
+               break;
        case tsd_state_nominal:
 #define O(n, t)                                                                \
                n##_cleanup(tsd);
@@ -106,15 +107,24 @@ MALLOC_TSD
 }
 
 bool
-malloc_tsd_boot(void)
+malloc_tsd_boot0(void)
 {
 
        ncleanups = 0;
-       if (tsd_boot())
+       if (tsd_boot0())
                return (true);
+       *tsd_arenas_cache_bypassp_get(tsd_fetch()) = true;
        return (false);
 }
 
+void
+malloc_tsd_boot1(void)
+{
+
+       tsd_boot1();
+       *tsd_arenas_cache_bypassp_get(tsd_fetch()) = false;
+}
+
 #ifdef _WIN32
 static BOOL WINAPI
 _tls_callback(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
@@ -144,7 +154,7 @@ _tls_callback(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
 #  pragma section(".CRT$XLY",long,read)
 #endif
 JEMALLOC_SECTION(".CRT$XLY") JEMALLOC_ATTR(used)
-static const BOOL      (WINAPI *tls_callback)(HINSTANCE hinstDLL,
+static BOOL    (WINAPI *const tls_callback)(HINSTANCE hinstDLL,
     DWORD fdwReason, LPVOID lpvReserved) = _tls_callback;
 #endif
 
index bfd86af8d8ce6ef601869693e45de41df9c16791..4cb0d6c1e68525dc532068c697d94a1ba4fd930d 100644 (file)
@@ -81,10 +81,10 @@ buferror(int err, char *buf, size_t buflen)
 {
 
 #ifdef _WIN32
-       FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
+       FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0,
            (LPSTR)buf, buflen, NULL);
        return (0);
-#elif defined(_GNU_SOURCE)
+#elif defined(__GLIBC__) && defined(_GNU_SOURCE)
        char *b = strerror_r(err, buf, buflen);
        if (b != buf) {
                strncpy(buf, b, buflen);
@@ -586,7 +586,7 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
        return (ret);
 }
 
-JEMALLOC_ATTR(format(printf, 3, 4))
+JEMALLOC_FORMAT_PRINTF(3, 4)
 int
 malloc_snprintf(char *str, size_t size, const char *format, ...)
 {
@@ -625,7 +625,7 @@ malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
  * Print to a callback function in such a way as to (hopefully) avoid memory
  * allocation.
  */
-JEMALLOC_ATTR(format(printf, 3, 4))
+JEMALLOC_FORMAT_PRINTF(3, 4)
 void
 malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,
     const char *format, ...)
@@ -638,7 +638,7 @@ malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,
 }
 
 /* Print to stderr in such a way as to avoid memory allocation. */
-JEMALLOC_ATTR(format(printf, 1, 2))
+JEMALLOC_FORMAT_PRINTF(1, 2)
 void
 malloc_printf(const char *format, ...)
 {
index c6bd533fcc2fd4da0f3f995c6c3b9bd82f2e8756..12e1734a9ebb2f088b64990f48e8fe5c2c6689ba 100644 (file)
@@ -263,7 +263,7 @@ register_zone(void)
                 * after the default zone.  On OSX < 10.6, there is no purgeable
                 * zone, so this does nothing.  On OSX >= 10.6, unregistering
                 * replaces the purgeable zone with the last registered zone
-                * above, i.e the default zone.  Registering it again then puts
+                * above, i.e. the default zone.  Registering it again then puts
                 * it at the end, obviously after the default zone.
                 */
                if (purgeable_zone) {
index 6018e58ac12dac4112a95f1f772b91def0e85b34..455569da43a34b875aa9863a22e2d8ef132e9e1b 100644 (file)
@@ -1,14 +1,21 @@
+#include <limits.h>
+#ifndef SIZE_T_MAX
+#  define SIZE_T_MAX   SIZE_MAX
+#endif
 #include <stdlib.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <errno.h>
-#include <inttypes.h>
 #include <math.h>
 #include <string.h>
+#ifdef _WIN32
+#  include "msvc_compat/strings.h"
+#endif
 #include <sys/time.h>
 
 #ifdef _WIN32
 #  include <windows.h>
+#  include "msvc_compat/windows_extra.h"
 #else
 #  include <pthread.h>
 #endif
index aaaaec14b2cb890736d56f0c01664e7b378fd73d..5cc8532a3488f01091096a7855196558f55002a4 100644 (file)
@@ -1,6 +1,9 @@
 #include "jemalloc/internal/jemalloc_internal_defs.h"
 #include "jemalloc/internal/jemalloc_internal_decls.h"
 
-/* For use by SFMT. */
+/*
+ * For use by SFMT.  configure.ac doesn't actually define HAVE_SSE2 because its
+ * dependencies are notoriously unportable in practice.
+ */
 #undef HAVE_SSE2
 #undef HAVE_ALTIVEC
index a862ed7db24adc218edeaf3c69d7ecb8a0d4b43d..b057b29a1d2a1d280924f66a9eec11f1d00a3679 100644 (file)
@@ -299,7 +299,7 @@ pt_chi2(double p, double df, double ln_gamma_df_2)
 
 /*
  * Given a value p in [0..1] and Gamma distribution shape and scale parameters,
- * compute the upper limit on the definite integeral from [0..z] that satisfies
+ * compute the upper limit on the definite integral from [0..z] that satisfies
  * p.
  */
 JEMALLOC_INLINE double
index 11188653c655f79a259a4b835f558dc81b289406..7c4df493188050602f152e3f5d4f8b814aa8b00a 100644 (file)
@@ -1,3 +1,5 @@
+void   mq_nanosleep(unsigned ns);
+
 /*
  * Simple templated message queue implementation that relies on only mutexes for
  * synchronization (which reduces portability issues).  Given the following
@@ -75,26 +77,23 @@ a_attr a_mq_msg_type *                                                      \
 a_prefix##get(a_mq_type *mq)                                           \
 {                                                                      \
        a_mq_msg_type *msg;                                             \
-       struct timespec timeout;                                        \
+       unsigned ns;                                                    \
                                                                        \
        msg = a_prefix##tryget(mq);                                     \
        if (msg != NULL)                                                \
                return (msg);                                           \
                                                                        \
-       timeout.tv_sec = 0;                                             \
-       timeout.tv_nsec = 1;                                            \
+       ns = 1;                                                         \
        while (true) {                                                  \
-               nanosleep(&timeout, NULL);                              \
+               mq_nanosleep(ns);                                       \
                msg = a_prefix##tryget(mq);                             \
                if (msg != NULL)                                        \
                        return (msg);                                   \
-               if (timeout.tv_sec == 0) {                              \
+               if (ns < 1000*1000*1000) {                              \
                        /* Double sleep time, up to max 1 second. */    \
-                       timeout.tv_nsec <<= 1;                          \
-                       if (timeout.tv_nsec >= 1000*1000*1000) {        \
-                               timeout.tv_sec = 1;                     \
-                               timeout.tv_nsec = 0;                    \
-                       }                                               \
+                       ns <<= 1;                                       \
+                       if (ns > 1000*1000*1000)                        \
+                               ns = 1000*1000*1000;                    \
                }                                                       \
        }                                                               \
 }                                                                      \
index f55bafce677a8cd49fdcd1fa764f30a2f6f6353d..3cf901fc46f0012cfcd21c6a904ba0058c22082d 100644 (file)
     <=, "zu", __VA_ARGS__)
 
 #define        assert_d32_eq(a, b, ...)        assert_cmp(int32_t, a, b, ==,   \
-    !=, PRId32, __VA_ARGS__)
+    !=, FMTd32, __VA_ARGS__)
 #define        assert_d32_ne(a, b, ...)        assert_cmp(int32_t, a, b, !=,   \
-    ==, PRId32, __VA_ARGS__)
+    ==, FMTd32, __VA_ARGS__)
 #define        assert_d32_lt(a, b, ...)        assert_cmp(int32_t, a, b, <,    \
-    >=, PRId32, __VA_ARGS__)
+    >=, FMTd32, __VA_ARGS__)
 #define        assert_d32_le(a, b, ...)        assert_cmp(int32_t, a, b, <=,   \
-    >, PRId32, __VA_ARGS__)
+    >, FMTd32, __VA_ARGS__)
 #define        assert_d32_ge(a, b, ...)        assert_cmp(int32_t, a, b, >=,   \
-    <, PRId32, __VA_ARGS__)
+    <, FMTd32, __VA_ARGS__)
 #define        assert_d32_gt(a, b, ...)        assert_cmp(int32_t, a, b, >,    \
-    <=, PRId32, __VA_ARGS__)
+    <=, FMTd32, __VA_ARGS__)
 
 #define        assert_u32_eq(a, b, ...)        assert_cmp(uint32_t, a, b, ==,  \
-    !=, PRIu32, __VA_ARGS__)
+    !=, FMTu32, __VA_ARGS__)
 #define        assert_u32_ne(a, b, ...)        assert_cmp(uint32_t, a, b, !=,  \
-    ==, PRIu32, __VA_ARGS__)
+    ==, FMTu32, __VA_ARGS__)
 #define        assert_u32_lt(a, b, ...)        assert_cmp(uint32_t, a, b, <,   \
-    >=, PRIu32, __VA_ARGS__)
+    >=, FMTu32, __VA_ARGS__)
 #define        assert_u32_le(a, b, ...)        assert_cmp(uint32_t, a, b, <=,  \
-    >, PRIu32, __VA_ARGS__)
+    >, FMTu32, __VA_ARGS__)
 #define        assert_u32_ge(a, b, ...)        assert_cmp(uint32_t, a, b, >=,  \
-    <, PRIu32, __VA_ARGS__)
+    <, FMTu32, __VA_ARGS__)
 #define        assert_u32_gt(a, b, ...)        assert_cmp(uint32_t, a, b, >,   \
-    <=, PRIu32, __VA_ARGS__)
+    <=, FMTu32, __VA_ARGS__)
 
 #define        assert_d64_eq(a, b, ...)        assert_cmp(int64_t, a, b, ==,   \
-    !=, PRId64, __VA_ARGS__)
+    !=, FMTd64, __VA_ARGS__)
 #define        assert_d64_ne(a, b, ...)        assert_cmp(int64_t, a, b, !=,   \
-    ==, PRId64, __VA_ARGS__)
+    ==, FMTd64, __VA_ARGS__)
 #define        assert_d64_lt(a, b, ...)        assert_cmp(int64_t, a, b, <,    \
-    >=, PRId64, __VA_ARGS__)
+    >=, FMTd64, __VA_ARGS__)
 #define        assert_d64_le(a, b, ...)        assert_cmp(int64_t, a, b, <=,   \
-    >, PRId64, __VA_ARGS__)
+    >, FMTd64, __VA_ARGS__)
 #define        assert_d64_ge(a, b, ...)        assert_cmp(int64_t, a, b, >=,   \
-    <, PRId64, __VA_ARGS__)
+    <, FMTd64, __VA_ARGS__)
 #define        assert_d64_gt(a, b, ...)        assert_cmp(int64_t, a, b, >,    \
-    <=, PRId64, __VA_ARGS__)
+    <=, FMTd64, __VA_ARGS__)
 
 #define        assert_u64_eq(a, b, ...)        assert_cmp(uint64_t, a, b, ==,  \
-    !=, PRIu64, __VA_ARGS__)
+    !=, FMTu64, __VA_ARGS__)
 #define        assert_u64_ne(a, b, ...)        assert_cmp(uint64_t, a, b, !=,  \
-    ==, PRIu64, __VA_ARGS__)
+    ==, FMTu64, __VA_ARGS__)
 #define        assert_u64_lt(a, b, ...)        assert_cmp(uint64_t, a, b, <,   \
-    >=, PRIu64, __VA_ARGS__)
+    >=, FMTu64, __VA_ARGS__)
 #define        assert_u64_le(a, b, ...)        assert_cmp(uint64_t, a, b, <=,  \
-    >, PRIu64, __VA_ARGS__)
+    >, FMTu64, __VA_ARGS__)
 #define        assert_u64_ge(a, b, ...)        assert_cmp(uint64_t, a, b, >=,  \
-    <, PRIu64, __VA_ARGS__)
+    <, FMTu64, __VA_ARGS__)
 #define        assert_u64_gt(a, b, ...)        assert_cmp(uint64_t, a, b, >,   \
-    <=, PRIu64, __VA_ARGS__)
+    <=, FMTu64, __VA_ARGS__)
 
 #define        assert_b_eq(a, b, ...) do {                                     \
        bool a_ = (a);                                                  \
@@ -319,8 +319,8 @@ label_test_end:                                                             \
        }                                                               \
 } while (0)
 
-void   test_skip(const char *format, ...) JEMALLOC_ATTR(format(printf, 1, 2));
-void   test_fail(const char *format, ...) JEMALLOC_ATTR(format(printf, 1, 2));
+void   test_skip(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);
+void   test_fail(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);
 
 /* For private use by macros. */
 test_status_t  p_test(test_t *t, ...);
index f941d7a752fed19b9b0855df406a7a8b9b732342..47a51262e990000795fdf183f64fe2816b863b85 100644 (file)
@@ -1,4 +1,4 @@
-/* Abstraction layer for threading in tests */
+/* Abstraction layer for threading in tests. */
 #ifdef _WIN32
 typedef HANDLE thd_t;
 #else
index 6877e4ac9f293323cb0923b819136505c369b390..a7fefdfd1bbbcbbaff67d0688ed4a0d38564f4ea 100644 (file)
@@ -1,12 +1,23 @@
-/*
- * Simple timer, for use in benchmark reporting.
- */
+/* Simple timer, for use in benchmark reporting. */
 
+#include <unistd.h>
 #include <sys/time.h>
 
+#define JEMALLOC_CLOCK_GETTIME defined(_POSIX_MONOTONIC_CLOCK) \
+    && _POSIX_MONOTONIC_CLOCK >= 0
+
 typedef struct {
+#ifdef _WIN32
+       FILETIME ft0;
+       FILETIME ft1;
+#elif JEMALLOC_CLOCK_GETTIME
+       struct timespec ts0;
+       struct timespec ts1;
+       int clock_id;
+#else
        struct timeval tv0;
        struct timeval tv1;
+#endif
 } timedelta_t;
 
 void   timer_start(timedelta_t *timer);
index 89938504eed50c8a6243283386e8a6ef850aad53..af1c9a53e44d2341166fc4bb23083b5d2ee4038a 100644 (file)
 #include "test/jemalloc_test.h"
 
-chunk_alloc_t *old_alloc;
-chunk_dalloc_t *old_dalloc;
+#ifdef JEMALLOC_FILL
+const char *malloc_conf = "junk:false";
+#endif
+
+static chunk_hooks_t orig_hooks;
+static chunk_hooks_t old_hooks;
+
+static bool do_dalloc = true;
+static bool do_decommit;
+
+static bool did_alloc;
+static bool did_dalloc;
+static bool did_commit;
+static bool did_decommit;
+static bool did_purge;
+static bool did_split;
+static bool did_merge;
+
+#if 0
+#  define TRACE_HOOK(fmt, ...) malloc_printf(fmt, __VA_ARGS__)
+#else
+#  define TRACE_HOOK(fmt, ...)
+#endif
+
+void *
+chunk_alloc(void *new_addr, size_t size, size_t alignment, bool *zero,
+    bool *commit, unsigned arena_ind)
+{
+
+       TRACE_HOOK("%s(new_addr=%p, size=%zu, alignment=%zu, *zero=%s, "
+           "*commit=%s, arena_ind=%u)\n", __func__, new_addr, size, alignment,
+           *zero ?  "true" : "false", *commit ? "true" : "false", arena_ind);
+       did_alloc = true;
+       return (old_hooks.alloc(new_addr, size, alignment, zero, commit,
+           arena_ind));
+}
 
 bool
-chunk_dalloc(void *chunk, size_t size, unsigned arena_ind)
+chunk_dalloc(void *chunk, size_t size, bool committed, unsigned arena_ind)
 {
 
-       return (old_dalloc(chunk, size, arena_ind));
+       TRACE_HOOK("%s(chunk=%p, size=%zu, committed=%s, arena_ind=%u)\n",
+           __func__, chunk, size, committed ? "true" : "false", arena_ind);
+       did_dalloc = true;
+       if (!do_dalloc)
+               return (true);
+       return (old_hooks.dalloc(chunk, size, committed, arena_ind));
 }
 
-void *
-chunk_alloc(void *new_addr, size_t size, size_t alignment, bool *zero,
+bool
+chunk_commit(void *chunk, size_t size, size_t offset, size_t length,
+    unsigned arena_ind)
+{
+       bool err;
+
+       TRACE_HOOK("%s(chunk=%p, size=%zu, offset=%zu, length=%zu, "
+           "arena_ind=%u)\n", __func__, chunk, size, offset, length,
+           arena_ind);
+       err = old_hooks.commit(chunk, size, offset, length, arena_ind);
+       did_commit = !err;
+       return (err);
+}
+
+bool
+chunk_decommit(void *chunk, size_t size, size_t offset, size_t length,
+    unsigned arena_ind)
+{
+       bool err;
+
+       TRACE_HOOK("%s(chunk=%p, size=%zu, offset=%zu, length=%zu, "
+           "arena_ind=%u)\n", __func__, chunk, size, offset, length,
+           arena_ind);
+       if (!do_decommit)
+               return (true);
+       err = old_hooks.decommit(chunk, size, offset, length, arena_ind);
+       did_decommit = !err;
+       return (err);
+}
+
+bool
+chunk_purge(void *chunk, size_t size, size_t offset, size_t length,
     unsigned arena_ind)
 {
 
-       return (old_alloc(new_addr, size, alignment, zero, arena_ind));
+       TRACE_HOOK("%s(chunk=%p, size=%zu, offset=%zu, length=%zu "
+           "arena_ind=%u)\n", __func__, chunk, size, offset, length,
+           arena_ind);
+       did_purge = true;
+       return (old_hooks.purge(chunk, size, offset, length, arena_ind));
+}
+
+bool
+chunk_split(void *chunk, size_t size, size_t size_a, size_t size_b,
+    bool committed, unsigned arena_ind)
+{
+
+       TRACE_HOOK("%s(chunk=%p, size=%zu, size_a=%zu, size_b=%zu, "
+           "committed=%s, arena_ind=%u)\n", __func__, chunk, size, size_a,
+           size_b, committed ? "true" : "false", arena_ind);
+       did_split = true;
+       return (old_hooks.split(chunk, size, size_a, size_b, committed,
+           arena_ind));
+}
+
+bool
+chunk_merge(void *chunk_a, size_t size_a, void *chunk_b, size_t size_b,
+    bool committed, unsigned arena_ind)
+{
+
+       TRACE_HOOK("%s(chunk_a=%p, size_a=%zu, chunk_b=%p size_b=%zu, "
+           "committed=%s, arena_ind=%u)\n", __func__, chunk_a, size_a, chunk_b,
+           size_b, committed ? "true" : "false", arena_ind);
+       did_merge = true;
+       return (old_hooks.merge(chunk_a, size_a, chunk_b, size_b,
+           committed, arena_ind));
 }
 
 TEST_BEGIN(test_chunk)
 {
        void *p;
-       chunk_alloc_t *new_alloc;
-       chunk_dalloc_t *new_dalloc;
-       size_t old_size, new_size;
+       size_t old_size, new_size, large0, large1, huge0, huge1, huge2, sz;
+       chunk_hooks_t new_hooks = {
+               chunk_alloc,
+               chunk_dalloc,
+               chunk_commit,
+               chunk_decommit,
+               chunk_purge,
+               chunk_split,
+               chunk_merge
+       };
+       bool xallocx_success_a, xallocx_success_b, xallocx_success_c;
 
-       new_alloc = chunk_alloc;
-       new_dalloc = chunk_dalloc;
-       old_size = sizeof(chunk_alloc_t *);
-       new_size = sizeof(chunk_alloc_t *);
+       /* Install custom chunk hooks. */
+       old_size = sizeof(chunk_hooks_t);
+       new_size = sizeof(chunk_hooks_t);
+       assert_d_eq(mallctl("arena.0.chunk_hooks", &old_hooks, &old_size,
+           &new_hooks, new_size), 0, "Unexpected chunk_hooks error");
+       orig_hooks = old_hooks;
+       assert_ptr_ne(old_hooks.alloc, chunk_alloc, "Unexpected alloc error");
+       assert_ptr_ne(old_hooks.dalloc, chunk_dalloc,
+           "Unexpected dalloc error");
+       assert_ptr_ne(old_hooks.commit, chunk_commit,
+           "Unexpected commit error");
+       assert_ptr_ne(old_hooks.decommit, chunk_decommit,
+           "Unexpected decommit error");
+       assert_ptr_ne(old_hooks.purge, chunk_purge, "Unexpected purge error");
+       assert_ptr_ne(old_hooks.split, chunk_split, "Unexpected split error");
+       assert_ptr_ne(old_hooks.merge, chunk_merge, "Unexpected merge error");
 
-       assert_d_eq(mallctl("arena.0.chunk.alloc", &old_alloc,
-           &old_size, &new_alloc, new_size), 0,
-           "Unexpected alloc error");
-       assert_ptr_ne(old_alloc, new_alloc,
-           "Unexpected alloc error");
-       assert_d_eq(mallctl("arena.0.chunk.dalloc", &old_dalloc, &old_size,
-           &new_dalloc, new_size), 0, "Unexpected dalloc error");
-       assert_ptr_ne(old_dalloc, new_dalloc, "Unexpected dalloc error");
+       /* Get large size classes. */
+       sz = sizeof(size_t);
+       assert_d_eq(mallctl("arenas.lrun.0.size", &large0, &sz, NULL, 0), 0,
+           "Unexpected arenas.lrun.0.size failure");
+       assert_d_eq(mallctl("arenas.lrun.1.size", &large1, &sz, NULL, 0), 0,
+           "Unexpected arenas.lrun.1.size failure");
+
+       /* Get huge size classes. */
+       assert_d_eq(mallctl("arenas.hchunk.0.size", &huge0, &sz, NULL, 0), 0,
+           "Unexpected arenas.hchunk.0.size failure");
+       assert_d_eq(mallctl("arenas.hchunk.1.size", &huge1, &sz, NULL, 0), 0,
+           "Unexpected arenas.hchunk.1.size failure");
+       assert_d_eq(mallctl("arenas.hchunk.2.size", &huge2, &sz, NULL, 0), 0,
+           "Unexpected arenas.hchunk.2.size failure");
+
+       /* Test dalloc/decommit/purge cascade. */
+       do_dalloc = false;
+       do_decommit = false;
+       p = mallocx(huge0 * 2, 0);
+       assert_ptr_not_null(p, "Unexpected mallocx() error");
+       did_dalloc = false;
+       did_decommit = false;
+       did_purge = false;
+       did_split = false;
+       xallocx_success_a = (xallocx(p, huge0, 0, 0) == huge0);
+       assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
+           "Unexpected arena.0.purge error");
+       if (xallocx_success_a) {
+               assert_true(did_dalloc, "Expected dalloc");
+               assert_false(did_decommit, "Unexpected decommit");
+               assert_true(did_purge, "Expected purge");
+       }
+       assert_true(did_split, "Expected split");
+       dallocx(p, 0);
+       do_dalloc = true;
+
+       /* Test decommit/commit and observe split/merge. */
+       do_dalloc = false;
+       do_decommit = true;
+       p = mallocx(huge0 * 2, 0);
+       assert_ptr_not_null(p, "Unexpected mallocx() error");
+       did_decommit = false;
+       did_commit = false;
+       did_split = false;
+       did_merge = false;
+       xallocx_success_b = (xallocx(p, huge0, 0, 0) == huge0);
+       assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
+           "Unexpected arena.0.purge error");
+       if (xallocx_success_b)
+               assert_true(did_split, "Expected split");
+       xallocx_success_c = (xallocx(p, huge0 * 2, 0, 0) == huge0 * 2);
+       assert_b_eq(did_decommit, did_commit, "Expected decommit/commit match");
+       if (xallocx_success_b && xallocx_success_c)
+               assert_true(did_merge, "Expected merge");
+       dallocx(p, 0);
+       do_dalloc = true;
+       do_decommit = false;
+
+       /* Test purge for partial-chunk huge allocations. */
+       if (huge0 * 2 > huge2) {
+               /*
+                * There are at least four size classes per doubling, so a
+                * successful xallocx() from size=huge2 to size=huge1 is
+                * guaranteed to leave trailing purgeable memory.
+                */
+               p = mallocx(huge2, 0);
+               assert_ptr_not_null(p, "Unexpected mallocx() error");
+               did_purge = false;
+               assert_zu_eq(xallocx(p, huge1, 0, 0), huge1,
+                   "Unexpected xallocx() failure");
+               assert_true(did_purge, "Expected purge");
+               dallocx(p, 0);
+       }
+
+       /* Test decommit for large allocations. */
+       do_decommit = true;
+       p = mallocx(large1, 0);
+       assert_ptr_not_null(p, "Unexpected mallocx() error");
+       assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
+           "Unexpected arena.0.purge error");
+       did_decommit = false;
+       assert_zu_eq(xallocx(p, large0, 0, 0), large0,
+           "Unexpected xallocx() failure");
+       assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
+           "Unexpected arena.0.purge error");
+       did_commit = false;
+       assert_zu_eq(xallocx(p, large1, 0, 0), large1,
+           "Unexpected xallocx() failure");
+       assert_b_eq(did_decommit, did_commit, "Expected decommit/commit match");
+       dallocx(p, 0);
+       do_decommit = false;
 
+       /* Make sure non-huge allocation succeeds. */
        p = mallocx(42, 0);
-       assert_ptr_ne(p, NULL, "Unexpected alloc error");
-       free(p);
+       assert_ptr_not_null(p, "Unexpected mallocx() error");
+       dallocx(p, 0);
 
-       assert_d_eq(mallctl("arena.0.chunk.alloc", NULL,
-           NULL, &old_alloc, old_size), 0,
+       /* Restore chunk hooks. */
+       assert_d_eq(mallctl("arena.0.chunk_hooks", NULL, NULL, &old_hooks,
+           new_size), 0, "Unexpected chunk_hooks error");
+       assert_d_eq(mallctl("arena.0.chunk_hooks", &old_hooks, &old_size,
+           NULL, 0), 0, "Unexpected chunk_hooks error");
+       assert_ptr_eq(old_hooks.alloc, orig_hooks.alloc,
            "Unexpected alloc error");
-       assert_d_eq(mallctl("arena.0.chunk.dalloc", NULL, NULL, &old_dalloc,
-           old_size), 0, "Unexpected dalloc error");
+       assert_ptr_eq(old_hooks.dalloc, orig_hooks.dalloc,
+           "Unexpected dalloc error");
+       assert_ptr_eq(old_hooks.commit, orig_hooks.commit,
+           "Unexpected commit error");
+       assert_ptr_eq(old_hooks.decommit, orig_hooks.decommit,
+           "Unexpected decommit error");
+       assert_ptr_eq(old_hooks.purge, orig_hooks.purge,
+           "Unexpected purge error");
+       assert_ptr_eq(old_hooks.split, orig_hooks.split,
+           "Unexpected split error");
+       assert_ptr_eq(old_hooks.merge, orig_hooks.merge,
+           "Unexpected merge error");
 }
 TEST_END
 
index 123e041fa33f09cee5875c8445a0cd172dba47cb..6253175d64c20ba4fc0c371cf02f6b53877f974b 100644 (file)
 #include "test/jemalloc_test.h"
 
-#define        CHUNK 0x400000
-#define        MAXALIGN (((size_t)1) << 25)
-#define        NITER 4
+static unsigned
+get_nsizes_impl(const char *cmd)
+{
+       unsigned ret;
+       size_t z;
+
+       z = sizeof(unsigned);
+       assert_d_eq(mallctl(cmd, &ret, &z, NULL, 0), 0,
+           "Unexpected mallctl(\"%s\", ...) failure", cmd);
+
+       return (ret);
+}
+
+static unsigned
+get_nhuge(void)
+{
+
+       return (get_nsizes_impl("arenas.nhchunks"));
+}
+
+static size_t
+get_size_impl(const char *cmd, size_t ind)
+{
+       size_t ret;
+       size_t z;
+       size_t mib[4];
+       size_t miblen = 4;
+
+       z = sizeof(size_t);
+       assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
+           0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
+       mib[2] = ind;
+       z = sizeof(size_t);
+       assert_d_eq(mallctlbymib(mib, miblen, &ret, &z, NULL, 0),
+           0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind);
+
+       return (ret);
+}
+
+static size_t
+get_huge_size(size_t ind)
+{
+
+       return (get_size_impl("arenas.hchunk.0.size", ind));
+}
+
+TEST_BEGIN(test_oom)
+{
+       size_t hugemax, size, alignment;
+
+       hugemax = get_huge_size(get_nhuge()-1);
+
+       /*
+        * It should be impossible to allocate two objects that each consume
+        * more than half the virtual address space.
+        */
+       {
+               void *p;
+
+               p = mallocx(hugemax, 0);
+               if (p != NULL) {
+                       assert_ptr_null(mallocx(hugemax, 0),
+                           "Expected OOM for mallocx(size=%#zx, 0)", hugemax);
+                       dallocx(p, 0);
+               }
+       }
+
+#if LG_SIZEOF_PTR == 3
+       size      = ZU(0x8000000000000000);
+       alignment = ZU(0x8000000000000000);
+#else
+       size      = ZU(0x80000000);
+       alignment = ZU(0x80000000);
+#endif
+       assert_ptr_null(mallocx(size, MALLOCX_ALIGN(alignment)),
+           "Expected OOM for mallocx(size=%#zx, MALLOCX_ALIGN(%#zx)", size,
+           alignment);
+}
+TEST_END
 
 TEST_BEGIN(test_basic)
 {
-       size_t nsz, rsz, sz;
-       void *p;
-
-       sz = 42;
-       nsz = nallocx(sz, 0);
-       assert_zu_ne(nsz, 0, "Unexpected nallocx() error");
-       p = mallocx(sz, 0);
-       assert_ptr_not_null(p, "Unexpected mallocx() error");
-       rsz = sallocx(p, 0);
-       assert_zu_ge(rsz, sz, "Real size smaller than expected");
-       assert_zu_eq(nsz, rsz, "nallocx()/sallocx() size mismatch");
-       dallocx(p, 0);
-
-       p = mallocx(sz, 0);
-       assert_ptr_not_null(p, "Unexpected mallocx() error");
-       dallocx(p, 0);
-
-       nsz = nallocx(sz, MALLOCX_ZERO);
-       assert_zu_ne(nsz, 0, "Unexpected nallocx() error");
-       p = mallocx(sz, MALLOCX_ZERO);
-       assert_ptr_not_null(p, "Unexpected mallocx() error");
-       rsz = sallocx(p, 0);
-       assert_zu_eq(nsz, rsz, "nallocx()/sallocx() rsize mismatch");
-       dallocx(p, 0);
+#define        MAXSZ (((size_t)1) << 26)
+       size_t sz;
+
+       for (sz = 1; sz < MAXSZ; sz = nallocx(sz, 0) + 1) {
+               size_t nsz, rsz;
+               void *p;
+               nsz = nallocx(sz, 0);
+               assert_zu_ne(nsz, 0, "Unexpected nallocx() error");
+               p = mallocx(sz, 0);
+               assert_ptr_not_null(p, "Unexpected mallocx() error");
+               rsz = sallocx(p, 0);
+               assert_zu_ge(rsz, sz, "Real size smaller than expected");
+               assert_zu_eq(nsz, rsz, "nallocx()/sallocx() size mismatch");
+               dallocx(p, 0);
+
+               p = mallocx(sz, 0);
+               assert_ptr_not_null(p, "Unexpected mallocx() error");
+               dallocx(p, 0);
+
+               nsz = nallocx(sz, MALLOCX_ZERO);
+               assert_zu_ne(nsz, 0, "Unexpected nallocx() error");
+               p = mallocx(sz, MALLOCX_ZERO);
+               assert_ptr_not_null(p, "Unexpected mallocx() error");
+               rsz = sallocx(p, 0);
+               assert_zu_eq(nsz, rsz, "nallocx()/sallocx() rsize mismatch");
+               dallocx(p, 0);
+       }
+#undef MAXSZ
 }
 TEST_END
 
 TEST_BEGIN(test_alignment_and_size)
 {
+#define        MAXALIGN (((size_t)1) << 25)
+#define        NITER 4
        size_t nsz, rsz, sz, alignment, total;
        unsigned i;
        void *ps[NITER];
@@ -84,6 +166,8 @@ TEST_BEGIN(test_alignment_and_size)
                        }
                }
        }
+#undef MAXALIGN
+#undef NITER
 }
 TEST_END
 
@@ -92,6 +176,7 @@ main(void)
 {
 
        return (test(
+           test_oom,
            test_basic,
            test_alignment_and_size));
 }
diff --git a/src/jemalloc/test/integration/overflow.c b/src/jemalloc/test/integration/overflow.c
new file mode 100644 (file)
index 0000000..303d9b2
--- /dev/null
@@ -0,0 +1,49 @@
+#include "test/jemalloc_test.h"
+
+TEST_BEGIN(test_overflow)
+{
+       unsigned nhchunks;
+       size_t mib[4];
+       size_t sz, miblen, max_size_class;
+       void *p;
+
+       sz = sizeof(unsigned);
+       assert_d_eq(mallctl("arenas.nhchunks", &nhchunks, &sz, NULL, 0), 0,
+           "Unexpected mallctl() error");
+
+       miblen = sizeof(mib) / sizeof(size_t);
+       assert_d_eq(mallctlnametomib("arenas.hchunk.0.size", mib, &miblen), 0,
+           "Unexpected mallctlnametomib() error");
+       mib[2] = nhchunks - 1;
+
+       sz = sizeof(size_t);
+       assert_d_eq(mallctlbymib(mib, miblen, &max_size_class, &sz, NULL, 0), 0,
+           "Unexpected mallctlbymib() error");
+
+       assert_ptr_null(malloc(max_size_class + 1),
+           "Expected OOM due to over-sized allocation request");
+       assert_ptr_null(malloc(SIZE_T_MAX),
+           "Expected OOM due to over-sized allocation request");
+
+       assert_ptr_null(calloc(1, max_size_class + 1),
+           "Expected OOM due to over-sized allocation request");
+       assert_ptr_null(calloc(1, SIZE_T_MAX),
+           "Expected OOM due to over-sized allocation request");
+
+       p = malloc(1);
+       assert_ptr_not_null(p, "Unexpected malloc() OOM");
+       assert_ptr_null(realloc(p, max_size_class + 1),
+           "Expected OOM due to over-sized allocation request");
+       assert_ptr_null(realloc(p, SIZE_T_MAX),
+           "Expected OOM due to over-sized allocation request");
+       free(p);
+}
+TEST_END
+
+int
+main(void)
+{
+
+       return (test(
+           test_overflow));
+}
index b69807298c0703205dcb21d260672c0d24371c42..be1b27b73896df68078272ca1fa3aa358e77babc 100644 (file)
@@ -22,7 +22,7 @@ TEST_BEGIN(test_grow_and_shrink)
                            szs[j-1], szs[j-1]+1);
                        szs[j] = sallocx(q, 0);
                        assert_zu_ne(szs[j], szs[j-1]+1,
-                           "Expected size to at least: %zu", szs[j-1]+1);
+                           "Expected size to be at least: %zu", szs[j-1]+1);
                        p = q;
                }
 
@@ -55,8 +55,9 @@ validate_fill(const void *p, uint8_t c, size_t offset, size_t len)
        for (i = 0; i < len; i++) {
                uint8_t b = buf[offset+i];
                if (b != c) {
-                       test_fail("Allocation at %p contains %#x rather than "
-                           "%#x at offset %zu", p, b, c, offset+i);
+                       test_fail("Allocation at %p (len=%zu) contains %#x "
+                           "rather than %#x at offset %zu", p, len, b, c,
+                           offset+i);
                        ret = true;
                }
        }
index ab4cf945e54e5915db225ff4167a75eb9f19c323..00451961550c6fb1fbab8a1ced7a0d44d5177b22 100644 (file)
@@ -1,5 +1,24 @@
 #include "test/jemalloc_test.h"
 
+/*
+ * Use a separate arena for xallocx() extension/contraction tests so that
+ * internal allocation e.g. by heap profiling can't interpose allocations where
+ * xallocx() would ordinarily be able to extend.
+ */
+static unsigned
+arena_ind(void)
+{
+       static unsigned ind = 0;
+
+       if (ind == 0) {
+               size_t sz = sizeof(ind);
+               assert_d_eq(mallctl("arenas.extend", &ind, &sz, NULL, 0), 0,
+                   "Unexpected mallctl failure creating arena");
+       }
+
+       return (ind);
+}
+
 TEST_BEGIN(test_same_size)
 {
        void *p;
@@ -48,6 +67,414 @@ TEST_BEGIN(test_no_move_fail)
 }
 TEST_END
 
+static unsigned
+get_nsizes_impl(const char *cmd)
+{
+       unsigned ret;
+       size_t z;
+
+       z = sizeof(unsigned);
+       assert_d_eq(mallctl(cmd, &ret, &z, NULL, 0), 0,
+           "Unexpected mallctl(\"%s\", ...) failure", cmd);
+
+       return (ret);
+}
+
+static unsigned
+get_nsmall(void)
+{
+
+       return (get_nsizes_impl("arenas.nbins"));
+}
+
+static unsigned
+get_nlarge(void)
+{
+
+       return (get_nsizes_impl("arenas.nlruns"));
+}
+
+static unsigned
+get_nhuge(void)
+{
+
+       return (get_nsizes_impl("arenas.nhchunks"));
+}
+
+static size_t
+get_size_impl(const char *cmd, size_t ind)
+{
+       size_t ret;
+       size_t z;
+       size_t mib[4];
+       size_t miblen = 4;
+
+       z = sizeof(size_t);
+       assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
+           0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
+       mib[2] = ind;
+       z = sizeof(size_t);
+       assert_d_eq(mallctlbymib(mib, miblen, &ret, &z, NULL, 0),
+           0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind);
+
+       return (ret);
+}
+
+static size_t
+get_small_size(size_t ind)
+{
+
+       return (get_size_impl("arenas.bin.0.size", ind));
+}
+
+static size_t
+get_large_size(size_t ind)
+{
+
+       return (get_size_impl("arenas.lrun.0.size", ind));
+}
+
+static size_t
+get_huge_size(size_t ind)
+{
+
+       return (get_size_impl("arenas.hchunk.0.size", ind));
+}
+
+TEST_BEGIN(test_size)
+{
+       size_t small0, hugemax;
+       void *p;
+
+       /* Get size classes. */
+       small0 = get_small_size(0);
+       hugemax = get_huge_size(get_nhuge()-1);
+
+       p = mallocx(small0, 0);
+       assert_ptr_not_null(p, "Unexpected mallocx() error");
+
+       /* Test smallest supported size. */
+       assert_zu_eq(xallocx(p, 1, 0, 0), small0,
+           "Unexpected xallocx() behavior");
+
+       /* Test largest supported size. */
+       assert_zu_le(xallocx(p, hugemax, 0, 0), hugemax,
+           "Unexpected xallocx() behavior");
+
+       /* Test size overflow. */
+       assert_zu_le(xallocx(p, hugemax+1, 0, 0), hugemax,
+           "Unexpected xallocx() behavior");
+       assert_zu_le(xallocx(p, SIZE_T_MAX, 0, 0), hugemax,
+           "Unexpected xallocx() behavior");
+
+       dallocx(p, 0);
+}
+TEST_END
+
+TEST_BEGIN(test_size_extra_overflow)
+{
+       size_t small0, hugemax;
+       void *p;
+
+       /* Get size classes. */
+       small0 = get_small_size(0);
+       hugemax = get_huge_size(get_nhuge()-1);
+
+       p = mallocx(small0, 0);
+       assert_ptr_not_null(p, "Unexpected mallocx() error");
+
+       /* Test overflows that can be resolved by clamping extra. */
+       assert_zu_le(xallocx(p, hugemax-1, 2, 0), hugemax,
+           "Unexpected xallocx() behavior");
+       assert_zu_le(xallocx(p, hugemax, 1, 0), hugemax,
+           "Unexpected xallocx() behavior");
+
+       /* Test overflow such that hugemax-size underflows. */
+       assert_zu_le(xallocx(p, hugemax+1, 2, 0), hugemax,
+           "Unexpected xallocx() behavior");
+       assert_zu_le(xallocx(p, hugemax+2, 3, 0), hugemax,
+           "Unexpected xallocx() behavior");
+       assert_zu_le(xallocx(p, SIZE_T_MAX-2, 2, 0), hugemax,
+           "Unexpected xallocx() behavior");
+       assert_zu_le(xallocx(p, SIZE_T_MAX-1, 1, 0), hugemax,
+           "Unexpected xallocx() behavior");
+
+       dallocx(p, 0);
+}
+TEST_END
+
+TEST_BEGIN(test_extra_small)
+{
+       size_t small0, small1, hugemax;
+       void *p;
+
+       /* Get size classes. */
+       small0 = get_small_size(0);
+       small1 = get_small_size(1);
+       hugemax = get_huge_size(get_nhuge()-1);
+
+       p = mallocx(small0, 0);
+       assert_ptr_not_null(p, "Unexpected mallocx() error");
+
+       assert_zu_eq(xallocx(p, small1, 0, 0), small0,
+           "Unexpected xallocx() behavior");
+
+       assert_zu_eq(xallocx(p, small1, 0, 0), small0,
+           "Unexpected xallocx() behavior");
+
+       assert_zu_eq(xallocx(p, small0, small1 - small0, 0), small0,
+           "Unexpected xallocx() behavior");
+
+       /* Test size+extra overflow. */
+       assert_zu_eq(xallocx(p, small0, hugemax - small0 + 1, 0), small0,
+           "Unexpected xallocx() behavior");
+       assert_zu_eq(xallocx(p, small0, SIZE_T_MAX - small0, 0), small0,
+           "Unexpected xallocx() behavior");
+
+       dallocx(p, 0);
+}
+TEST_END
+
+TEST_BEGIN(test_extra_large)
+{
+       int flags = MALLOCX_ARENA(arena_ind());
+       size_t smallmax, large0, large1, large2, huge0, hugemax;
+       void *p;
+
+       /* Get size classes. */
+       smallmax = get_small_size(get_nsmall()-1);
+       large0 = get_large_size(0);
+       large1 = get_large_size(1);
+       large2 = get_large_size(2);
+       huge0 = get_huge_size(0);
+       hugemax = get_huge_size(get_nhuge()-1);
+
+       p = mallocx(large2, flags);
+       assert_ptr_not_null(p, "Unexpected mallocx() error");
+
+       assert_zu_eq(xallocx(p, large2, 0, flags), large2,
+           "Unexpected xallocx() behavior");
+       /* Test size decrease with zero extra. */
+       assert_zu_eq(xallocx(p, large0, 0, flags), large0,
+           "Unexpected xallocx() behavior");
+       assert_zu_eq(xallocx(p, smallmax, 0, flags), large0,
+           "Unexpected xallocx() behavior");
+
+       assert_zu_eq(xallocx(p, large2, 0, flags), large2,
+           "Unexpected xallocx() behavior");
+       /* Test size decrease with non-zero extra. */
+       assert_zu_eq(xallocx(p, large0, large2 - large0, flags), large2,
+           "Unexpected xallocx() behavior");
+       assert_zu_eq(xallocx(p, large1, large2 - large1, flags), large2,
+           "Unexpected xallocx() behavior");
+       assert_zu_eq(xallocx(p, large0, large1 - large0, flags), large1,
+           "Unexpected xallocx() behavior");
+       assert_zu_eq(xallocx(p, smallmax, large0 - smallmax, flags), large0,
+           "Unexpected xallocx() behavior");
+
+       assert_zu_eq(xallocx(p, large0, 0, flags), large0,
+           "Unexpected xallocx() behavior");
+       /* Test size increase with zero extra. */
+       assert_zu_eq(xallocx(p, large2, 0, flags), large2,
+           "Unexpected xallocx() behavior");
+       assert_zu_eq(xallocx(p, huge0, 0, flags), large2,
+           "Unexpected xallocx() behavior");
+
+       assert_zu_eq(xallocx(p, large0, 0, flags), large0,
+           "Unexpected xallocx() behavior");
+       /* Test size increase with non-zero extra. */
+       assert_zu_lt(xallocx(p, large0, huge0 - large0, flags), huge0,
+           "Unexpected xallocx() behavior");
+
+       assert_zu_eq(xallocx(p, large0, 0, flags), large0,
+           "Unexpected xallocx() behavior");
+       /* Test size increase with non-zero extra. */
+       assert_zu_eq(xallocx(p, large0, large2 - large0, flags), large2,
+           "Unexpected xallocx() behavior");
+
+       assert_zu_eq(xallocx(p, large2, 0, flags), large2,
+           "Unexpected xallocx() behavior");
+       /* Test size+extra overflow. */
+       assert_zu_lt(xallocx(p, large2, hugemax - large2 + 1, flags), huge0,
+           "Unexpected xallocx() behavior");
+
+       dallocx(p, flags);
+}
+TEST_END
+
+TEST_BEGIN(test_extra_huge)
+{
+       int flags = MALLOCX_ARENA(arena_ind());
+       size_t largemax, huge0, huge1, huge2, hugemax;
+       void *p;
+
+       /* Get size classes. */
+       largemax = get_large_size(get_nlarge()-1);
+       huge0 = get_huge_size(0);
+       huge1 = get_huge_size(1);
+       huge2 = get_huge_size(2);
+       hugemax = get_huge_size(get_nhuge()-1);
+
+       p = mallocx(huge2, flags);
+       assert_ptr_not_null(p, "Unexpected mallocx() error");
+
+       assert_zu_eq(xallocx(p, huge2, 0, flags), huge2,
+           "Unexpected xallocx() behavior");
+       /* Test size decrease with zero extra. */
+       assert_zu_ge(xallocx(p, huge0, 0, flags), huge0,
+           "Unexpected xallocx() behavior");
+       assert_zu_ge(xallocx(p, largemax, 0, flags), huge0,
+           "Unexpected xallocx() behavior");
+
+       assert_zu_eq(xallocx(p, huge2, 0, flags), huge2,
+           "Unexpected xallocx() behavior");
+       /* Test size decrease with non-zero extra. */
+       assert_zu_eq(xallocx(p, huge0, huge2 - huge0, flags), huge2,
+           "Unexpected xallocx() behavior");
+       assert_zu_eq(xallocx(p, huge1, huge2 - huge1, flags), huge2,
+           "Unexpected xallocx() behavior");
+       assert_zu_eq(xallocx(p, huge0, huge1 - huge0, flags), huge1,
+           "Unexpected xallocx() behavior");
+       assert_zu_ge(xallocx(p, largemax, huge0 - largemax, flags), huge0,
+           "Unexpected xallocx() behavior");
+
+       assert_zu_ge(xallocx(p, huge0, 0, flags), huge0,
+           "Unexpected xallocx() behavior");
+       /* Test size increase with zero extra. */
+       assert_zu_le(xallocx(p, huge2, 0, flags), huge2,
+           "Unexpected xallocx() behavior");
+       assert_zu_le(xallocx(p, hugemax+1, 0, flags), huge2,
+           "Unexpected xallocx() behavior");
+
+       assert_zu_ge(xallocx(p, huge0, 0, flags), huge0,
+           "Unexpected xallocx() behavior");
+       /* Test size increase with non-zero extra. */
+       assert_zu_le(xallocx(p, huge0, SIZE_T_MAX - huge0, flags), hugemax,
+           "Unexpected xallocx() behavior");
+
+       assert_zu_ge(xallocx(p, huge0, 0, flags), huge0,
+           "Unexpected xallocx() behavior");
+       /* Test size increase with non-zero extra. */
+       assert_zu_le(xallocx(p, huge0, huge2 - huge0, flags), huge2,
+           "Unexpected xallocx() behavior");
+
+       assert_zu_eq(xallocx(p, huge2, 0, flags), huge2,
+           "Unexpected xallocx() behavior");
+       /* Test size+extra overflow. */
+       assert_zu_le(xallocx(p, huge2, hugemax - huge2 + 1, flags), hugemax,
+           "Unexpected xallocx() behavior");
+
+       dallocx(p, flags);
+}
+TEST_END
+
+static void
+print_filled_extents(const void *p, uint8_t c, size_t len)
+{
+       const uint8_t *pc = (const uint8_t *)p;
+       size_t i, range0;
+       uint8_t c0;
+
+       malloc_printf("  p=%p, c=%#x, len=%zu:", p, c, len);
+       range0 = 0;
+       c0 = pc[0];
+       for (i = 0; i < len; i++) {
+               if (pc[i] != c0) {
+                       malloc_printf(" %#x[%zu..%zu)", c0, range0, i);
+                       range0 = i;
+                       c0 = pc[i];
+               }
+       }
+       malloc_printf(" %#x[%zu..%zu)\n", c0, range0, i);
+}
+
+static bool
+validate_fill(const void *p, uint8_t c, size_t offset, size_t len)
+{
+       const uint8_t *pc = (const uint8_t *)p;
+       bool err;
+       size_t i;
+
+       for (i = offset, err = false; i < offset+len; i++) {
+               if (pc[i] != c)
+                       err = true;
+       }
+
+       if (err)
+               print_filled_extents(p, c, offset + len);
+
+       return (err);
+}
+
+static void
+test_zero(size_t szmin, size_t szmax)
+{
+       int flags = MALLOCX_ARENA(arena_ind()) | MALLOCX_ZERO;
+       size_t sz, nsz;
+       void *p;
+#define        FILL_BYTE 0x7aU
+
+       sz = szmax;
+       p = mallocx(sz, flags);
+       assert_ptr_not_null(p, "Unexpected mallocx() error");
+       assert_false(validate_fill(p, 0x00, 0, sz), "Memory not filled: sz=%zu",
+           sz);
+
+       /*
+        * Fill with non-zero so that non-debug builds are more likely to detect
+        * errors.
+        */
+       memset(p, FILL_BYTE, sz);
+       assert_false(validate_fill(p, FILL_BYTE, 0, sz),
+           "Memory not filled: sz=%zu", sz);
+
+       /* Shrink in place so that we can expect growing in place to succeed. */
+       sz = szmin;
+       assert_zu_eq(xallocx(p, sz, 0, flags), sz,
+           "Unexpected xallocx() error");
+       assert_false(validate_fill(p, FILL_BYTE, 0, sz),
+           "Memory not filled: sz=%zu", sz);
+
+       for (sz = szmin; sz < szmax; sz = nsz) {
+               nsz = nallocx(sz+1, flags);
+               assert_zu_eq(xallocx(p, sz+1, 0, flags), nsz,
+                   "Unexpected xallocx() failure");
+               assert_false(validate_fill(p, FILL_BYTE, 0, sz),
+                   "Memory not filled: sz=%zu", sz);
+               assert_false(validate_fill(p, 0x00, sz, nsz-sz),
+                   "Memory not filled: sz=%zu, nsz-sz=%zu", sz, nsz-sz);
+               memset((void *)((uintptr_t)p + sz), FILL_BYTE, nsz-sz);
+               assert_false(validate_fill(p, FILL_BYTE, 0, nsz),
+                   "Memory not filled: nsz=%zu", nsz);
+       }
+
+       dallocx(p, flags);
+}
+
+TEST_BEGIN(test_zero_large)
+{
+       size_t large0, largemax;
+
+       /* Get size classes. */
+       large0 = get_large_size(0);
+       largemax = get_large_size(get_nlarge()-1);
+
+       test_zero(large0, largemax);
+}
+TEST_END
+
+TEST_BEGIN(test_zero_huge)
+{
+       size_t huge0, huge1;
+
+       /* Get size classes. */
+       huge0 = get_huge_size(0);
+       huge1 = get_huge_size(1);
+
+       test_zero(huge1, huge0 * 2);
+}
+TEST_END
+
 int
 main(void)
 {
@@ -55,5 +482,12 @@ main(void)
        return (test(
            test_same_size,
            test_extra_no_move,
-           test_no_move_fail));
+           test_no_move_fail,
+           test_size,
+           test_size_extra_overflow,
+           test_extra_small,
+           test_extra_large,
+           test_extra_huge,
+           test_zero_large,
+           test_zero_huge));
 }
diff --git a/src/jemalloc/test/src/mq.c b/src/jemalloc/test/src/mq.c
new file mode 100644 (file)
index 0000000..40b31c1
--- /dev/null
@@ -0,0 +1,29 @@
+#include "test/jemalloc_test.h"
+
+/*
+ * Sleep for approximately ns nanoseconds.  No lower *nor* upper bound on sleep
+ * time is guaranteed.
+ */
+void
+mq_nanosleep(unsigned ns)
+{
+
+       assert(ns <= 1000*1000*1000);
+
+#ifdef _WIN32
+       Sleep(ns / 1000);
+#else
+       {
+               struct timespec timeout;
+
+               if (ns < 1000*1000*1000) {
+                       timeout.tv_sec = 0;
+                       timeout.tv_nsec = ns;
+               } else {
+                       timeout.tv_sec = 1;
+                       timeout.tv_nsec = 0;
+               }
+               nanosleep(&timeout, NULL);
+       }
+#endif
+}
index 0f8bd4947105a2f99c21a4a478d845f53533ca6d..8173614cf638060c4a5d3fafdcda632cfdf8c82c 100644 (file)
@@ -5,7 +5,7 @@ static test_status_t    test_counts[test_status_count] = {0, 0, 0};
 static test_status_t   test_status = test_status_pass;
 static const char *    test_name = "";
 
-JEMALLOC_ATTR(format(printf, 1, 2))
+JEMALLOC_FORMAT_PRINTF(1, 2)
 void
 test_skip(const char *format, ...)
 {
@@ -18,7 +18,7 @@ test_skip(const char *format, ...)
        test_status = test_status_skip;
 }
 
-JEMALLOC_ATTR(format(printf, 1, 2))
+JEMALLOC_FORMAT_PRINTF(1, 2)
 void
 test_fail(const char *format, ...)
 {
index 36fbedd48952e02e0088b22a9ca1d7101d36f2e7..0c93abaf96b2b525dd701633a3743f4a64079a0b 100644 (file)
@@ -4,22 +4,50 @@ void
 timer_start(timedelta_t *timer)
 {
 
+#ifdef _WIN32
+       GetSystemTimeAsFileTime(&timer->ft0);
+#elif JEMALLOC_CLOCK_GETTIME
+       if (sysconf(_SC_MONOTONIC_CLOCK) <= 0)
+               timer->clock_id = CLOCK_REALTIME;
+       else
+               timer->clock_id = CLOCK_MONOTONIC;
+       clock_gettime(timer->clock_id, &timer->ts0);
+#else
        gettimeofday(&timer->tv0, NULL);
+#endif
 }
 
 void
 timer_stop(timedelta_t *timer)
 {
 
+#ifdef _WIN32
+       GetSystemTimeAsFileTime(&timer->ft0);
+#elif JEMALLOC_CLOCK_GETTIME
+       clock_gettime(timer->clock_id, &timer->ts1);
+#else
        gettimeofday(&timer->tv1, NULL);
+#endif
 }
 
 uint64_t
 timer_usec(const timedelta_t *timer)
 {
 
+#ifdef _WIN32
+       uint64_t t0, t1;
+       t0 = (((uint64_t)timer->ft0.dwHighDateTime) << 32) |
+           timer->ft0.dwLowDateTime;
+       t1 = (((uint64_t)timer->ft1.dwHighDateTime) << 32) |
+           timer->ft1.dwLowDateTime;
+       return ((t1 - t0) / 10);
+#elif JEMALLOC_CLOCK_GETTIME
+       return (((timer->ts1.tv_sec - timer->ts0.tv_sec) * 1000000) +
+           (timer->ts1.tv_nsec - timer->ts0.tv_nsec) / 1000);
+#else
        return (((timer->tv1.tv_sec - timer->tv0.tv_sec) * 1000000) +
            timer->tv1.tv_usec - timer->tv0.tv_usec);
+#endif
 }
 
 void
@@ -33,7 +61,7 @@ timer_ratio(timedelta_t *a, timedelta_t *b, char *buf, size_t buflen)
        int n;
 
        /* Whole. */
-       n = malloc_snprintf(&buf[i], buflen-i, "%"PRIu64, t0 / t1);
+       n = malloc_snprintf(&buf[i], buflen-i, "%"FMTu64, t0 / t1);
        i += n;
        if (i >= buflen)
                return;
@@ -50,7 +78,7 @@ timer_ratio(timedelta_t *a, timedelta_t *b, char *buf, size_t buflen)
                uint64_t round = (i+1 == buflen-1 && ((t0 * mult * 10 / t1) % 10
                    >= 5)) ? 1 : 0;
                n = malloc_snprintf(&buf[i], buflen-i,
-                   "%"PRIu64, (t0 * mult / t1) % 10 + round);
+                   "%"FMTu64, (t0 * mult / t1) % 10 + round);
                i += n;
                mult *= 10;
        }
index 980eca410820d2be9c4b32caf4bcfddfcfc20cb2..ee39fea7f1b4d163ecf6f019bcf17fbcc8d9629f 100644 (file)
@@ -31,8 +31,8 @@ compare_funcs(uint64_t nwarmup, uint64_t niter, const char *name_a,
        time_func(&timer_b, nwarmup, niter, func_b);
 
        timer_ratio(&timer_a, &timer_b, ratio_buf, sizeof(ratio_buf));
-       malloc_printf("%"PRIu64" iterations, %s=%"PRIu64"us, "
-           "%s=%"PRIu64"us, ratio=1:%s\n",
+       malloc_printf("%"FMTu64" iterations, %s=%"FMTu64"us, "
+           "%s=%"FMTu64"us, ratio=1:%s\n",
            niter, name_a, timer_usec(&timer_a), name_b, timer_usec(&timer_b),
            ratio_buf);
 
@@ -114,6 +114,10 @@ malloc_mus_free(void)
        void *p;
 
        p = malloc(1);
+       if (p == NULL) {
+               test_fail("Unexpected malloc() failure");
+               return;
+       }
        malloc_usable_size(p);
        free(p);
 }
@@ -124,6 +128,10 @@ malloc_sallocx_free(void)
        void *p;
 
        p = malloc(1);
+       if (p == NULL) {
+               test_fail("Unexpected malloc() failure");
+               return;
+       }
        if (sallocx(p, 0) < 1)
                test_fail("Unexpected sallocx() failure");
        free(p);
@@ -143,6 +151,10 @@ malloc_nallocx_free(void)
        void *p;
 
        p = malloc(1);
+       if (p == NULL) {
+               test_fail("Unexpected malloc() failure");
+               return;
+       }
        if (nallocx(1, 0) < 1)
                test_fail("Unexpected nallocx() failure");
        free(p);
index 88b31f6efdb8f70fd24a7d6ba736a66debb693e2..ba4be8702edac3bbb14618f59b7ffd5d1369e266 100644 (file)
@@ -1543,13 +1543,13 @@ TEST_BEGIN(test_gen_rand_64)
                }
                r = gen_rand64(ctx);
                assert_u64_eq(r, array64[i],
-                   "Mismatch at array64[%d]=%"PRIx64", gen=%"PRIx64, i,
+                   "Mismatch at array64[%d]=%"FMTx64", gen=%"FMTx64, i,
                    array64[i], r);
        }
        for (i = 0; i < COUNT_2; i++) {
                r = gen_rand64(ctx);
                assert_u64_eq(r, array64_2[i],
-                   "Mismatch at array64_2[%d]=%"PRIx64" gen=%"PRIx64"", i,
+                   "Mismatch at array64_2[%d]=%"FMTx64" gen=%"FMTx64"", i,
                    array64_2[i], r);
        }
        fini_gen_rand(ctx);
@@ -1580,13 +1580,13 @@ TEST_BEGIN(test_by_array_64)
                }
                r = gen_rand64(ctx);
                assert_u64_eq(r, array64[i],
-                   "Mismatch at array64[%d]=%"PRIx64" gen=%"PRIx64, i,
+                   "Mismatch at array64[%d]=%"FMTx64" gen=%"FMTx64, i,
                    array64[i], r);
        }
        for (i = 0; i < COUNT_2; i++) {
                r = gen_rand64(ctx);
                assert_u64_eq(r, array64_2[i],
-                   "Mismatch at array64_2[%d]=%"PRIx64" gen=%"PRIx64, i,
+                   "Mismatch at array64_2[%d]=%"FMTx64" gen=%"FMTx64, i,
                    array64_2[i], r);
        }
        fini_gen_rand(ctx);
index eb6136c79403937b61ecd41b17bcc7abb68191d7..bdd74f659cf31f95e69704ae21011cecdbb8f827 100644 (file)
@@ -4,48 +4,64 @@
 struct p##_test_s {                                                    \
        t       accum0;                                                 \
        t       x;                                                      \
+       t       s;                                                      \
 };                                                                     \
 typedef struct p##_test_s p##_test_t;
 
-#define        TEST_BODY(p, t, PRI) do {                                       \
+#define        TEST_BODY(p, t, tc, ta, FMT) do {                               \
        const p##_test_t tests[] = {                                    \
-               {-1, -1},                                               \
-               {-1,  0},                                               \
-               {-1,  1},                                               \
+               {(t)-1, (t)-1, (t)-2},                                  \
+               {(t)-1, (t) 0, (t)-2},                                  \
+               {(t)-1, (t) 1, (t)-2},                                  \
                                                                        \
-               { 0, -1},                                               \
-               { 0,  0},                                               \
-               { 0,  1},                                               \
+               {(t) 0, (t)-1, (t)-2},                                  \
+               {(t) 0, (t) 0, (t)-2},                                  \
+               {(t) 0, (t) 1, (t)-2},                                  \
                                                                        \
-               { 1, -1},                                               \
-               { 1,  0},                                               \
-               { 1,  1},                                               \
+               {(t) 1, (t)-1, (t)-2},                                  \
+               {(t) 1, (t) 0, (t)-2},                                  \
+               {(t) 1, (t) 1, (t)-2},                                  \
                                                                        \
-               {0, -(1 << 22)},                                        \
-               {0, (1 << 22)},                                         \
-               {(1 << 22), -(1 << 22)},                                \
-               {(1 << 22), (1 << 22)}                                  \
+               {(t)0, (t)-(1 << 22), (t)-2},                           \
+               {(t)0, (t)(1 << 22), (t)-2},                            \
+               {(t)(1 << 22), (t)-(1 << 22), (t)-2},                   \
+               {(t)(1 << 22), (t)(1 << 22), (t)-2}                     \
        };                                                              \
        unsigned i;                                                     \
                                                                        \
        for (i = 0; i < sizeof(tests)/sizeof(p##_test_t); i++) {        \
+               bool err;                                               \
                t accum = tests[i].accum0;                              \
-               assert_u64_eq(atomic_read_##p(&accum), tests[i].accum0, \
-                   "i=%u", i);                                         \
-               assert_u64_eq(atomic_add_##p(&accum, tests[i].x),       \
-                   tests[i].accum0 + tests[i].x,                       \
-                   "i=%u, accum=%#"PRI", x=%#"PRI,                     \
+               assert_##ta##_eq(atomic_read_##p(&accum),               \
+                   tests[i].accum0,                                    \
+                   "Erroneous read, i=%u", i);                         \
+                                                                       \
+               assert_##ta##_eq(atomic_add_##p(&accum, tests[i].x),    \
+                   (t)((tc)tests[i].accum0 + (tc)tests[i].x),          \
+                   "i=%u, accum=%"FMT", x=%"FMT,                       \
                    i, tests[i].accum0, tests[i].x);                    \
-               assert_u64_eq(atomic_read_##p(&accum), accum,           \
-                   "i=%u", i);                                         \
+               assert_##ta##_eq(atomic_read_##p(&accum), accum,        \
+                   "Erroneous add, i=%u", i);                          \
                                                                        \
                accum = tests[i].accum0;                                \
-               assert_u64_eq(atomic_sub_##p(&accum, tests[i].x),       \
-                   tests[i].accum0 - tests[i].x,                       \
-                   "i=%u, accum=%#"PRI", x=%#"PRI,                     \
+               assert_##ta##_eq(atomic_sub_##p(&accum, tests[i].x),    \
+                   (t)((tc)tests[i].accum0 - (tc)tests[i].x),          \
+                   "i=%u, accum=%"FMT", x=%"FMT,                       \
                    i, tests[i].accum0, tests[i].x);                    \
-               assert_u64_eq(atomic_read_##p(&accum), accum,           \
-                   "i=%u", i);                                         \
+               assert_##ta##_eq(atomic_read_##p(&accum), accum,        \
+                   "Erroneous sub, i=%u", i);                          \
+                                                                       \
+               accum = tests[i].accum0;                                \
+               err = atomic_cas_##p(&accum, tests[i].x, tests[i].s);   \
+               assert_b_eq(err, tests[i].accum0 != tests[i].x,         \
+                   "Erroneous cas success/failure result");            \
+               assert_##ta##_eq(accum, err ? tests[i].accum0 :         \
+                   tests[i].s, "Erroneous cas effect, i=%u", i);       \
+                                                                       \
+               accum = tests[i].accum0;                                \
+               atomic_write_##p(&accum, tests[i].s);                   \
+               assert_##ta##_eq(accum, tests[i].s,                     \
+                   "Erroneous write, i=%u", i);                        \
        }                                                               \
 } while (0)
 
@@ -56,7 +72,7 @@ TEST_BEGIN(test_atomic_uint64)
 #if !(LG_SIZEOF_PTR == 3 || LG_SIZEOF_INT == 3)
        test_skip("64-bit atomic operations not supported");
 #else
-       TEST_BODY(uint64, uint64_t, PRIx64);
+       TEST_BODY(uint64, uint64_t, uint64_t, u64, FMTx64);
 #endif
 }
 TEST_END
@@ -65,7 +81,15 @@ TEST_STRUCT(uint32, uint32_t)
 TEST_BEGIN(test_atomic_uint32)
 {
 
-       TEST_BODY(uint32, uint32_t, PRIx32);
+       TEST_BODY(uint32, uint32_t, uint32_t, u32, "#"FMTx32);
+}
+TEST_END
+
+TEST_STRUCT(p, void *)
+TEST_BEGIN(test_atomic_p)
+{
+
+       TEST_BODY(p, void *, uintptr_t, ptr, "p");
 }
 TEST_END
 
@@ -73,7 +97,7 @@ TEST_STRUCT(z, size_t)
 TEST_BEGIN(test_atomic_z)
 {
 
-       TEST_BODY(z, size_t, "zx");
+       TEST_BODY(z, size_t, size_t, zu, "#zx");
 }
 TEST_END
 
@@ -81,7 +105,7 @@ TEST_STRUCT(u, unsigned)
 TEST_BEGIN(test_atomic_u)
 {
 
-       TEST_BODY(u, unsigned, "x");
+       TEST_BODY(u, unsigned, unsigned, u, "#x");
 }
 TEST_END
 
@@ -92,6 +116,7 @@ main(void)
        return (test(
            test_atomic_uint64,
            test_atomic_uint32,
+           test_atomic_p,
            test_atomic_z,
            test_atomic_u));
 }
index 4ea94f8575215b76844591c5804bb62ecff08417..7da583d85c49322badc228e2062331440bf34b13 100644 (file)
@@ -23,7 +23,7 @@ TEST_BEGIN(test_bitmap_init)
                bitmap_info_init(&binfo, i);
                {
                        size_t j;
-                       bitmap_t *bitmap = malloc(sizeof(bitmap_t) *
+                       bitmap_t *bitmap = (bitmap_t *)malloc(sizeof(bitmap_t) *
                                bitmap_info_ngroups(&binfo));
                        bitmap_init(bitmap, &binfo);
 
@@ -46,7 +46,7 @@ TEST_BEGIN(test_bitmap_set)
                bitmap_info_init(&binfo, i);
                {
                        size_t j;
-                       bitmap_t *bitmap = malloc(sizeof(bitmap_t) *
+                       bitmap_t *bitmap = (bitmap_t *)malloc(sizeof(bitmap_t) *
                                bitmap_info_ngroups(&binfo));
                        bitmap_init(bitmap, &binfo);
 
@@ -69,7 +69,7 @@ TEST_BEGIN(test_bitmap_unset)
                bitmap_info_init(&binfo, i);
                {
                        size_t j;
-                       bitmap_t *bitmap = malloc(sizeof(bitmap_t) *
+                       bitmap_t *bitmap = (bitmap_t *)malloc(sizeof(bitmap_t) *
                                bitmap_info_ngroups(&binfo));
                        bitmap_init(bitmap, &binfo);
 
@@ -98,7 +98,7 @@ TEST_BEGIN(test_bitmap_sfu)
                bitmap_info_init(&binfo, i);
                {
                        ssize_t j;
-                       bitmap_t *bitmap = malloc(sizeof(bitmap_t) *
+                       bitmap_t *bitmap = (bitmap_t *)malloc(sizeof(bitmap_t) *
                                bitmap_info_ngroups(&binfo));
                        bitmap_init(bitmap, &binfo);
 
index c2126487257c774aa8c3ee05c1255972a2e11eb0..b117595994abb394cd4db46ac9e13bb11d8eb34a 100644 (file)
@@ -64,10 +64,10 @@ TEST_BEGIN(test_count_insert_search_remove)
 
                ks = (i & 1) ? strs[i] : (const char *)NULL;
                vs = (i & 2) ? strs[i] : (const char *)NULL;
-               assert_ptr_eq((void *)ks, (void *)k.s,
-                   "Key mismatch, i=%zu", i);
-               assert_ptr_eq((void *)vs, (void *)v.s,
-                   "Value mismatch, i=%zu", i);
+               assert_ptr_eq((void *)ks, (void *)k.s, "Key mismatch, i=%zu",
+                   i);
+               assert_ptr_eq((void *)vs, (void *)v.s, "Value mismatch, i=%zu",
+                   i);
        }
        assert_true(ckh_search(&ckh, missing, NULL, NULL),
            "Unexpected ckh_search() success");
@@ -90,14 +90,14 @@ TEST_BEGIN(test_count_insert_search_remove)
 
                ks = (i & 1) ? strs[i] : (const char *)NULL;
                vs = (i & 2) ? strs[i] : (const char *)NULL;
-               assert_ptr_eq((void *)ks, (void *)k.s,
-                   "Key mismatch, i=%zu", i);
-               assert_ptr_eq((void *)vs, (void *)v.s,
-                   "Value mismatch, i=%zu", i);
+               assert_ptr_eq((void *)ks, (void *)k.s, "Key mismatch, i=%zu",
+                   i);
+               assert_ptr_eq((void *)vs, (void *)v.s, "Value mismatch, i=%zu",
+                   i);
                assert_zu_eq(ckh_count(&ckh),
                    sizeof(strs)/sizeof(const char *) - i - 1,
                    "ckh_count() should return %zu, but it returned %zu",
-                   sizeof(strs)/sizeof(const char *) - i - 1,
+                       sizeof(strs)/sizeof(const char *) - i - 1,
                    ckh_count(&ckh));
        }
 
@@ -196,8 +196,8 @@ TEST_BEGIN(test_insert_iter_remove)
        }
 
        assert_zu_eq(ckh_count(&ckh), 0,
-           "ckh_count() should return %zu, but it returned %zu", ZU(0),
-           ckh_count(&ckh));
+           "ckh_count() should return %zu, but it returned %zu",
+           ZU(0), ckh_count(&ckh));
        ckh_delete(tsd, &ckh);
 #undef NITEMS
 }
index 301428f2cb119eacb6d1bc0c134572a0199a520c..b23dd1e9592a0294509ed27c00c9a10911aa0c64 100644 (file)
@@ -1,14 +1,26 @@
 #include "test/jemalloc_test.h"
 
 #ifdef JEMALLOC_FILL
+#  ifndef JEMALLOC_TEST_JUNK_OPT
+#    define JEMALLOC_TEST_JUNK_OPT "junk:true"
+#  endif
 const char *malloc_conf =
-    "abort:false,junk:true,zero:false,redzone:true,quarantine:0";
+    "abort:false,zero:false,redzone:true,quarantine:0," JEMALLOC_TEST_JUNK_OPT;
 #endif
 
 static arena_dalloc_junk_small_t *arena_dalloc_junk_small_orig;
 static arena_dalloc_junk_large_t *arena_dalloc_junk_large_orig;
 static huge_dalloc_junk_t *huge_dalloc_junk_orig;
-static void *most_recently_junked;
+static void *watch_for_junking;
+static bool saw_junking;
+
+static void
+watch_junking(void *p)
+{
+
+       watch_for_junking = p;
+       saw_junking = false;
+}
 
 static void
 arena_dalloc_junk_small_intercept(void *ptr, arena_bin_info_t *bin_info)
@@ -21,7 +33,8 @@ arena_dalloc_junk_small_intercept(void *ptr, arena_bin_info_t *bin_info)
                    "Missing junk fill for byte %zu/%zu of deallocated region",
                    i, bin_info->reg_size);
        }
-       most_recently_junked = ptr;
+       if (ptr == watch_for_junking)
+               saw_junking = true;
 }
 
 static void
@@ -35,7 +48,8 @@ arena_dalloc_junk_large_intercept(void *ptr, size_t usize)
                    "Missing junk fill for byte %zu/%zu of deallocated region",
                    i, usize);
        }
-       most_recently_junked = ptr;
+       if (ptr == watch_for_junking)
+               saw_junking = true;
 }
 
 static void
@@ -48,7 +62,8 @@ huge_dalloc_junk_intercept(void *ptr, size_t usize)
         * enough that it doesn't make sense to duplicate the decision logic in
         * test code, so don't actually check that the region is junk-filled.
         */
-       most_recently_junked = ptr;
+       if (ptr == watch_for_junking)
+               saw_junking = true;
 }
 
 static void
@@ -57,12 +72,14 @@ test_junk(size_t sz_min, size_t sz_max)
        char *s;
        size_t sz_prev, sz, i;
 
-       arena_dalloc_junk_small_orig = arena_dalloc_junk_small;
-       arena_dalloc_junk_small = arena_dalloc_junk_small_intercept;
-       arena_dalloc_junk_large_orig = arena_dalloc_junk_large;
-       arena_dalloc_junk_large = arena_dalloc_junk_large_intercept;
-       huge_dalloc_junk_orig = huge_dalloc_junk;
-       huge_dalloc_junk = huge_dalloc_junk_intercept;
+       if (opt_junk_free) {
+               arena_dalloc_junk_small_orig = arena_dalloc_junk_small;
+               arena_dalloc_junk_small = arena_dalloc_junk_small_intercept;
+               arena_dalloc_junk_large_orig = arena_dalloc_junk_large;
+               arena_dalloc_junk_large = arena_dalloc_junk_large_intercept;
+               huge_dalloc_junk_orig = huge_dalloc_junk;
+               huge_dalloc_junk = huge_dalloc_junk_intercept;
+       }
 
        sz_prev = 0;
        s = (char *)mallocx(sz_min, 0);
@@ -80,31 +97,35 @@ test_junk(size_t sz_min, size_t sz_max)
                }
 
                for (i = sz_prev; i < sz; i++) {
-                       assert_c_eq(s[i], 0xa5,
-                           "Newly allocated byte %zu/%zu isn't junk-filled",
-                           i, sz);
+                       if (opt_junk_alloc) {
+                               assert_c_eq(s[i], 0xa5,
+                                   "Newly allocated byte %zu/%zu isn't "
+                                   "junk-filled", i, sz);
+                       }
                        s[i] = 'a';
                }
 
                if (xallocx(s, sz+1, 0, 0) == sz) {
-                       void *junked = (void *)s;
-
+                       watch_junking(s);
                        s = (char *)rallocx(s, sz+1, 0);
                        assert_ptr_not_null((void *)s,
                            "Unexpected rallocx() failure");
-                       assert_ptr_eq(most_recently_junked, junked,
+                       assert_true(!opt_junk_free || saw_junking,
                            "Expected region of size %zu to be junk-filled",
                            sz);
                }
        }
 
+       watch_junking(s);
        dallocx(s, 0);
-       assert_ptr_eq(most_recently_junked, (void *)s,
+       assert_true(!opt_junk_free || saw_junking,
            "Expected region of size %zu to be junk-filled", sz);
 
-       arena_dalloc_junk_small = arena_dalloc_junk_small_orig;
-       arena_dalloc_junk_large = arena_dalloc_junk_large_orig;
-       huge_dalloc_junk = huge_dalloc_junk_orig;
+       if (opt_junk_free) {
+               arena_dalloc_junk_small = arena_dalloc_junk_small_orig;
+               arena_dalloc_junk_large = arena_dalloc_junk_large_orig;
+               huge_dalloc_junk = huge_dalloc_junk_orig;
+       }
 }
 
 TEST_BEGIN(test_junk_small)
@@ -119,7 +140,7 @@ TEST_BEGIN(test_junk_large)
 {
 
        test_skip_if(!config_fill);
-       test_junk(SMALL_MAXCLASS+1, arena_maxclass);
+       test_junk(SMALL_MAXCLASS+1, large_maxclass);
 }
 TEST_END
 
@@ -127,20 +148,32 @@ TEST_BEGIN(test_junk_huge)
 {
 
        test_skip_if(!config_fill);
-       test_junk(arena_maxclass+1, chunksize*2);
+       test_junk(large_maxclass+1, chunksize*2);
 }
 TEST_END
 
 arena_ralloc_junk_large_t *arena_ralloc_junk_large_orig;
 static void *most_recently_trimmed;
 
+static size_t
+shrink_size(size_t size)
+{
+       size_t shrink_size;
+
+       for (shrink_size = size - 1; nallocx(shrink_size, 0) == size;
+           shrink_size--)
+               ; /* Do nothing. */
+
+       return (shrink_size);
+}
+
 static void
 arena_ralloc_junk_large_intercept(void *ptr, size_t old_usize, size_t usize)
 {
 
        arena_ralloc_junk_large_orig(ptr, old_usize, usize);
-       assert_zu_eq(old_usize, arena_maxclass, "Unexpected old_usize");
-       assert_zu_eq(usize, arena_maxclass-PAGE, "Unexpected usize");
+       assert_zu_eq(old_usize, large_maxclass, "Unexpected old_usize");
+       assert_zu_eq(usize, shrink_size(large_maxclass), "Unexpected usize");
        most_recently_trimmed = ptr;
 }
 
@@ -148,13 +181,13 @@ TEST_BEGIN(test_junk_large_ralloc_shrink)
 {
        void *p1, *p2;
 
-       p1 = mallocx(arena_maxclass, 0);
+       p1 = mallocx(large_maxclass, 0);
        assert_ptr_not_null(p1, "Unexpected mallocx() failure");
 
        arena_ralloc_junk_large_orig = arena_ralloc_junk_large;
        arena_ralloc_junk_large = arena_ralloc_junk_large_intercept;
 
-       p2 = rallocx(p1, arena_maxclass-PAGE, 0);
+       p2 = rallocx(p1, shrink_size(large_maxclass), 0);
        assert_ptr_eq(p1, p2, "Unexpected move during shrink");
 
        arena_ralloc_junk_large = arena_ralloc_junk_large_orig;
@@ -180,6 +213,7 @@ TEST_BEGIN(test_junk_redzone)
        arena_redzone_corruption_t *arena_redzone_corruption_orig;
 
        test_skip_if(!config_fill);
+       test_skip_if(!opt_junk_alloc || !opt_junk_free);
 
        arena_redzone_corruption_orig = arena_redzone_corruption;
        arena_redzone_corruption = arena_redzone_corruption_replacement;
@@ -210,6 +244,7 @@ int
 main(void)
 {
 
+       assert(!config_fill || opt_junk_alloc || opt_junk_free);
        return (test(
            test_junk_small,
            test_junk_large,
diff --git a/src/jemalloc/test/unit/junk_alloc.c b/src/jemalloc/test/unit/junk_alloc.c
new file mode 100644 (file)
index 0000000..8db3331
--- /dev/null
@@ -0,0 +1,3 @@
+#define JEMALLOC_TEST_JUNK_OPT "junk:alloc"
+#include "junk.c"
+#undef JEMALLOC_TEST_JUNK_OPT
diff --git a/src/jemalloc/test/unit/junk_free.c b/src/jemalloc/test/unit/junk_free.c
new file mode 100644 (file)
index 0000000..482a61d
--- /dev/null
@@ -0,0 +1,3 @@
+#define JEMALLOC_TEST_JUNK_OPT "junk:free"
+#include "junk.c"
+#undef JEMALLOC_TEST_JUNK_OPT
diff --git a/src/jemalloc/test/unit/lg_chunk.c b/src/jemalloc/test/unit/lg_chunk.c
new file mode 100644 (file)
index 0000000..7e5df38
--- /dev/null
@@ -0,0 +1,26 @@
+#include "test/jemalloc_test.h"
+
+/*
+ * Make sure that opt.lg_chunk clamping is sufficient.  In practice, this test
+ * program will fail a debug assertion during initialization and abort (rather
+ * than the test soft-failing) if clamping is insufficient.
+ */
+const char *malloc_conf = "lg_chunk:0";
+
+TEST_BEGIN(test_lg_chunk_clamp)
+{
+       void *p;
+
+       p = mallocx(1, 0);
+       assert_ptr_not_null(p, "Unexpected mallocx() failure");
+       dallocx(p, 0);
+}
+TEST_END
+
+int
+main(void)
+{
+
+       return (test(
+           test_lg_chunk_clamp));
+}
index c70473cc92a666fd8ceba087baa2ed148973acb0..31e354ca7d472bd7ec001732dfde839e5d8f728a 100644 (file)
@@ -126,6 +126,7 @@ TEST_BEGIN(test_mallctl_config)
        assert_zu_eq(sz, sizeof(oldval), "Unexpected output size");     \
 } while (0)
 
+       TEST_MALLCTL_CONFIG(cache_oblivious);
        TEST_MALLCTL_CONFIG(debug);
        TEST_MALLCTL_CONFIG(fill);
        TEST_MALLCTL_CONFIG(lazy_lock);
@@ -164,7 +165,7 @@ TEST_BEGIN(test_mallctl_opt)
        TEST_MALLCTL_OPT(size_t, narenas, always);
        TEST_MALLCTL_OPT(ssize_t, lg_dirty_mult, always);
        TEST_MALLCTL_OPT(bool, stats_print, always);
-       TEST_MALLCTL_OPT(bool, junk, fill);
+       TEST_MALLCTL_OPT(const char *, junk, fill);
        TEST_MALLCTL_OPT(size_t, quarantine, fill);
        TEST_MALLCTL_OPT(bool, redzone, fill);
        TEST_MALLCTL_OPT(bool, zero, fill);
@@ -211,6 +212,126 @@ TEST_BEGIN(test_manpage_example)
 }
 TEST_END
 
+TEST_BEGIN(test_tcache_none)
+{
+       void *p0, *q, *p1;
+
+       test_skip_if(!config_tcache);
+
+       /* Allocate p and q. */
+       p0 = mallocx(42, 0);
+       assert_ptr_not_null(p0, "Unexpected mallocx() failure");
+       q = mallocx(42, 0);
+       assert_ptr_not_null(q, "Unexpected mallocx() failure");
+
+       /* Deallocate p and q, but bypass the tcache for q. */
+       dallocx(p0, 0);
+       dallocx(q, MALLOCX_TCACHE_NONE);
+
+       /* Make sure that tcache-based allocation returns p, not q. */
+       p1 = mallocx(42, 0);
+       assert_ptr_not_null(p1, "Unexpected mallocx() failure");
+       assert_ptr_eq(p0, p1, "Expected tcache to allocate cached region");
+
+       /* Clean up. */
+       dallocx(p1, MALLOCX_TCACHE_NONE);
+}
+TEST_END
+
+TEST_BEGIN(test_tcache)
+{
+#define        NTCACHES        10
+       unsigned tis[NTCACHES];
+       void *ps[NTCACHES];
+       void *qs[NTCACHES];
+       unsigned i;
+       size_t sz, psz, qsz;
+
+       test_skip_if(!config_tcache);
+
+       psz = 42;
+       qsz = nallocx(psz, 0) + 1;
+
+       /* Create tcaches. */
+       for (i = 0; i < NTCACHES; i++) {
+               sz = sizeof(unsigned);
+               assert_d_eq(mallctl("tcache.create", &tis[i], &sz, NULL, 0), 0,
+                   "Unexpected mallctl() failure, i=%u", i);
+       }
+
+       /* Exercise tcache ID recycling. */
+       for (i = 0; i < NTCACHES; i++) {
+               assert_d_eq(mallctl("tcache.destroy", NULL, NULL, &tis[i],
+                   sizeof(unsigned)), 0, "Unexpected mallctl() failure, i=%u",
+                   i);
+       }
+       for (i = 0; i < NTCACHES; i++) {
+               sz = sizeof(unsigned);
+               assert_d_eq(mallctl("tcache.create", &tis[i], &sz, NULL, 0), 0,
+                   "Unexpected mallctl() failure, i=%u", i);
+       }
+
+       /* Flush empty tcaches. */
+       for (i = 0; i < NTCACHES; i++) {
+               assert_d_eq(mallctl("tcache.flush", NULL, NULL, &tis[i],
+                   sizeof(unsigned)), 0, "Unexpected mallctl() failure, i=%u",
+                   i);
+       }
+
+       /* Cache some allocations. */
+       for (i = 0; i < NTCACHES; i++) {
+               ps[i] = mallocx(psz, MALLOCX_TCACHE(tis[i]));
+               assert_ptr_not_null(ps[i], "Unexpected mallocx() failure, i=%u",
+                   i);
+               dallocx(ps[i], MALLOCX_TCACHE(tis[i]));
+
+               qs[i] = mallocx(qsz, MALLOCX_TCACHE(tis[i]));
+               assert_ptr_not_null(qs[i], "Unexpected mallocx() failure, i=%u",
+                   i);
+               dallocx(qs[i], MALLOCX_TCACHE(tis[i]));
+       }
+
+       /* Verify that tcaches allocate cached regions. */
+       for (i = 0; i < NTCACHES; i++) {
+               void *p0 = ps[i];
+               ps[i] = mallocx(psz, MALLOCX_TCACHE(tis[i]));
+               assert_ptr_not_null(ps[i], "Unexpected mallocx() failure, i=%u",
+                   i);
+               assert_ptr_eq(ps[i], p0,
+                   "Expected mallocx() to allocate cached region, i=%u", i);
+       }
+
+       /* Verify that reallocation uses cached regions. */
+       for (i = 0; i < NTCACHES; i++) {
+               void *q0 = qs[i];
+               qs[i] = rallocx(ps[i], qsz, MALLOCX_TCACHE(tis[i]));
+               assert_ptr_not_null(qs[i], "Unexpected rallocx() failure, i=%u",
+                   i);
+               assert_ptr_eq(qs[i], q0,
+                   "Expected rallocx() to allocate cached region, i=%u", i);
+               /* Avoid undefined behavior in case of test failure. */
+               if (qs[i] == NULL)
+                       qs[i] = ps[i];
+       }
+       for (i = 0; i < NTCACHES; i++)
+               dallocx(qs[i], MALLOCX_TCACHE(tis[i]));
+
+       /* Flush some non-empty tcaches. */
+       for (i = 0; i < NTCACHES/2; i++) {
+               assert_d_eq(mallctl("tcache.flush", NULL, NULL, &tis[i],
+                   sizeof(unsigned)), 0, "Unexpected mallctl() failure, i=%u",
+                   i);
+       }
+
+       /* Destroy tcaches. */
+       for (i = 0; i < NTCACHES; i++) {
+               assert_d_eq(mallctl("tcache.destroy", NULL, NULL, &tis[i],
+                   sizeof(unsigned)), 0, "Unexpected mallctl() failure, i=%u",
+                   i);
+       }
+}
+TEST_END
+
 TEST_BEGIN(test_thread_arena)
 {
        unsigned arena_old, arena_new, narenas;
@@ -228,6 +349,38 @@ TEST_BEGIN(test_thread_arena)
 }
 TEST_END
 
+TEST_BEGIN(test_arena_i_lg_dirty_mult)
+{
+       ssize_t lg_dirty_mult, orig_lg_dirty_mult, prev_lg_dirty_mult;
+       size_t sz = sizeof(ssize_t);
+
+       assert_d_eq(mallctl("arena.0.lg_dirty_mult", &orig_lg_dirty_mult, &sz,
+           NULL, 0), 0, "Unexpected mallctl() failure");
+
+       lg_dirty_mult = -2;
+       assert_d_eq(mallctl("arena.0.lg_dirty_mult", NULL, NULL,
+           &lg_dirty_mult, sizeof(ssize_t)), EFAULT,
+           "Unexpected mallctl() success");
+
+       lg_dirty_mult = (sizeof(size_t) << 3);
+       assert_d_eq(mallctl("arena.0.lg_dirty_mult", NULL, NULL,
+           &lg_dirty_mult, sizeof(ssize_t)), EFAULT,
+           "Unexpected mallctl() success");
+
+       for (prev_lg_dirty_mult = orig_lg_dirty_mult, lg_dirty_mult = -1;
+           lg_dirty_mult < (ssize_t)(sizeof(size_t) << 3); prev_lg_dirty_mult
+           = lg_dirty_mult, lg_dirty_mult++) {
+               ssize_t old_lg_dirty_mult;
+
+               assert_d_eq(mallctl("arena.0.lg_dirty_mult", &old_lg_dirty_mult,
+                   &sz, &lg_dirty_mult, sizeof(ssize_t)), 0,
+                   "Unexpected mallctl() failure");
+               assert_zd_eq(old_lg_dirty_mult, prev_lg_dirty_mult,
+                   "Unexpected old arena.0.lg_dirty_mult");
+       }
+}
+TEST_END
+
 TEST_BEGIN(test_arena_i_purge)
 {
        unsigned narenas;
@@ -307,6 +460,38 @@ TEST_BEGIN(test_arenas_initialized)
 }
 TEST_END
 
+TEST_BEGIN(test_arenas_lg_dirty_mult)
+{
+       ssize_t lg_dirty_mult, orig_lg_dirty_mult, prev_lg_dirty_mult;
+       size_t sz = sizeof(ssize_t);
+
+       assert_d_eq(mallctl("arenas.lg_dirty_mult", &orig_lg_dirty_mult, &sz,
+           NULL, 0), 0, "Unexpected mallctl() failure");
+
+       lg_dirty_mult = -2;
+       assert_d_eq(mallctl("arenas.lg_dirty_mult", NULL, NULL,
+           &lg_dirty_mult, sizeof(ssize_t)), EFAULT,
+           "Unexpected mallctl() success");
+
+       lg_dirty_mult = (sizeof(size_t) << 3);
+       assert_d_eq(mallctl("arenas.lg_dirty_mult", NULL, NULL,
+           &lg_dirty_mult, sizeof(ssize_t)), EFAULT,
+           "Unexpected mallctl() success");
+
+       for (prev_lg_dirty_mult = orig_lg_dirty_mult, lg_dirty_mult = -1;
+           lg_dirty_mult < (ssize_t)(sizeof(size_t) << 3); prev_lg_dirty_mult =
+           lg_dirty_mult, lg_dirty_mult++) {
+               ssize_t old_lg_dirty_mult;
+
+               assert_d_eq(mallctl("arenas.lg_dirty_mult", &old_lg_dirty_mult,
+                   &sz, &lg_dirty_mult, sizeof(ssize_t)), 0,
+                   "Unexpected mallctl() failure");
+               assert_zd_eq(old_lg_dirty_mult, prev_lg_dirty_mult,
+                   "Unexpected old arenas.lg_dirty_mult");
+       }
+}
+TEST_END
+
 TEST_BEGIN(test_arenas_constants)
 {
 
@@ -321,7 +506,8 @@ TEST_BEGIN(test_arenas_constants)
        TEST_ARENAS_CONSTANT(size_t, quantum, QUANTUM);
        TEST_ARENAS_CONSTANT(size_t, page, PAGE);
        TEST_ARENAS_CONSTANT(unsigned, nbins, NBINS);
-       TEST_ARENAS_CONSTANT(size_t, nlruns, nlclasses);
+       TEST_ARENAS_CONSTANT(unsigned, nlruns, nlclasses);
+       TEST_ARENAS_CONSTANT(unsigned, nhchunks, nhclasses);
 
 #undef TEST_ARENAS_CONSTANT
 }
@@ -357,12 +543,29 @@ TEST_BEGIN(test_arenas_lrun_constants)
        assert_zu_eq(name, expected, "Incorrect "#name" size");         \
 } while (0)
 
-       TEST_ARENAS_LRUN_CONSTANT(size_t, size, (1 << LG_PAGE));
+       TEST_ARENAS_LRUN_CONSTANT(size_t, size, LARGE_MINCLASS);
 
 #undef TEST_ARENAS_LRUN_CONSTANT
 }
 TEST_END
 
+TEST_BEGIN(test_arenas_hchunk_constants)
+{
+
+#define        TEST_ARENAS_HCHUNK_CONSTANT(t, name, expected) do {             \
+       t name;                                                         \
+       size_t sz = sizeof(t);                                          \
+       assert_d_eq(mallctl("arenas.hchunk.0."#name, &name, &sz, NULL,  \
+           0), 0, "Unexpected mallctl() failure");                     \
+       assert_zu_eq(name, expected, "Incorrect "#name" size");         \
+} while (0)
+
+       TEST_ARENAS_HCHUNK_CONSTANT(size_t, size, chunksize);
+
+#undef TEST_ARENAS_HCHUNK_CONSTANT
+}
+TEST_END
+
 TEST_BEGIN(test_arenas_extend)
 {
        unsigned narenas_before, arena, narenas_after;
@@ -413,13 +616,18 @@ main(void)
            test_mallctl_config,
            test_mallctl_opt,
            test_manpage_example,
+           test_tcache_none,
+           test_tcache,
            test_thread_arena,
+           test_arena_i_lg_dirty_mult,
            test_arena_i_purge,
            test_arena_i_dss,
            test_arenas_initialized,
+           test_arenas_lg_dirty_mult,
            test_arenas_constants,
            test_arenas_bin_constants,
            test_arenas_lrun_constants,
+           test_arenas_hchunk_constants,
            test_arenas_extend,
            test_stats_arenas));
 }
index bd289c54d6b86800580c0ce5e67edec3a9c0faa0..bde2a480b6bfda2f02ec9d84b440e5afe354d0b2 100644 (file)
@@ -85,6 +85,7 @@ TEST_END
 int
 main(void)
 {
+
        return (test(
            test_mq_basic,
            test_mq_threaded));
index d4bab8d013465e48912d6444de2543cb8740ab72..81490957292538b1291fc5670f1eb3d547377dcb 100644 (file)
@@ -2,7 +2,7 @@
 
 #ifdef JEMALLOC_PROF
 const char *malloc_conf =
-    "prof:true,prof_thread_active_init:false,lg_prof_sample:0,prof_final:false";
+    "prof:true,prof_thread_active_init:false,lg_prof_sample:0";
 #endif
 
 static void
index a00b1054f1d6f638067275a4f64190ec481711ce..a0e6ee921178cf7226e2fe23afd74da844d56134 100644 (file)
@@ -21,8 +21,9 @@ prof_dump_open_intercept(bool propagate_err, const char *filename)
 
 TEST_BEGIN(test_gdump)
 {
-       bool active;
-       void *p, *q;
+       bool active, gdump, gdump_old;
+       void *p, *q, *r, *s;
+       size_t sz;
 
        test_skip_if(!config_prof);
 
@@ -42,8 +43,32 @@ TEST_BEGIN(test_gdump)
        assert_ptr_not_null(q, "Unexpected mallocx() failure");
        assert_true(did_prof_dump_open, "Expected a profile dump");
 
+       gdump = false;
+       sz = sizeof(gdump_old);
+       assert_d_eq(mallctl("prof.gdump", &gdump_old, &sz, &gdump,
+           sizeof(gdump)), 0,
+           "Unexpected mallctl failure while disabling prof.gdump");
+       assert(gdump_old);
+       did_prof_dump_open = false;
+       r = mallocx(chunksize, 0);
+       assert_ptr_not_null(q, "Unexpected mallocx() failure");
+       assert_false(did_prof_dump_open, "Unexpected profile dump");
+
+       gdump = true;
+       sz = sizeof(gdump_old);
+       assert_d_eq(mallctl("prof.gdump", &gdump_old, &sz, &gdump,
+           sizeof(gdump)), 0,
+           "Unexpected mallctl failure while enabling prof.gdump");
+       assert(!gdump_old);
+       did_prof_dump_open = false;
+       s = mallocx(chunksize, 0);
+       assert_ptr_not_null(q, "Unexpected mallocx() failure");
+       assert_true(did_prof_dump_open, "Expected a profile dump");
+
        dallocx(p, 0);
        dallocx(q, 0);
+       dallocx(r, 0);
+       dallocx(s, 0);
 }
 TEST_END
 
index 3af1964294a9acb3910c1b89dd323044a5dedd24..69983e5e53037acd62839cb129e8953476a21c40 100644 (file)
@@ -16,6 +16,35 @@ prof_dump_open_intercept(bool propagate_err, const char *filename)
        return (fd);
 }
 
+static void
+set_prof_active(bool active)
+{
+
+       assert_d_eq(mallctl("prof.active", NULL, NULL, &active, sizeof(active)),
+           0, "Unexpected mallctl failure");
+}
+
+static size_t
+get_lg_prof_sample(void)
+{
+       size_t lg_prof_sample;
+       size_t sz = sizeof(size_t);
+
+       assert_d_eq(mallctl("prof.lg_sample", &lg_prof_sample, &sz, NULL, 0), 0,
+           "Unexpected mallctl failure while reading profiling sample rate");
+       return (lg_prof_sample);
+}
+
+static void
+do_prof_reset(size_t lg_prof_sample)
+{
+       assert_d_eq(mallctl("prof.reset", NULL, NULL,
+           &lg_prof_sample, sizeof(size_t)), 0,
+           "Unexpected mallctl failure while resetting profile data");
+       assert_zu_eq(lg_prof_sample, get_lg_prof_sample(),
+           "Expected profile sample rate change");
+}
+
 TEST_BEGIN(test_prof_reset_basic)
 {
        size_t lg_prof_sample_orig, lg_prof_sample, lg_prof_sample_next;
@@ -30,9 +59,7 @@ TEST_BEGIN(test_prof_reset_basic)
            "Unexpected mallctl failure while reading profiling sample rate");
        assert_zu_eq(lg_prof_sample_orig, 0,
            "Unexpected profiling sample rate");
-       sz = sizeof(size_t);
-       assert_d_eq(mallctl("prof.lg_sample", &lg_prof_sample, &sz, NULL, 0), 0,
-           "Unexpected mallctl failure while reading profiling sample rate");
+       lg_prof_sample = get_lg_prof_sample();
        assert_zu_eq(lg_prof_sample_orig, lg_prof_sample,
            "Unexpected disagreement between \"opt.lg_prof_sample\" and "
            "\"prof.lg_sample\"");
@@ -41,10 +68,7 @@ TEST_BEGIN(test_prof_reset_basic)
        for (i = 0; i < 2; i++) {
                assert_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0), 0,
                    "Unexpected mallctl failure while resetting profile data");
-               sz = sizeof(size_t);
-               assert_d_eq(mallctl("prof.lg_sample", &lg_prof_sample, &sz,
-                   NULL, 0), 0, "Unexpected mallctl failure while reading "
-                   "profiling sample rate");
+               lg_prof_sample = get_lg_prof_sample();
                assert_zu_eq(lg_prof_sample_orig, lg_prof_sample,
                    "Unexpected profile sample rate change");
        }
@@ -52,22 +76,15 @@ TEST_BEGIN(test_prof_reset_basic)
        /* Test resets with prof.lg_sample changes. */
        lg_prof_sample_next = 1;
        for (i = 0; i < 2; i++) {
-               assert_d_eq(mallctl("prof.reset", NULL, NULL,
-                   &lg_prof_sample_next, sizeof(size_t)), 0,
-                   "Unexpected mallctl failure while resetting profile data");
-               sz = sizeof(size_t);
-               assert_d_eq(mallctl("prof.lg_sample", &lg_prof_sample, &sz,
-                   NULL, 0), 0, "Unexpected mallctl failure while reading "
-                   "profiling sample rate");
+               do_prof_reset(lg_prof_sample_next);
+               lg_prof_sample = get_lg_prof_sample();
                assert_zu_eq(lg_prof_sample, lg_prof_sample_next,
                    "Expected profile sample rate change");
                lg_prof_sample_next = lg_prof_sample_orig;
        }
 
        /* Make sure the test code restored prof.lg_sample. */
-       sz = sizeof(size_t);
-       assert_d_eq(mallctl("prof.lg_sample", &lg_prof_sample, &sz, NULL, 0), 0,
-           "Unexpected mallctl failure while reading profiling sample rate");
+       lg_prof_sample = get_lg_prof_sample();
        assert_zu_eq(lg_prof_sample_orig, lg_prof_sample,
            "Unexpected disagreement between \"opt.lg_prof_sample\" and "
            "\"prof.lg_sample\"");
@@ -88,15 +105,12 @@ prof_dump_header_intercept(bool propagate_err, const prof_cnt_t *cnt_all)
 
 TEST_BEGIN(test_prof_reset_cleanup)
 {
-       bool active;
        void *p;
        prof_dump_header_t *prof_dump_header_orig;
 
        test_skip_if(!config_prof);
 
-       active = true;
-       assert_d_eq(mallctl("prof.active", NULL, NULL, &active, sizeof(active)),
-           0, "Unexpected mallctl failure while activating profiling");
+       set_prof_active(true);
 
        assert_zu_eq(prof_bt_count(), 0, "Expected 0 backtraces");
        p = mallocx(1, 0);
@@ -124,9 +138,7 @@ TEST_BEGIN(test_prof_reset_cleanup)
        dallocx(p, 0);
        assert_zu_eq(prof_bt_count(), 0, "Expected 0 backtraces");
 
-       active = false;
-       assert_d_eq(mallctl("prof.active", NULL, NULL, &active, sizeof(active)),
-           0, "Unexpected mallctl failure while deactivating profiling");
+       set_prof_active(false);
 }
 TEST_END
 
@@ -182,7 +194,7 @@ thd_start(void *varg)
 
 TEST_BEGIN(test_prof_reset)
 {
-       bool active;
+       size_t lg_prof_sample_orig;
        thd_t thds[NTHREADS];
        unsigned thd_args[NTHREADS];
        unsigned i;
@@ -195,9 +207,10 @@ TEST_BEGIN(test_prof_reset)
            "Unexpected pre-existing tdata structures");
        tdata_count = prof_tdata_count();
 
-       active = true;
-       assert_d_eq(mallctl("prof.active", NULL, NULL, &active, sizeof(active)),
-           0, "Unexpected mallctl failure while activating profiling");
+       lg_prof_sample_orig = get_lg_prof_sample();
+       do_prof_reset(5);
+
+       set_prof_active(true);
 
        for (i = 0; i < NTHREADS; i++) {
                thd_args[i] = i;
@@ -211,9 +224,9 @@ TEST_BEGIN(test_prof_reset)
        assert_zu_eq(prof_tdata_count(), tdata_count,
            "Unexpected remaining tdata structures");
 
-       active = false;
-       assert_d_eq(mallctl("prof.active", NULL, NULL, &active, sizeof(active)),
-           0, "Unexpected mallctl failure while deactivating profiling");
+       set_prof_active(false);
+
+       do_prof_reset(lg_prof_sample_orig);
 }
 TEST_END
 #undef NTHREADS
@@ -222,6 +235,58 @@ TEST_END
 #undef RESET_INTERVAL
 #undef DUMP_INTERVAL
 
+/* Test sampling at the same allocation site across resets. */
+#define        NITER 10
+TEST_BEGIN(test_xallocx)
+{
+       size_t lg_prof_sample_orig;
+       unsigned i;
+       void *ptrs[NITER];
+
+       test_skip_if(!config_prof);
+
+       lg_prof_sample_orig = get_lg_prof_sample();
+       set_prof_active(true);
+
+       /* Reset profiling. */
+       do_prof_reset(0);
+
+       for (i = 0; i < NITER; i++) {
+               void *p;
+               size_t sz, nsz;
+
+               /* Reset profiling. */
+               do_prof_reset(0);
+
+               /* Allocate small object (which will be promoted). */
+               p = ptrs[i] = mallocx(1, 0);
+               assert_ptr_not_null(p, "Unexpected mallocx() failure");
+
+               /* Reset profiling. */
+               do_prof_reset(0);
+
+               /* Perform successful xallocx(). */
+               sz = sallocx(p, 0);
+               assert_zu_eq(xallocx(p, sz, 0, 0), sz,
+                   "Unexpected xallocx() failure");
+
+               /* Perform unsuccessful xallocx(). */
+               nsz = nallocx(sz+1, 0);
+               assert_zu_eq(xallocx(p, nsz, 0, 0), sz,
+                   "Unexpected xallocx() success");
+       }
+
+       for (i = 0; i < NITER; i++) {
+               /* dallocx. */
+               dallocx(ptrs[i], 0);
+       }
+
+       set_prof_active(false);
+       do_prof_reset(lg_prof_sample_orig);
+}
+TEST_END
+#undef NITER
+
 int
 main(void)
 {
@@ -232,5 +297,6 @@ main(void)
        return (test(
            test_prof_reset_basic,
            test_prof_reset_cleanup,
-           test_prof_reset));
+           test_prof_reset,
+           test_xallocx));
 }
index 6066dba7adb4a001471de92c3c1492062922c8ec..f501158d7de072e5c56dc1d0d9609a01d6cb1142 100644 (file)
@@ -1,8 +1,7 @@
 #include "test/jemalloc_test.h"
 
 #ifdef JEMALLOC_PROF
-const char *malloc_conf =
-    "prof:true,prof_active:false,prof_final:false";
+const char *malloc_conf = "prof:true,prof_active:false";
 #endif
 
 static void
index 77a947d60fdff61805d7a4f6ccbd23d1179ba846..b54b3e86f574b3a3fc3bf42b8697b8fd38bdb1d1 100644 (file)
@@ -1,14 +1,30 @@
 #include "test/jemalloc_test.h"
 
+static rtree_node_elm_t *
+node_alloc(size_t nelms)
+{
+
+       return ((rtree_node_elm_t *)calloc(nelms, sizeof(rtree_node_elm_t)));
+}
+
+static void
+node_dalloc(rtree_node_elm_t *node)
+{
+
+       free(node);
+}
+
 TEST_BEGIN(test_rtree_get_empty)
 {
        unsigned i;
 
        for (i = 1; i <= (sizeof(uintptr_t) << 3); i++) {
-               rtree_t *rtree = rtree_new(i, malloc, free);
-               assert_u_eq(rtree_get(rtree, 0), 0,
+               rtree_t rtree;
+               assert_false(rtree_new(&rtree, i, node_alloc, node_dalloc),
+                   "Unexpected rtree_new() failure");
+               assert_ptr_null(rtree_get(&rtree, 0, false),
                    "rtree_get() should return NULL for empty tree");
-               rtree_delete(rtree);
+               rtree_delete(&rtree);
        }
 }
 TEST_END
@@ -16,19 +32,24 @@ TEST_END
 TEST_BEGIN(test_rtree_extrema)
 {
        unsigned i;
+       extent_node_t node_a, node_b;
 
        for (i = 1; i <= (sizeof(uintptr_t) << 3); i++) {
-               rtree_t *rtree = rtree_new(i, malloc, free);
+               rtree_t rtree;
+               assert_false(rtree_new(&rtree, i, node_alloc, node_dalloc),
+                   "Unexpected rtree_new() failure");
 
-               rtree_set(rtree, 0, 1);
-               assert_u_eq(rtree_get(rtree, 0), 1,
+               assert_false(rtree_set(&rtree, 0, &node_a),
+                   "Unexpected rtree_set() failure");
+               assert_ptr_eq(rtree_get(&rtree, 0, true), &node_a,
                    "rtree_get() should return previously set value");
 
-               rtree_set(rtree, ~((uintptr_t)0), 1);
-               assert_u_eq(rtree_get(rtree, ~((uintptr_t)0)), 1,
+               assert_false(rtree_set(&rtree, ~((uintptr_t)0), &node_b),
+                   "Unexpected rtree_set() failure");
+               assert_ptr_eq(rtree_get(&rtree, ~((uintptr_t)0), true), &node_b,
                    "rtree_get() should return previously set value");
 
-               rtree_delete(rtree);
+               rtree_delete(&rtree);
        }
 }
 TEST_END
@@ -40,26 +61,32 @@ TEST_BEGIN(test_rtree_bits)
        for (i = 1; i < (sizeof(uintptr_t) << 3); i++) {
                uintptr_t keys[] = {0, 1,
                    (((uintptr_t)1) << (sizeof(uintptr_t)*8-i)) - 1};
-               rtree_t *rtree = rtree_new(i, malloc, free);
+               extent_node_t node;
+               rtree_t rtree;
+
+               assert_false(rtree_new(&rtree, i, node_alloc, node_dalloc),
+                   "Unexpected rtree_new() failure");
 
                for (j = 0; j < sizeof(keys)/sizeof(uintptr_t); j++) {
-                       rtree_set(rtree, keys[j], 1);
+                       assert_false(rtree_set(&rtree, keys[j], &node),
+                           "Unexpected rtree_set() failure");
                        for (k = 0; k < sizeof(keys)/sizeof(uintptr_t); k++) {
-                               assert_u_eq(rtree_get(rtree, keys[k]), 1,
-                                   "rtree_get() should return previously set "
-                                   "value and ignore insignificant key bits; "
-                                   "i=%u, j=%u, k=%u, set key=%#"PRIxPTR", "
-                                   "get key=%#"PRIxPTR, i, j, k, keys[j],
-                                   keys[k]);
+                               assert_ptr_eq(rtree_get(&rtree, keys[k], true),
+                                   &node, "rtree_get() should return "
+                                   "previously set value and ignore "
+                                   "insignificant key bits; i=%u, j=%u, k=%u, "
+                                   "set key=%#"FMTxPTR", get key=%#"FMTxPTR, i,
+                                   j, k, keys[j], keys[k]);
                        }
-                       assert_u_eq(rtree_get(rtree,
-                           (((uintptr_t)1) << (sizeof(uintptr_t)*8-i))), 0,
+                       assert_ptr_null(rtree_get(&rtree,
+                           (((uintptr_t)1) << (sizeof(uintptr_t)*8-i)), false),
                            "Only leftmost rtree leaf should be set; "
                            "i=%u, j=%u", i, j);
-                       rtree_set(rtree, keys[j], 0);
+                       assert_false(rtree_set(&rtree, keys[j], NULL),
+                           "Unexpected rtree_set() failure");
                }
 
-               rtree_delete(rtree);
+               rtree_delete(&rtree);
        }
 }
 TEST_END
@@ -68,37 +95,43 @@ TEST_BEGIN(test_rtree_random)
 {
        unsigned i;
        sfmt_t *sfmt;
-#define        NSET 100
+#define        NSET 16
 #define        SEED 42
 
        sfmt = init_gen_rand(SEED);
        for (i = 1; i <= (sizeof(uintptr_t) << 3); i++) {
-               rtree_t *rtree = rtree_new(i, malloc, free);
                uintptr_t keys[NSET];
+               extent_node_t node;
                unsigned j;
+               rtree_t rtree;
+
+               assert_false(rtree_new(&rtree, i, node_alloc, node_dalloc),
+                   "Unexpected rtree_new() failure");
 
                for (j = 0; j < NSET; j++) {
                        keys[j] = (uintptr_t)gen_rand64(sfmt);
-                       rtree_set(rtree, keys[j], 1);
-                       assert_u_eq(rtree_get(rtree, keys[j]), 1,
+                       assert_false(rtree_set(&rtree, keys[j], &node),
+                           "Unexpected rtree_set() failure");
+                       assert_ptr_eq(rtree_get(&rtree, keys[j], true), &node,
                            "rtree_get() should return previously set value");
                }
                for (j = 0; j < NSET; j++) {
-                       assert_u_eq(rtree_get(rtree, keys[j]), 1,
+                       assert_ptr_eq(rtree_get(&rtree, keys[j], true), &node,
                            "rtree_get() should return previously set value");
                }
 
                for (j = 0; j < NSET; j++) {
-                       rtree_set(rtree, keys[j], 0);
-                       assert_u_eq(rtree_get(rtree, keys[j]), 0,
+                       assert_false(rtree_set(&rtree, keys[j], NULL),
+                           "Unexpected rtree_set() failure");
+                       assert_ptr_null(rtree_get(&rtree, keys[j], true),
                            "rtree_get() should return previously set value");
                }
                for (j = 0; j < NSET; j++) {
-                       assert_u_eq(rtree_get(rtree, keys[j]), 0,
+                       assert_ptr_null(rtree_get(&rtree, keys[j], true),
                            "rtree_get() should return previously set value");
                }
 
-               rtree_delete(rtree);
+               rtree_delete(&rtree);
        }
        fini_gen_rand(sfmt);
 #undef NSET
diff --git a/src/jemalloc/test/unit/size_classes.c b/src/jemalloc/test/unit/size_classes.c
new file mode 100644 (file)
index 0000000..d3aaebd
--- /dev/null
@@ -0,0 +1,89 @@
+#include "test/jemalloc_test.h"
+
+static size_t
+get_max_size_class(void)
+{
+       unsigned nhchunks;
+       size_t mib[4];
+       size_t sz, miblen, max_size_class;
+
+       sz = sizeof(unsigned);
+       assert_d_eq(mallctl("arenas.nhchunks", &nhchunks, &sz, NULL, 0), 0,
+           "Unexpected mallctl() error");
+
+       miblen = sizeof(mib) / sizeof(size_t);
+       assert_d_eq(mallctlnametomib("arenas.hchunk.0.size", mib, &miblen), 0,
+           "Unexpected mallctlnametomib() error");
+       mib[2] = nhchunks - 1;
+
+       sz = sizeof(size_t);
+       assert_d_eq(mallctlbymib(mib, miblen, &max_size_class, &sz, NULL, 0), 0,
+           "Unexpected mallctlbymib() error");
+
+       return (max_size_class);
+}
+
+TEST_BEGIN(test_size_classes)
+{
+       size_t size_class, max_size_class;
+       szind_t index, max_index;
+
+       max_size_class = get_max_size_class();
+       max_index = size2index(max_size_class);
+
+       for (index = 0, size_class = index2size(index); index < max_index ||
+           size_class < max_size_class; index++, size_class =
+           index2size(index)) {
+               assert_true(index < max_index,
+                   "Loop conditionals should be equivalent; index=%u, "
+                   "size_class=%zu (%#zx)", index, size_class, size_class);
+               assert_true(size_class < max_size_class,
+                   "Loop conditionals should be equivalent; index=%u, "
+                   "size_class=%zu (%#zx)", index, size_class, size_class);
+
+               assert_u_eq(index, size2index(size_class),
+                   "size2index() does not reverse index2size(): index=%u -->"
+                   " size_class=%zu --> index=%u --> size_class=%zu", index,
+                   size_class, size2index(size_class),
+                   index2size(size2index(size_class)));
+               assert_zu_eq(size_class, index2size(size2index(size_class)),
+                   "index2size() does not reverse size2index(): index=%u -->"
+                   " size_class=%zu --> index=%u --> size_class=%zu", index,
+                   size_class, size2index(size_class),
+                   index2size(size2index(size_class)));
+
+               assert_u_eq(index+1, size2index(size_class+1),
+                   "Next size_class does not round up properly");
+
+               assert_zu_eq(size_class, (index > 0) ?
+                   s2u(index2size(index-1)+1) : s2u(1),
+                   "s2u() does not round up to size class");
+               assert_zu_eq(size_class, s2u(size_class-1),
+                   "s2u() does not round up to size class");
+               assert_zu_eq(size_class, s2u(size_class),
+                   "s2u() does not compute same size class");
+               assert_zu_eq(s2u(size_class+1), index2size(index+1),
+                   "s2u() does not round up to next size class");
+       }
+
+       assert_u_eq(index, size2index(index2size(index)),
+           "size2index() does not reverse index2size()");
+       assert_zu_eq(max_size_class, index2size(size2index(max_size_class)),
+           "index2size() does not reverse size2index()");
+
+       assert_zu_eq(size_class, s2u(index2size(index-1)+1),
+           "s2u() does not round up to size class");
+       assert_zu_eq(size_class, s2u(size_class-1),
+           "s2u() does not round up to size class");
+       assert_zu_eq(size_class, s2u(size_class),
+           "s2u() does not compute same size class");
+}
+TEST_END
+
+int
+main(void)
+{
+
+       return (test(
+           test_size_classes));
+}
index 78c78cd501a8bc0a2d6592126b32dd2f4003851d..8e4bc631e336693e7f8d99691a4f375baeebabf5 100644 (file)
@@ -3,7 +3,7 @@
 TEST_BEGIN(test_stats_summary)
 {
        size_t *cactive;
-       size_t sz, allocated, active, mapped;
+       size_t sz, allocated, active, resident, mapped;
        int expected = config_stats ? 0 : ENOENT;
 
        sz = sizeof(cactive);
@@ -15,6 +15,8 @@ TEST_BEGIN(test_stats_summary)
            expected, "Unexpected mallctl() result");
        assert_d_eq(mallctl("stats.active", &active, &sz, NULL, 0), expected,
            "Unexpected mallctl() result");
+       assert_d_eq(mallctl("stats.resident", &resident, &sz, NULL, 0),
+           expected, "Unexpected mallctl() result");
        assert_d_eq(mallctl("stats.mapped", &mapped, &sz, NULL, 0), expected,
            "Unexpected mallctl() result");
 
@@ -23,34 +25,10 @@ TEST_BEGIN(test_stats_summary)
                    "active should be no larger than cactive");
                assert_zu_le(allocated, active,
                    "allocated should be no larger than active");
-               assert_zu_le(active, mapped,
-                   "active should be no larger than mapped");
-       }
-}
-TEST_END
-
-TEST_BEGIN(test_stats_chunks)
-{
-       size_t current, high;
-       uint64_t total;
-       size_t sz;
-       int expected = config_stats ? 0 : ENOENT;
-
-       sz = sizeof(size_t);
-       assert_d_eq(mallctl("stats.chunks.current", &current, &sz, NULL, 0),
-           expected, "Unexpected mallctl() result");
-       sz = sizeof(uint64_t);
-       assert_d_eq(mallctl("stats.chunks.total", &total, &sz, NULL, 0),
-           expected, "Unexpected mallctl() result");
-       sz = sizeof(size_t);
-       assert_d_eq(mallctl("stats.chunks.high", &high, &sz, NULL, 0), expected,
-           "Unexpected mallctl() result");
-
-       if (config_stats) {
-               assert_zu_le(current, high,
-                   "current should be no larger than high");
-               assert_u64_le((uint64_t)high, total,
-                   "high should be no larger than total");
+               assert_zu_lt(active, resident,
+                   "active should be less than resident");
+               assert_zu_lt(active, mapped,
+                   "active should be less than mapped");
        }
 }
 TEST_END
@@ -64,7 +42,7 @@ TEST_BEGIN(test_stats_huge)
        size_t sz;
        int expected = config_stats ? 0 : ENOENT;
 
-       p = mallocx(arena_maxclass+1, 0);
+       p = mallocx(large_maxclass+1, 0);
        assert_ptr_not_null(p, "Unexpected mallocx() failure");
 
        assert_d_eq(mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch)), 0,
@@ -97,7 +75,7 @@ TEST_END
 TEST_BEGIN(test_stats_arenas_summary)
 {
        unsigned arena;
-       void *little, *large;
+       void *little, *large, *huge;
        uint64_t epoch;
        size_t sz;
        int expected = config_stats ? 0 : ENOENT;
@@ -110,8 +88,10 @@ TEST_BEGIN(test_stats_arenas_summary)
 
        little = mallocx(SMALL_MAXCLASS, 0);
        assert_ptr_not_null(little, "Unexpected mallocx() failure");
-       large = mallocx(arena_maxclass, 0);
+       large = mallocx(large_maxclass, 0);
        assert_ptr_not_null(large, "Unexpected mallocx() failure");
+       huge = mallocx(chunksize, 0);
+       assert_ptr_not_null(huge, "Unexpected mallocx() failure");
 
        assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
            "Unexpected mallctl() failure");
@@ -139,6 +119,7 @@ TEST_BEGIN(test_stats_arenas_summary)
 
        dallocx(little, 0);
        dallocx(large, 0);
+       dallocx(huge, 0);
 }
 TEST_END
 
@@ -219,7 +200,7 @@ TEST_BEGIN(test_stats_arenas_large)
        assert_d_eq(mallctl("thread.arena", NULL, NULL, &arena, sizeof(arena)),
            0, "Unexpected mallctl() failure");
 
-       p = mallocx(arena_maxclass, 0);
+       p = mallocx(large_maxclass, 0);
        assert_ptr_not_null(p, "Unexpected mallocx() failure");
 
        assert_d_eq(mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch)), 0,
@@ -251,11 +232,51 @@ TEST_BEGIN(test_stats_arenas_large)
 }
 TEST_END
 
+TEST_BEGIN(test_stats_arenas_huge)
+{
+       unsigned arena;
+       void *p;
+       size_t sz, allocated;
+       uint64_t epoch, nmalloc, ndalloc;
+       int expected = config_stats ? 0 : ENOENT;
+
+       arena = 0;
+       assert_d_eq(mallctl("thread.arena", NULL, NULL, &arena, sizeof(arena)),
+           0, "Unexpected mallctl() failure");
+
+       p = mallocx(chunksize, 0);
+       assert_ptr_not_null(p, "Unexpected mallocx() failure");
+
+       assert_d_eq(mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch)), 0,
+           "Unexpected mallctl() failure");
+
+       sz = sizeof(size_t);
+       assert_d_eq(mallctl("stats.arenas.0.huge.allocated", &allocated, &sz,
+           NULL, 0), expected, "Unexpected mallctl() result");
+       sz = sizeof(uint64_t);
+       assert_d_eq(mallctl("stats.arenas.0.huge.nmalloc", &nmalloc, &sz,
+           NULL, 0), expected, "Unexpected mallctl() result");
+       assert_d_eq(mallctl("stats.arenas.0.huge.ndalloc", &ndalloc, &sz,
+           NULL, 0), expected, "Unexpected mallctl() result");
+
+       if (config_stats) {
+               assert_zu_gt(allocated, 0,
+                   "allocated should be greater than zero");
+               assert_zu_gt(nmalloc, 0,
+                   "nmalloc should be greater than zero");
+               assert_zu_ge(nmalloc, ndalloc,
+                   "nmalloc should be at least as large as ndalloc");
+       }
+
+       dallocx(p, 0);
+}
+TEST_END
+
 TEST_BEGIN(test_stats_arenas_bins)
 {
        unsigned arena;
        void *p;
-       size_t sz, allocated, curruns;
+       size_t sz, curruns, curregs;
        uint64_t epoch, nmalloc, ndalloc, nrequests, nfills, nflushes;
        uint64_t nruns, nreruns;
        int expected = config_stats ? 0 : ENOENT;
@@ -273,9 +294,6 @@ TEST_BEGIN(test_stats_arenas_bins)
        assert_d_eq(mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch)), 0,
            "Unexpected mallctl() failure");
 
-       sz = sizeof(size_t);
-       assert_d_eq(mallctl("stats.arenas.0.bins.0.allocated", &allocated, &sz,
-           NULL, 0), expected, "Unexpected mallctl() result");
        sz = sizeof(uint64_t);
        assert_d_eq(mallctl("stats.arenas.0.bins.0.nmalloc", &nmalloc, &sz,
            NULL, 0), expected, "Unexpected mallctl() result");
@@ -283,7 +301,11 @@ TEST_BEGIN(test_stats_arenas_bins)
            NULL, 0), expected, "Unexpected mallctl() result");
        assert_d_eq(mallctl("stats.arenas.0.bins.0.nrequests", &nrequests, &sz,
            NULL, 0), expected, "Unexpected mallctl() result");
+       sz = sizeof(size_t);
+       assert_d_eq(mallctl("stats.arenas.0.bins.0.curregs", &curregs, &sz,
+           NULL, 0), expected, "Unexpected mallctl() result");
 
+       sz = sizeof(uint64_t);
        assert_d_eq(mallctl("stats.arenas.0.bins.0.nfills", &nfills, &sz,
            NULL, 0), config_tcache ? expected : ENOENT,
            "Unexpected mallctl() result");
@@ -300,14 +322,14 @@ TEST_BEGIN(test_stats_arenas_bins)
            NULL, 0), expected, "Unexpected mallctl() result");
 
        if (config_stats) {
-               assert_zu_gt(allocated, 0,
-                   "allocated should be greater than zero");
                assert_u64_gt(nmalloc, 0,
                    "nmalloc should be greater than zero");
                assert_u64_ge(nmalloc, ndalloc,
                    "nmalloc should be at least as large as ndalloc");
                assert_u64_gt(nrequests, 0,
                    "nrequests should be greater than zero");
+               assert_zu_gt(curregs, 0,
+                   "allocated should be greater than zero");
                if (config_tcache) {
                        assert_u64_gt(nfills, 0,
                            "At least one fill should have occurred");
@@ -336,7 +358,7 @@ TEST_BEGIN(test_stats_arenas_lruns)
        assert_d_eq(mallctl("thread.arena", NULL, NULL, &arena, sizeof(arena)),
            0, "Unexpected mallctl() failure");
 
-       p = mallocx(SMALL_MAXCLASS+1, 0);
+       p = mallocx(LARGE_MINCLASS, 0);
        assert_ptr_not_null(p, "Unexpected mallocx() failure");
 
        assert_d_eq(mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch)), 0,
@@ -368,17 +390,58 @@ TEST_BEGIN(test_stats_arenas_lruns)
 }
 TEST_END
 
+TEST_BEGIN(test_stats_arenas_hchunks)
+{
+       unsigned arena;
+       void *p;
+       uint64_t epoch, nmalloc, ndalloc;
+       size_t curhchunks, sz;
+       int expected = config_stats ? 0 : ENOENT;
+
+       arena = 0;
+       assert_d_eq(mallctl("thread.arena", NULL, NULL, &arena, sizeof(arena)),
+           0, "Unexpected mallctl() failure");
+
+       p = mallocx(chunksize, 0);
+       assert_ptr_not_null(p, "Unexpected mallocx() failure");
+
+       assert_d_eq(mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch)), 0,
+           "Unexpected mallctl() failure");
+
+       sz = sizeof(uint64_t);
+       assert_d_eq(mallctl("stats.arenas.0.hchunks.0.nmalloc", &nmalloc, &sz,
+           NULL, 0), expected, "Unexpected mallctl() result");
+       assert_d_eq(mallctl("stats.arenas.0.hchunks.0.ndalloc", &ndalloc, &sz,
+           NULL, 0), expected, "Unexpected mallctl() result");
+       sz = sizeof(size_t);
+       assert_d_eq(mallctl("stats.arenas.0.hchunks.0.curhchunks", &curhchunks,
+           &sz, NULL, 0), expected, "Unexpected mallctl() result");
+
+       if (config_stats) {
+               assert_u64_gt(nmalloc, 0,
+                   "nmalloc should be greater than zero");
+               assert_u64_ge(nmalloc, ndalloc,
+                   "nmalloc should be at least as large as ndalloc");
+               assert_u64_gt(curhchunks, 0,
+                   "At least one chunk should be currently allocated");
+       }
+
+       dallocx(p, 0);
+}
+TEST_END
+
 int
 main(void)
 {
 
        return (test(
            test_stats_summary,
-           test_stats_chunks,
            test_stats_huge,
            test_stats_arenas_summary,
            test_stats_arenas_small,
            test_stats_arenas_large,
+           test_stats_arenas_huge,
            test_stats_arenas_bins,
-           test_stats_arenas_lruns));
+           test_stats_arenas_lruns,
+           test_stats_arenas_hchunks));
 }
index eb1c597699003a26484819189ecdedb477806b91..8be787fda935cc8e88e220f122b99c03eeeda423 100644 (file)
@@ -6,6 +6,7 @@ typedef unsigned int data_t;
 
 static bool data_cleanup_executed;
 
+malloc_tsd_types(data_, data_t)
 malloc_tsd_protos(, data_, data_t)
 
 void
@@ -55,9 +56,14 @@ static void *
 thd_start(void *arg)
 {
        data_t d = (data_t)(uintptr_t)arg;
+       void *p;
+
        assert_x_eq(*data_tsd_get(), DATA_INIT,
            "Initial tsd get should return initialization value");
 
+       p = malloc(1);
+       assert_ptr_not_null(p, "Unexpected malloc() failure");
+
        data_tsd_set(&d);
        assert_x_eq(*data_tsd_get(), d,
            "After tsd set, tsd get should return value that was set");
@@ -66,6 +72,7 @@ thd_start(void *arg)
        assert_x_eq(*data_tsd_get(), (data_t)(uintptr_t)arg,
            "Resetting local data should have no effect on tsd");
 
+       free(p);
        return (NULL);
 }
 
index 65a8f0c9c3217cc4dcc624fdd2fd961f2274afb3..93afc2b871020354ea066d52d0c29fe13fc8622e 100644 (file)
@@ -55,7 +55,7 @@ TEST_BEGIN(test_zero_large)
 {
 
        test_skip_if(!config_fill);
-       test_zero(SMALL_MAXCLASS+1, arena_maxclass);
+       test_zero(SMALL_MAXCLASS+1, large_maxclass);
 }
 TEST_END
 
@@ -63,7 +63,7 @@ TEST_BEGIN(test_zero_huge)
 {
 
        test_skip_if(!config_fill);
-       test_zero(arena_maxclass+1, chunksize*2);
+       test_zero(large_maxclass+1, chunksize*2);
 }
 TEST_END
 
index f8483a8ed9b406d75df80a6517606ba2add5dfea..169634a7c82555cb1bf4d2c71f444f0897215978 100644 (file)
@@ -79,11 +79,10 @@ use core::cmp::Ordering;
 use core::mem::{align_of_val, size_of_val};
 use core::intrinsics::abort;
 use core::mem;
+use core::mem::uninitialized;
 use core::ops::Deref;
-#[cfg(not(stage0))]
 use core::ops::CoerceUnsized;
 use core::ptr::{self, Shared};
-#[cfg(not(stage0))]
 use core::marker::Unsize;
 use core::hash::{Hash, Hasher};
 use core::{usize, isize};
@@ -135,8 +134,6 @@ unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
 #[stable(feature = "rust1", since = "1.0.0")]
 unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
 
-// remove cfg after new snapshot
-#[cfg(not(stage0))]
 #[unstable(feature = "coerce_unsized", issue = "27732")]
 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Arc<U>> for Arc<T> {}
 
@@ -157,8 +154,6 @@ unsafe impl<T: ?Sized + Sync + Send> Send for Weak<T> {}
 #[stable(feature = "rust1", since = "1.0.0")]
 unsafe impl<T: ?Sized + Sync + Send> Sync for Weak<T> {}
 
-// remove cfg after new snapshot
-#[cfg(not(stage0))]
 #[unstable(feature = "coerce_unsized", issue = "27732")]
 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Weak<U>> for Weak<T> {}
 
@@ -385,13 +380,6 @@ impl<T: ?Sized> Deref for Arc<T> {
 }
 
 impl<T: Clone> Arc<T> {
-    #[unstable(feature = "arc_make_unique", reason = "renamed to Arc::make_mut",
-               issue = "27718")]
-    #[rustc_deprecated(since = "1.4.0", reason = "renamed to Arc::make_mut")]
-    pub fn make_unique(this: &mut Self) -> &mut T {
-        Arc::make_mut(this)
-    }
-
     /// Make a mutable reference into the given `Arc<T>` by cloning the inner
     /// data if the `Arc<T>` doesn't have one strong reference and no weak
     /// references.
@@ -428,7 +416,7 @@ impl<T: Clone> Arc<T> {
         // weak count, there's no chance the ArcInner itself could be
         // deallocated.
         if this.inner().strong.compare_and_swap(1, 0, Acquire) != 1 {
-            // Another srong pointer exists; clone
+            // Another strong pointer exists; clone
             *this = Arc::new((**this).clone());
         } else if this.inner().weak.load(Relaxed) != 1 {
             // Relaxed suffices in the above because this is fundamentally an
@@ -567,9 +555,9 @@ impl<T: ?Sized> Drop for Arc<T> {
         // This structure has #[unsafe_no_drop_flag], so this drop glue may run
         // more than once (but it is guaranteed to be zeroed after the first if
         // it's run more than once)
-        let ptr = *self._ptr;
-        // if ptr.is_null() { return }
-        if ptr as *mut u8 as usize == 0 || ptr as *mut u8 as usize == mem::POST_DROP_USIZE {
+        let thin = *self._ptr as *const ();
+
+        if thin as usize == mem::POST_DROP_USIZE {
             return;
         }
 
@@ -722,9 +710,10 @@ impl<T: ?Sized> Drop for Weak<T> {
     /// ```
     fn drop(&mut self) {
         let ptr = *self._ptr;
+        let thin = ptr as *const ();
 
         // see comments above for why this check is here
-        if ptr as *mut u8 as usize == 0 || ptr as *mut u8 as usize == mem::POST_DROP_USIZE {
+        if thin as usize == mem::POST_DROP_USIZE {
             return;
         }
 
@@ -917,6 +906,35 @@ impl<T> From<T> for Arc<T> {
     }
 }
 
+impl<T> Weak<T> {
+    /// Constructs a new `Weak<T>` without an accompanying instance of T.
+    ///
+    /// This allocates memory for T, but does not initialize it. Calling
+    /// Weak<T>::upgrade() on the return value always gives None.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(downgraded_weak)]
+    ///
+    /// use std::sync::Weak;
+    ///
+    /// let empty: Weak<i64> = Weak::new();
+    /// ```
+    #[unstable(feature = "downgraded_weak",
+               reason = "recently added",
+               issue = "30425")]
+    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(),
+            }))}
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use std::clone::Clone;
@@ -1167,6 +1185,12 @@ mod tests {
         let foo_arc = Arc::from(foo);
         assert!(123 == *foo_arc);
     }
+
+    #[test]
+    fn test_new_weak() {
+        let foo: Weak<usize> = Weak::new();
+        assert!(foo.upgrade().is_none());
+    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
index 1afb49d9184b5001567325f9b30525a1eac646a6..be140469eb662b3493e979f8b0a7ddeea8f50b55 100644 (file)
@@ -222,12 +222,12 @@ impl<T: ?Sized> Drop for IntermediateBox<T> {
 }
 
 impl<T> Box<T> {
-    /// Allocates memory on the heap and then moves `x` into it.
+    /// Allocates memory on the heap and then places `x` into it.
     ///
     /// # Examples
     ///
     /// ```
-    /// let x = Box::new(5);
+    /// let five = Box::new(5);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline(always)]
@@ -237,17 +237,17 @@ impl<T> Box<T> {
 }
 
 impl<T: ?Sized> Box<T> {
-    /// Constructs a box from the raw pointer.
+    /// Constructs a box from a raw pointer.
     ///
-    /// After this function call, pointer is owned by resulting box.
-    /// In particular, it means that `Box` destructor calls destructor
-    /// of `T` and releases memory. Since the way `Box` allocates and
-    /// releases memory is unspecified, the only valid pointer to pass
-    /// to this function is the one taken from another `Box` with
-    /// `Box::into_raw` function.
+    /// After calling this function, the raw pointer is owned by the
+    /// resulting `Box`. Specifically, the `Box` destructor will call
+    /// the destructor of `T` and free the allocated memory. Since the
+    /// way `Box` allocates and releases memory is unspecified, the
+    /// only valid pointer to pass to this function is the one taken
+    /// from another `Box` via the `Box::into_raw` function.
     ///
-    /// Function is unsafe, because improper use of this function may
-    /// lead to memory problems like double-free, for example if the
+    /// This function is unsafe because improper use may lead to
+    /// memory problems. For example, a double-free may occur if the
     /// function is called twice on the same raw pointer.
     #[stable(feature = "box_raw", since = "1.4.0")]
     #[inline]
@@ -257,16 +257,16 @@ impl<T: ?Sized> Box<T> {
 
     /// Consumes the `Box`, returning the wrapped raw pointer.
     ///
-    /// After call to this function, caller is responsible for the memory
-    /// previously managed by `Box`, in particular caller should properly
-    /// destroy `T` and release memory. The proper way to do it is to
-    /// convert pointer back to `Box` with `Box::from_raw` function, because
-    /// `Box` does not specify, how memory is allocated.
+    /// After calling this function, the caller is responsible for the
+    /// memory previously managed by the `Box`. In particular, the
+    /// caller should properly destroy `T` and release the memory. The
+    /// proper way to do so is to convert the raw pointer back into a
+    /// `Box` with the `Box::from_raw` function.
     ///
     /// # Examples
     ///
     /// ```
-    /// let seventeen = Box::new(17u32);
+    /// let seventeen = Box::new(17);
     /// let raw = Box::into_raw(seventeen);
     /// let boxed_again = unsafe { Box::from_raw(raw) };
     /// ```
index f665b1e19241c7511bf60171abb155f169a98040..0a232ed0620d4a25106e5679b7c20e2462ab55a8 100644 (file)
 //! The [`heap`](heap/index.html) module defines the low-level interface to the
 //! default global allocator. It is not compatible with the libc allocator API.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "alloc"]
 #![crate_type = "rlib"]
-#![cfg_attr(stage0, staged_api)]
 #![allow(unused_attributes)]
 #![unstable(feature = "alloc",
             reason = "this library is unlikely to be stabilized in its current \
        issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/",
        test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))]
 #![no_std]
-#![cfg_attr(not(stage0), needs_allocator)]
+#![needs_allocator]
 
-#![cfg_attr(stage0, feature(rustc_attrs))]
-#![cfg_attr(stage0, feature(no_std))]
-#![cfg_attr(stage0, allow(unused_attributes))]
 #![feature(allocator)]
 #![feature(box_syntax)]
 #![feature(coerce_unsized)]
@@ -84,8 +78,6 @@
 #![feature(custom_attribute)]
 #![feature(fundamental)]
 #![feature(lang_items)]
-#![feature(nonzero)]
-#![feature(num_bits_bytes)]
 #![feature(optin_builtin_traits)]
 #![feature(placement_in_syntax)]
 #![feature(placement_new_protocol)]
 #![feature(unboxed_closures)]
 #![feature(unique)]
 #![feature(unsafe_no_drop_flag, filling_drop)]
-// SNAP 1af31d4
-#![allow(unused_features)]
-// SNAP 1af31d4
-#![allow(unused_attributes)]
 #![feature(dropck_parametricity)]
 #![feature(unsize)]
 #![feature(drop_in_place)]
 #![feature(fn_traits)]
+#![feature(const_fn)]
+
+#![feature(needs_allocator)]
 
+// Issue# 30592: Systematically use alloc_system during stage0 since jemalloc
+// might be unavailable or disabled
 #![cfg_attr(stage0, feature(alloc_system))]
-#![cfg_attr(not(stage0), feature(needs_allocator))]
 
 #![cfg_attr(test, feature(test, rustc_private, box_heap))]
 
@@ -142,15 +134,6 @@ mod boxed_test;
 pub mod arc;
 pub mod rc;
 pub mod raw_vec;
+pub mod oom;
 
-/// Common out-of-memory routine
-#[cold]
-#[inline(never)]
-#[unstable(feature = "oom", reason = "not a scrutinized interface",
-           issue = "27700")]
-pub fn oom() -> ! {
-    // FIXME(#14674): This really needs to do something other than just abort
-    //                here, but any printing done must be *guaranteed* to not
-    //                allocate.
-    unsafe { core::intrinsics::abort() }
-}
+pub use oom::oom;
diff --git a/src/liballoc/oom.rs b/src/liballoc/oom.rs
new file mode 100644 (file)
index 0000000..d355d59
--- /dev/null
@@ -0,0 +1,42 @@
+// 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.
+
+use core::sync::atomic::{AtomicPtr, Ordering};
+use core::mem;
+use core::intrinsics;
+
+static OOM_HANDLER: AtomicPtr<()> = AtomicPtr::new(default_oom_handler as *mut ());
+
+fn default_oom_handler() -> ! {
+    // The default handler can't do much more since we can't assume the presence
+    // of libc or any way of printing an error message.
+    unsafe { intrinsics::abort() }
+}
+
+/// Common out-of-memory routine
+#[cold]
+#[inline(never)]
+#[unstable(feature = "oom", reason = "not a scrutinized interface",
+           issue = "27700")]
+pub fn oom() -> ! {
+    let value = OOM_HANDLER.load(Ordering::SeqCst);
+    let handler: fn() -> ! = unsafe { mem::transmute(value) };
+    handler();
+}
+
+/// Set a custom handler for out-of-memory conditions
+///
+/// To avoid recursive OOM failures, it is critical that the OOM handler does
+/// not allocate any memory itself.
+#[unstable(feature = "oom", reason = "not a scrutinized interface",
+           issue = "27700")]
+pub fn set_oom_handler(handler: fn() -> !) {
+    OOM_HANDLER.store(handler as *mut (), Ordering::SeqCst);
+}
index 92f35c08a7debacc76496f339719508ca7d8c9db..c407cef25e74c30be2217e6f8e612273257f537d 100644 (file)
@@ -16,7 +16,6 @@ use super::oom;
 use super::boxed::Box;
 use core::ops::Drop;
 use core::cmp;
-use core;
 
 /// A low-level utility for more ergonomically allocating, reallocating, and deallocating a
 /// a buffer of memory on the heap without having to worry about all the corner cases
@@ -240,6 +239,47 @@ impl<T> RawVec<T> {
         }
     }
 
+    /// Attempts to double the size of the type's backing allocation in place. This is common
+    /// enough to want to do that it's easiest to just have a dedicated method. Slightly
+    /// more efficient logic can be provided for this than the general case.
+    ///
+    /// Returns true if the reallocation attempt has succeeded, or false otherwise.
+    ///
+    /// # Panics
+    ///
+    /// * Panics if T is zero-sized on the assumption that you managed to exhaust
+    ///   all `usize::MAX` slots in your imaginary buffer.
+    /// * Panics on 32-bit platforms if the requested capacity exceeds
+    ///   `isize::MAX` bytes.
+    #[inline(never)]
+    #[cold]
+    pub fn double_in_place(&mut self) -> bool {
+        unsafe {
+            let elem_size = mem::size_of::<T>();
+            let align = mem::align_of::<T>();
+
+            // since we set the capacity to usize::MAX when elem_size is
+            // 0, getting to here necessarily means the RawVec is overfull.
+            assert!(elem_size != 0, "capacity overflow");
+
+            // Since we guarantee that we never allocate more than isize::MAX bytes,
+            // `elem_size * self.cap <= isize::MAX` as a precondition, so this can't overflow
+            let new_cap = 2 * self.cap;
+            let new_alloc_size = new_cap * elem_size;
+
+            alloc_guard(new_alloc_size);
+            let size = heap::reallocate_inplace(self.ptr() as *mut _,
+                                                self.cap * elem_size,
+                                                new_alloc_size,
+                                                align);
+            if size >= new_alloc_size {
+                // We can't directly divide `size`.
+                self.cap = new_cap;
+            }
+            size >= new_alloc_size
+        }
+    }
+
     /// Ensures that the buffer contains at least enough space to hold
     /// `used_cap + needed_extra_cap` elements. If it doesn't already,
     /// will reallocate the minimum possible amount of memory necessary.
@@ -300,6 +340,22 @@ impl<T> RawVec<T> {
         }
     }
 
+    /// Calculates the buffer's new size given that it'll hold `used_cap +
+    /// needed_extra_cap` elements. This logic is used in amortized reserve methods.
+    /// Returns `(new_capacity, new_alloc_size)`.
+    fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize) -> (usize, usize) {
+        let elem_size = mem::size_of::<T>();
+        // Nothing we can really do about these checks :(
+        let required_cap = used_cap.checked_add(needed_extra_cap)
+                                   .expect("capacity overflow");
+        // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
+        let double_cap = self.cap * 2;
+        // `double_cap` guarantees exponential growth.
+        let new_cap = cmp::max(double_cap, required_cap);
+        let new_alloc_size = new_cap.checked_mul(elem_size).expect("capacity overflow");
+        (new_cap, new_alloc_size)
+    }
+
     /// Ensures that the buffer contains at least enough space to hold
     /// `used_cap + needed_extra_cap` elements. If it doesn't already have
     /// enough capacity, will reallocate enough space plus comfortable slack
@@ -360,17 +416,7 @@ impl<T> RawVec<T> {
                 return;
             }
 
-            // Nothing we can really do about these checks :(
-            let required_cap = used_cap.checked_add(needed_extra_cap)
-                                       .expect("capacity overflow");
-
-            // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`.
-            let double_cap = self.cap * 2;
-
-            // `double_cap` guarantees exponential growth.
-            let new_cap = cmp::max(double_cap, required_cap);
-
-            let new_alloc_size = new_cap.checked_mul(elem_size).expect("capacity overflow");
+            let (new_cap, new_alloc_size) = self.amortized_new_size(used_cap, needed_extra_cap);
             // FIXME: may crash and burn on over-reserve
             alloc_guard(new_alloc_size);
 
@@ -393,6 +439,55 @@ impl<T> RawVec<T> {
         }
     }
 
+    /// Attempts to ensure that the buffer contains at least enough space to hold
+    /// `used_cap + needed_extra_cap` elements. If it doesn't already have
+    /// enough capacity, will reallocate in place enough space plus comfortable slack
+    /// space to get amortized `O(1)` behaviour. Will limit this behaviour
+    /// if it would needlessly cause itself to panic.
+    ///
+    /// If `used_cap` exceeds `self.cap()`, this may fail to actually allocate
+    /// the requested space. This is not really unsafe, but the unsafe
+    /// code *you* write that relies on the behaviour of this function may break.
+    ///
+    /// Returns true if the reallocation attempt has succeeded, or false otherwise.
+    ///
+    /// # Panics
+    ///
+    /// * Panics if the requested capacity exceeds `usize::MAX` bytes.
+    /// * Panics on 32-bit platforms if the requested capacity exceeds
+    ///   `isize::MAX` bytes.
+    pub fn reserve_in_place(&mut self, used_cap: usize, needed_extra_cap: usize) -> bool {
+        unsafe {
+            let elem_size = mem::size_of::<T>();
+            let align = mem::align_of::<T>();
+
+            // NOTE: we don't early branch on ZSTs here because we want this
+            // to actually catch "asking for more than usize::MAX" in that case.
+            // If we make it past the first branch then we are guaranteed to
+            // panic.
+
+            // Don't actually need any more capacity. If the current `cap` is 0, we can't
+            // reallocate in place.
+            // Wrapping in case they give a bad `used_cap`
+            if self.cap().wrapping_sub(used_cap) >= needed_extra_cap || self.cap == 0 {
+                return false;
+            }
+
+            let (_, new_alloc_size) = self.amortized_new_size(used_cap, needed_extra_cap);
+            // FIXME: may crash and burn on over-reserve
+            alloc_guard(new_alloc_size);
+
+            let size = heap::reallocate_inplace(self.ptr() as *mut _,
+                                                self.cap * elem_size,
+                                                new_alloc_size,
+                                                align);
+            if size >= new_alloc_size {
+                self.cap = new_alloc_size / elem_size;
+            }
+            size >= new_alloc_size
+        }
+    }
+
     /// Shrinks the allocation down to the specified amount. If the given amount
     /// is 0, actually completely deallocates.
     ///
@@ -488,7 +583,7 @@ impl<T> Drop for RawVec<T> {
 
 #[inline]
 fn alloc_guard(alloc_size: usize) {
-    if core::usize::BITS < 64 {
+    if mem::size_of::<usize>() < 8 {
         assert!(alloc_size <= ::core::isize::MAX as usize,
                 "capacity overflow");
     }
index 404c7522403cd06c928b3101468389d65199ffbc..2c45e88bb24e802d904875583ce6ddfebe7e04e7 100644 (file)
@@ -162,11 +162,9 @@ use core::fmt;
 use core::hash::{Hasher, Hash};
 use core::intrinsics::{assume, abort};
 use core::marker;
-#[cfg(not(stage0))]
 use core::marker::Unsize;
-use core::mem::{self, align_of_val, size_of_val, forget};
+use core::mem::{self, align_of_val, size_of_val, forget, uninitialized};
 use core::ops::Deref;
-#[cfg(not(stage0))]
 use core::ops::CoerceUnsized;
 use core::ptr::{self, Shared};
 use core::convert::From;
@@ -196,8 +194,6 @@ impl<T: ?Sized> !marker::Send for Rc<T> {}
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: ?Sized> !marker::Sync for Rc<T> {}
 
-// remove cfg after new snapshot
-#[cfg(not(stage0))]
 #[unstable(feature = "coerce_unsized", issue = "27732")]
 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Rc<U>> for Rc<T> {}
 
@@ -360,14 +356,6 @@ impl<T: ?Sized> Rc<T> {
 }
 
 impl<T: Clone> Rc<T> {
-    #[inline]
-    #[unstable(feature = "rc_make_unique", reason = "renamed to Rc::make_mut",
-               issue = "27718")]
-    #[rustc_deprecated(since = "1.4.0", reason = "renamed to Rc::make_mut")]
-    pub fn make_unique(&mut self) -> &mut T {
-        Rc::make_mut(self)
-    }
-
     /// Make a mutable reference into the given `Rc<T>` by cloning the inner
     /// data if the `Rc<T>` doesn't have one strong reference and no weak
     /// references.
@@ -461,8 +449,9 @@ impl<T: ?Sized> Drop for Rc<T> {
     fn drop(&mut self) {
         unsafe {
             let ptr = *self._ptr;
-            if !(*(&ptr as *const _ as *const *const ())).is_null() &&
-               ptr as *const () as usize != mem::POST_DROP_USIZE {
+            let thin = ptr as *const ();
+
+            if thin as usize != mem::POST_DROP_USIZE {
                 self.dec_strong();
                 if self.strong() == 0 {
                     // destroy the contained object
@@ -731,8 +720,6 @@ impl<T: ?Sized> !marker::Send for Weak<T> {}
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: ?Sized> !marker::Sync for Weak<T> {}
 
-// remove cfg after new snapshot
-#[cfg(not(stage0))]
 #[unstable(feature = "coerce_unsized", issue = "27732")]
 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Weak<U>> for Weak<T> {}
 
@@ -796,8 +783,9 @@ impl<T: ?Sized> Drop for Weak<T> {
     fn drop(&mut self) {
         unsafe {
             let ptr = *self._ptr;
-            if !(*(&ptr as *const _ as *const *const ())).is_null() &&
-               ptr as *const () as usize != mem::POST_DROP_USIZE {
+            let thin = ptr as *const ();
+
+            if thin as usize != mem::POST_DROP_USIZE {
                 self.dec_weak();
                 // the weak count starts at 1, and will only go to zero if all
                 // the strong pointers have disappeared.
@@ -838,6 +826,37 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for Weak<T> {
     }
 }
 
+impl<T> Weak<T> {
+    /// Constructs a new `Weak<T>` without an accompanying instance of T.
+    ///
+    /// This allocates memory for T, but does not initialize it. Calling
+    /// Weak<T>::upgrade() on the return value always gives None.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(downgraded_weak)]
+    ///
+    /// use std::rc::Weak;
+    ///
+    /// let empty: Weak<i64> = Weak::new();
+    /// ```
+    #[unstable(feature = "downgraded_weak",
+               reason = "recently added",
+               issue="30425")]
+    pub fn new() -> Weak<T> {
+        unsafe {
+            Weak {
+                _ptr: Shared::new(Box::into_raw(box RcBox {
+                    strong: Cell::new(0),
+                    weak: Cell::new(1),
+                    value: uninitialized(),
+                })),
+            }
+        }
+    }
+}
+
 // NOTE: We checked_add here to deal with mem::forget safety. In particular
 // if you mem::forget Rcs (or Weaks), the ref-count can overflow, and then
 // you can free the allocation while outstanding Rcs (or Weaks) exist.
@@ -1130,6 +1149,12 @@ mod tests {
         let foo_rc = Rc::from(foo);
         assert!(123 == *foo_rc);
     }
+
+    #[test]
+    fn test_new_weak() {
+        let foo: Weak<usize> = Weak::new();
+        assert!(foo.upgrade().is_none());
+    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
index ec6c6ae31c17e7afffa24968c81cde77e2f94da2..91d229b819df197b84987f7f0b2002cfb8a05c00 100644 (file)
@@ -8,13 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "alloc_jemalloc"]
 #![crate_type = "rlib"]
-#![cfg_attr(stage0, staged_api)]
 #![no_std]
-#![cfg_attr(not(stage0), allocator)]
-#![cfg_attr(stage0, allow(improper_ctypes))]
+#![allocator]
 #![unstable(feature = "alloc_jemalloc",
             reason = "this library is unlikely to be stabilized in its current \
                       form or name",
@@ -22,7 +19,6 @@
 #![feature(allocator)]
 #![feature(libc)]
 #![feature(staged_api)]
-#![cfg_attr(stage0, feature(no_std))]
 
 extern crate libc;
 
@@ -59,7 +55,9 @@ extern "C" {
 const MIN_ALIGN: usize = 8;
 #[cfg(all(any(target_arch = "x86",
               target_arch = "x86_64",
-              target_arch = "aarch64")))]
+              target_arch = "aarch64",
+              target_arch = "powerpc64",
+              target_arch = "powerpc64le")))]
 const MIN_ALIGN: usize = 16;
 
 // MALLOCX_ALIGN(a) macro
@@ -112,3 +110,14 @@ pub extern "C" fn __rust_usable_size(size: usize, align: usize) -> usize {
     let flags = align_to_flags(align);
     unsafe { je_nallocx(size as size_t, flags) as usize }
 }
+
+// These symbols are used by jemalloc on android but the really old android
+// we're building on doesn't have them defined, so just make sure the symbols
+// 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 {
+    0
+}
index a6e89d5d00cb8c17acaf02c395e27296b442fe96..ffb6999d6e3fef7ef932832ecbb65f219cdcd8bd 100644 (file)
@@ -8,13 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "alloc_system"]
 #![crate_type = "rlib"]
-#![cfg_attr(stage0, staged_api)]
 #![no_std]
-#![cfg_attr(not(stage0), allocator)]
-#![cfg_attr(stage0, allow(improper_ctypes))]
+#![allocator]
 #![unstable(feature = "alloc_system",
             reason = "this library is unlikely to be stabilized in its current \
                       form or name",
@@ -22,7 +19,6 @@
 #![feature(allocator)]
 #![feature(libc)]
 #![feature(staged_api)]
-#![cfg_attr(stage0, feature(no_std))]
 
 extern crate libc;
 
@@ -33,7 +29,9 @@ extern crate libc;
               target_arch = "arm",
               target_arch = "mips",
               target_arch = "mipsel",
-              target_arch = "powerpc")))]
+              target_arch = "powerpc",
+              target_arch = "powerpc64",
+              target_arch = "powerpc64le")))]
 const MIN_ALIGN: usize = 8;
 #[cfg(all(any(target_arch = "x86_64",
               target_arch = "aarch64")))]
@@ -79,37 +77,17 @@ mod imp {
     use libc;
     use MIN_ALIGN;
 
-    extern "C" {
-        // Apparently android doesn't have posix_memalign
-        #[cfg(target_os = "android")]
-        fn memalign(align: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
-
-        #[cfg(not(target_os = "android"))]
-        fn posix_memalign(memptr: *mut *mut libc::c_void,
-                          align: libc::size_t,
-                          size: libc::size_t)
-                          -> libc::c_int;
-    }
-
     pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
         if align <= MIN_ALIGN {
             libc::malloc(size as libc::size_t) as *mut u8
         } else {
-            #[cfg(target_os = "android")]
-            unsafe fn more_aligned_malloc(size: usize, align: usize) -> *mut u8 {
-                memalign(align as libc::size_t, size as libc::size_t) as *mut u8
-            }
-            #[cfg(not(target_os = "android"))]
-            unsafe fn more_aligned_malloc(size: usize, align: usize) -> *mut u8 {
-                let mut out = ptr::null_mut();
-                let ret = posix_memalign(&mut out, align as libc::size_t, size as libc::size_t);
-                if ret != 0 {
-                    ptr::null_mut()
-                } else {
-                    out as *mut u8
-                }
+            let mut out = ptr::null_mut();
+            let ret = libc::posix_memalign(&mut out, align as libc::size_t, size as libc::size_t);
+            if ret != 0 {
+                ptr::null_mut()
+            } else {
+                out as *mut u8
             }
-            more_aligned_malloc(size, align)
         }
     }
 
index 7871135e9c21432416e19ecf68bd97c09c222e50..cd2093984e618ef785f7ee748351f5a06f3c5aed 100644 (file)
 //! arena but can only hold objects of a single type, and `Arena`, which is a
 //! more complex, slower arena which can hold objects of any type.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "arena"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
        test(no_crate_inject, attr(deny(warnings))))]
 
 #![feature(alloc)]
-#![feature(box_syntax)]
 #![feature(core_intrinsics)]
+#![feature(drop_in_place)]
 #![feature(heap_api)]
-#![feature(oom)]
-#![feature(ptr_as_ref)]
 #![feature(raw)]
+#![feature(heap_api)]
 #![feature(staged_api)]
 #![feature(dropck_parametricity)]
 #![cfg_attr(test, feature(test))]
 
-// SNAP 1af31d4
-#![allow(unused_features)]
-// SNAP 1af31d4
-#![allow(unused_attributes)]
+#![allow(deprecated)]
 
 extern crate alloc;
 
 use std::cell::{Cell, RefCell};
 use std::cmp;
 use std::intrinsics;
-use std::marker;
+use std::marker::{PhantomData, Send};
 use std::mem;
 use std::ptr;
-use std::rc::Rc;
+use std::slice;
 
-use alloc::heap::{allocate, deallocate};
+use alloc::heap;
+use alloc::raw_vec::RawVec;
 
-// The way arena uses arrays is really deeply awful. The arrays are
-// allocated, and have capacities reserved, but the fill for the array
-// will always stay at 0.
-#[derive(Clone, PartialEq)]
 struct Chunk {
-    data: Rc<RefCell<Vec<u8>>>,
+    data: RawVec<u8>,
+    /// Index of the first unused byte.
     fill: Cell<usize>,
+    /// Indicates whether objects with destructors are stored in this chunk.
     is_copy: Cell<bool>,
 }
 
 impl Chunk {
+    fn new(size: usize, is_copy: bool) -> Chunk {
+        Chunk {
+            data: RawVec::with_capacity(size),
+            fill: Cell::new(0),
+            is_copy: Cell::new(is_copy),
+        }
+    }
+
     fn capacity(&self) -> usize {
-        self.data.borrow().capacity()
+        self.data.cap()
     }
 
     unsafe fn as_ptr(&self) -> *const u8 {
-        self.data.borrow().as_ptr()
+        self.data.ptr()
+    }
+
+    // Walk down a chunk, running the destructors for any objects stored
+    // in it.
+    unsafe fn destroy(&self) {
+        let mut idx = 0;
+        let buf = self.as_ptr();
+        let fill = self.fill.get();
+
+        while idx < fill {
+            let tydesc_data = buf.offset(idx as isize) as *const usize;
+            let (tydesc, is_done) = un_bitpack_tydesc_ptr(*tydesc_data);
+            let (size, align) = ((*tydesc).size, (*tydesc).align);
+
+            let after_tydesc = idx + mem::size_of::<*const TyDesc>();
+
+            let start = round_up(after_tydesc, align);
+
+            if is_done {
+                ((*tydesc).drop_glue)(buf.offset(start as isize) as *const i8);
+            }
+
+            // Find where the next tydesc lives
+            idx = round_up(start + size, mem::align_of::<*const TyDesc>());
+        }
     }
 }
 
 /// A slower reflection-based arena that can allocate objects of any type.
 ///
-/// This arena uses `Vec<u8>` as a backing store to allocate objects from. For
-/// each allocated object, the arena stores a pointer to the type descriptor
+/// This arena uses `RawVec<u8>` as a backing store to allocate objects from.
+/// For each allocated object, the arena stores a pointer to the type descriptor
 /// followed by the object (potentially with alignment padding after each
 /// element). When the arena is destroyed, it iterates through all of its
 /// chunks, and uses the tydesc information to trace through the objects,
@@ -99,14 +124,17 @@ impl Chunk {
 /// than objects without destructors. This reduces overhead when initializing
 /// plain-old-data (`Copy` types) and means we don't need to waste time running
 /// their destructors.
+#[unstable(feature = "rustc_private",
+           reason = "Private to rustc", issue = "0")]
+#[rustc_deprecated(since = "1.6.0-dev", reason =
+"The reflection-based arena is superseded by the any-arena crate")]
 pub struct Arena<'longer_than_self> {
-    // The head is separated out from the list as a unbenchmarked
-    // microoptimization, to avoid needing to case on the list to access the
-    // head.
+    // The heads are separated out from the list as a unbenchmarked
+    // microoptimization, to avoid needing to case on the list to access a head.
     head: RefCell<Chunk>,
     copy_head: RefCell<Chunk>,
     chunks: RefCell<Vec<Chunk>>,
-    _marker: marker::PhantomData<*mut &'longer_than_self ()>,
+    _marker: PhantomData<*mut &'longer_than_self ()>,
 }
 
 impl<'a> Arena<'a> {
@@ -118,29 +146,21 @@ impl<'a> Arena<'a> {
     /// Allocates a new Arena with `initial_size` bytes preallocated.
     pub fn new_with_size(initial_size: usize) -> Arena<'a> {
         Arena {
-            head: RefCell::new(chunk(initial_size, false)),
-            copy_head: RefCell::new(chunk(initial_size, true)),
+            head: RefCell::new(Chunk::new(initial_size, false)),
+            copy_head: RefCell::new(Chunk::new(initial_size, true)),
             chunks: RefCell::new(Vec::new()),
-            _marker: marker::PhantomData,
+            _marker: PhantomData,
         }
     }
 }
 
-fn chunk(size: usize, is_copy: bool) -> Chunk {
-    Chunk {
-        data: Rc::new(RefCell::new(Vec::with_capacity(size))),
-        fill: Cell::new(0),
-        is_copy: Cell::new(is_copy),
-    }
-}
-
 impl<'longer_than_self> Drop for Arena<'longer_than_self> {
     fn drop(&mut self) {
         unsafe {
-            destroy_chunk(&*self.head.borrow());
+            self.head.borrow().destroy();
             for chunk in self.chunks.borrow().iter() {
                 if !chunk.is_copy.get() {
-                    destroy_chunk(chunk);
+                    chunk.destroy();
                 }
             }
         }
@@ -152,33 +172,6 @@ fn round_up(base: usize, align: usize) -> usize {
     (base.checked_add(align - 1)).unwrap() & !(align - 1)
 }
 
-// Walk down a chunk, running the destructors for any objects stored
-// in it.
-unsafe fn destroy_chunk(chunk: &Chunk) {
-    let mut idx = 0;
-    let buf = chunk.as_ptr();
-    let fill = chunk.fill.get();
-
-    while idx < fill {
-        let tydesc_data = buf.offset(idx as isize) as *const usize;
-        let (tydesc, is_done) = un_bitpack_tydesc_ptr(*tydesc_data);
-        let (size, align) = ((*tydesc).size, (*tydesc).align);
-
-        let after_tydesc = idx + mem::size_of::<*const TyDesc>();
-
-        let start = round_up(after_tydesc, align);
-
-        // debug!("freeing object: idx = {}, size = {}, align = {}, done = {}",
-        //        start, size, align, is_done);
-        if is_done {
-            ((*tydesc).drop_glue)(buf.offset(start as isize) as *const i8);
-        }
-
-        // Find where the next tydesc lives
-        idx = round_up(start + size, mem::align_of::<*const TyDesc>());
-    }
-}
-
 // We encode whether the object a tydesc describes has been
 // initialized in the arena in the low bit of the tydesc pointer. This
 // is necessary in order to properly do cleanup if a panic occurs
@@ -195,6 +188,9 @@ fn un_bitpack_tydesc_ptr(p: usize) -> (*const TyDesc, bool) {
 // HACK(eddyb) TyDesc replacement using a trait object vtable.
 // This could be replaced in the future with a custom DST layout,
 // or `&'static (drop_glue, size, align)` created by a `const fn`.
+// Requirements:
+// * rvalue promotion (issue #1056)
+// * mem::{size_of, align_of} must be const fns
 struct TyDesc {
     drop_glue: fn(*const i8),
     size: usize,
@@ -210,7 +206,7 @@ impl<T: ?Sized> AllTypes for T {}
 unsafe fn get_tydesc<T>() -> *const TyDesc {
     use std::raw::TraitObject;
 
-    let ptr = &*(1 as *const T);
+    let ptr = &*(heap::EMPTY as *const T);
 
     // Can use any trait that is implemented for all types.
     let obj = mem::transmute::<&AllTypes, TraitObject>(ptr);
@@ -218,31 +214,44 @@ unsafe fn get_tydesc<T>() -> *const TyDesc {
 }
 
 impl<'longer_than_self> Arena<'longer_than_self> {
-    fn chunk_size(&self) -> usize {
-        self.copy_head.borrow().capacity()
+    // Grows a given chunk and returns `false`, or replaces it with a bigger
+    // chunk and returns `true`.
+    // This method is shared by both parts of the arena.
+    #[cold]
+    fn alloc_grow(&self, head: &mut Chunk, used_cap: usize, n_bytes: usize) -> bool {
+        if head.data.reserve_in_place(used_cap, n_bytes) {
+            // In-place reallocation succeeded.
+            false
+        } else {
+            // Allocate a new chunk.
+            let new_min_chunk_size = cmp::max(n_bytes, head.capacity());
+            let new_chunk = Chunk::new((new_min_chunk_size + 1).next_power_of_two(), false);
+            let old_chunk = mem::replace(head, new_chunk);
+            if old_chunk.fill.get() != 0 {
+                self.chunks.borrow_mut().push(old_chunk);
+            }
+            true
+        }
     }
 
-    // Functions for the POD part of the arena
-    fn alloc_copy_grow(&self, n_bytes: usize, align: usize) -> *const u8 {
-        // Allocate a new chunk.
-        let new_min_chunk_size = cmp::max(n_bytes, self.chunk_size());
-        self.chunks.borrow_mut().push(self.copy_head.borrow().clone());
-
-        *self.copy_head.borrow_mut() = chunk((new_min_chunk_size + 1).next_power_of_two(), true);
-
-        self.alloc_copy_inner(n_bytes, align)
-    }
+    // Functions for the copyable part of the arena.
 
     #[inline]
     fn alloc_copy_inner(&self, n_bytes: usize, align: usize) -> *const u8 {
-        let start = round_up(self.copy_head.borrow().fill.get(), align);
-
-        let end = start + n_bytes;
-        if end > self.chunk_size() {
-            return self.alloc_copy_grow(n_bytes, align);
+        let mut copy_head = self.copy_head.borrow_mut();
+        let fill = copy_head.fill.get();
+        let mut start = round_up(fill, align);
+        let mut end = start + n_bytes;
+
+        if end > copy_head.capacity() {
+            if self.alloc_grow(&mut *copy_head, fill, end - fill) {
+                // Continuing with a newly allocated chunk
+                start = 0;
+                end = n_bytes;
+                copy_head.is_copy.set(true);
+            }
         }
 
-        let copy_head = self.copy_head.borrow();
         copy_head.fill.set(end);
 
         unsafe { copy_head.as_ptr().offset(start as isize) }
@@ -260,39 +269,28 @@ impl<'longer_than_self> Arena<'longer_than_self> {
         }
     }
 
-    // Functions for the non-POD part of the arena
-    fn alloc_noncopy_grow(&self, n_bytes: usize, align: usize) -> (*const u8, *const u8) {
-        // Allocate a new chunk.
-        let new_min_chunk_size = cmp::max(n_bytes, self.chunk_size());
-        self.chunks.borrow_mut().push(self.head.borrow().clone());
-
-        *self.head.borrow_mut() = chunk((new_min_chunk_size + 1).next_power_of_two(), false);
-
-        self.alloc_noncopy_inner(n_bytes, align)
-    }
+    // Functions for the non-copyable part of the arena.
 
     #[inline]
     fn alloc_noncopy_inner(&self, n_bytes: usize, align: usize) -> (*const u8, *const u8) {
-        // Be careful to not maintain any `head` borrows active, because
-        // `alloc_noncopy_grow` borrows it mutably.
-        let (start, end, tydesc_start, head_capacity) = {
-            let head = self.head.borrow();
-            let fill = head.fill.get();
-
-            let tydesc_start = fill;
-            let after_tydesc = fill + mem::size_of::<*const TyDesc>();
-            let start = round_up(after_tydesc, align);
-            let end = start + n_bytes;
-
-            (start, end, tydesc_start, head.capacity())
-        };
-
-        if end > head_capacity {
-            return self.alloc_noncopy_grow(n_bytes, align);
+        let mut head = self.head.borrow_mut();
+        let fill = head.fill.get();
+
+        let mut tydesc_start = fill;
+        let after_tydesc = fill + mem::size_of::<*const TyDesc>();
+        let mut start = round_up(after_tydesc, align);
+        let mut end = round_up(start + n_bytes, mem::align_of::<*const TyDesc>());
+
+        if end > head.capacity() {
+            if self.alloc_grow(&mut *head, tydesc_start, end - tydesc_start) {
+                // Continuing with a newly allocated chunk
+                tydesc_start = 0;
+                start = round_up(mem::size_of::<*const TyDesc>(), align);
+                end = round_up(start + n_bytes, mem::align_of::<*const TyDesc>());
+            }
         }
 
-        let head = self.head.borrow();
-        head.fill.set(round_up(end, mem::align_of::<*const TyDesc>()));
+        head.fill.set(end);
 
         unsafe {
             let buf = head.as_ptr();
@@ -337,140 +335,111 @@ impl<'longer_than_self> Arena<'longer_than_self> {
             }
         }
     }
-}
 
-#[test]
-fn test_arena_destructors() {
-    let arena = Arena::new();
-    for i in 0..10 {
-        // Arena allocate something with drop glue to make sure it
-        // doesn't leak.
-        arena.alloc(|| Rc::new(i));
-        // Allocate something with funny size and alignment, to keep
-        // things interesting.
-        arena.alloc(|| [0u8, 1u8, 2u8]);
+    /// Allocates a slice of bytes of requested length. The bytes are not guaranteed to be zero
+    /// if the arena has previously been cleared.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the requested length is too large and causes overflow.
+    pub fn alloc_bytes(&self, len: usize) -> &mut [u8] {
+        unsafe {
+            // Check for overflow.
+            self.copy_head.borrow().fill.get().checked_add(len).expect("length overflow");
+            let ptr = self.alloc_copy_inner(len, 1);
+            intrinsics::assume(!ptr.is_null());
+            slice::from_raw_parts_mut(ptr as *mut _, len)
+        }
     }
-}
 
-#[test]
-#[should_panic]
-fn test_arena_destructors_fail() {
-    let arena = Arena::new();
-    // Put some stuff in the arena.
-    for i in 0..10 {
-        // Arena allocate something with drop glue to make sure it
-        // doesn't leak.
-        arena.alloc(|| Rc::new(i));
-        // Allocate something with funny size and alignment, to keep
-        // things interesting.
-        arena.alloc(|| [0u8, 1, 2]);
-    }
-    // Now, panic while allocating
-    arena.alloc::<Rc<i32>, _>(|| {
-        panic!();
-    });
+    /// Clears the arena. Deallocates all but the longest chunk which may be reused.
+    pub fn clear(&mut self) {
+        unsafe {
+            self.head.borrow().destroy();
+            self.head.borrow().fill.set(0);
+            self.copy_head.borrow().fill.set(0);
+            for chunk in self.chunks.borrow().iter() {
+                if !chunk.is_copy.get() {
+                    chunk.destroy();
+                }
+            }
+            self.chunks.borrow_mut().clear();
+        }
+    }
 }
 
 /// A faster arena that can hold objects of only one type.
 pub struct TypedArena<T> {
     /// A pointer to the next object to be allocated.
-    ptr: Cell<*const T>,
+    ptr: Cell<*mut T>,
 
     /// A pointer to the end of the allocated area. When this pointer is
     /// reached, a new chunk is allocated.
-    end: Cell<*const T>,
+    end: Cell<*mut T>,
 
-    /// A pointer to the first arena segment.
-    first: RefCell<*mut TypedArenaChunk<T>>,
+    /// A vector arena segments.
+    chunks: RefCell<Vec<TypedArenaChunk<T>>>,
 
     /// Marker indicating that dropping the arena causes its owned
     /// instances of `T` to be dropped.
-    _own: marker::PhantomData<T>,
+    _own: PhantomData<T>,
 }
 
 struct TypedArenaChunk<T> {
-    marker: marker::PhantomData<T>,
-
     /// Pointer to the next arena segment.
-    next: *mut TypedArenaChunk<T>,
-
-    /// The number of elements that this chunk can hold.
-    capacity: usize,
-
-    // Objects follow here, suitably aligned.
-}
-
-fn calculate_size<T>(capacity: usize) -> usize {
-    let mut size = mem::size_of::<TypedArenaChunk<T>>();
-    size = round_up(size, mem::align_of::<T>());
-    let elem_size = mem::size_of::<T>();
-    let elems_size = elem_size.checked_mul(capacity).unwrap();
-    size = size.checked_add(elems_size).unwrap();
-    size
+    storage: RawVec<T>,
 }
 
 impl<T> TypedArenaChunk<T> {
     #[inline]
-    unsafe fn new(next: *mut TypedArenaChunk<T>, capacity: usize) -> *mut TypedArenaChunk<T> {
-        let size = calculate_size::<T>(capacity);
-        let chunk =
-            allocate(size, mem::align_of::<TypedArenaChunk<T>>()) as *mut TypedArenaChunk<T>;
-        if chunk.is_null() {
-            alloc::oom()
-        }
-        (*chunk).next = next;
-        (*chunk).capacity = capacity;
-        chunk
+    unsafe fn new(capacity: usize) -> TypedArenaChunk<T> {
+        TypedArenaChunk { storage: RawVec::with_capacity(capacity) }
     }
 
-    /// Destroys this arena chunk. If the type descriptor is supplied, the
-    /// drop glue is called; otherwise, drop glue is not called.
+    /// Destroys this arena chunk.
     #[inline]
     unsafe fn destroy(&mut self, len: usize) {
-        // Destroy all the allocated objects.
+        // The branch on needs_drop() is an -O1 performance optimization.
+        // Without the branch, dropping TypedArena<u8> takes linear time.
         if intrinsics::needs_drop::<T>() {
             let mut start = self.start();
+            // Destroy all allocated objects.
             for _ in 0..len {
-                ptr::read(start as *const T); // run the destructor on the pointer
-                start = start.offset(mem::size_of::<T>() as isize)
+                ptr::drop_in_place(start);
+                start = start.offset(1);
             }
         }
-
-        // Destroy the next chunk.
-        let next = self.next;
-        let size = calculate_size::<T>(self.capacity);
-        let self_ptr: *mut TypedArenaChunk<T> = self;
-        deallocate(self_ptr as *mut u8,
-                   size,
-                   mem::align_of::<TypedArenaChunk<T>>());
-        if !next.is_null() {
-            let capacity = (*next).capacity;
-            (*next).destroy(capacity);
-        }
     }
 
     // Returns a pointer to the first allocated object.
     #[inline]
-    fn start(&self) -> *const u8 {
-        let this: *const TypedArenaChunk<T> = self;
-        unsafe { round_up(this.offset(1) as usize, mem::align_of::<T>()) as *const u8 }
+    fn start(&self) -> *mut T {
+        self.storage.ptr()
     }
 
     // Returns a pointer to the end of the allocated space.
     #[inline]
-    fn end(&self) -> *const u8 {
+    fn end(&self) -> *mut T {
         unsafe {
-            let size = mem::size_of::<T>().checked_mul(self.capacity).unwrap();
-            self.start().offset(size as isize)
+            if mem::size_of::<T>() == 0 {
+                // A pointer as large as possible for zero-sized elements.
+                !0 as *mut T
+            } else {
+                self.start().offset(self.storage.cap() as isize)
+            }
         }
     }
 }
 
+const PAGE: usize = 4096;
+
 impl<T> TypedArena<T> {
-    /// Creates a new `TypedArena` with preallocated space for eight objects.
+    /// Creates a new `TypedArena` with preallocated space for many objects.
     #[inline]
     pub fn new() -> TypedArena<T> {
-        TypedArena::with_capacity(8)
+        // Reserve at least one page.
+        let elem_size = cmp::max(1, mem::size_of::<T>());
+        TypedArena::with_capacity(PAGE / elem_size)
     }
 
     /// Creates a new `TypedArena` with preallocated space for the given number of
@@ -478,12 +447,12 @@ impl<T> TypedArena<T> {
     #[inline]
     pub fn with_capacity(capacity: usize) -> TypedArena<T> {
         unsafe {
-            let chunk = TypedArenaChunk::<T>::new(ptr::null_mut(), capacity);
+            let chunk = TypedArenaChunk::<T>::new(cmp::max(1, capacity));
             TypedArena {
-                ptr: Cell::new((*chunk).start() as *const T),
-                end: Cell::new((*chunk).end() as *const T),
-                first: RefCell::new(chunk),
-                _own: marker::PhantomData,
+                ptr: Cell::new(chunk.start()),
+                end: Cell::new(chunk.end()),
+                chunks: RefCell::new(vec![chunk]),
+                _own: PhantomData,
             }
         }
     }
@@ -496,24 +465,79 @@ impl<T> TypedArena<T> {
         }
 
         unsafe {
-            let ptr: &mut T = &mut *(self.ptr.get() as *mut T);
-            ptr::write(ptr, object);
-            self.ptr.set(self.ptr.get().offset(1));
-            ptr
+            if mem::size_of::<T>() == 0 {
+                self.ptr.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T);
+                let ptr = heap::EMPTY as *mut T;
+                // Don't drop the object. This `write` is equivalent to `forget`.
+                ptr::write(ptr, object);
+                &mut *ptr
+            } else {
+                let ptr = self.ptr.get();
+                // Advance the pointer.
+                self.ptr.set(self.ptr.get().offset(1));
+                // Write into uninitialized memory.
+                ptr::write(ptr, object);
+                &mut *ptr
+            }
         }
     }
 
     /// Grows the arena.
     #[inline(never)]
+    #[cold]
     fn grow(&self) {
         unsafe {
-            let chunk = *self.first.borrow_mut();
-            let new_capacity = (*chunk).capacity.checked_mul(2).unwrap();
-            let chunk = TypedArenaChunk::<T>::new(chunk, new_capacity);
-            self.ptr.set((*chunk).start() as *const T);
-            self.end.set((*chunk).end() as *const T);
-            *self.first.borrow_mut() = chunk
+            let mut chunks = self.chunks.borrow_mut();
+            let prev_capacity = chunks.last().unwrap().storage.cap();
+            let new_capacity = prev_capacity.checked_mul(2).unwrap();
+            if chunks.last_mut().unwrap().storage.double_in_place() {
+                self.end.set(chunks.last().unwrap().end());
+            } else {
+                let chunk = TypedArenaChunk::<T>::new(new_capacity);
+                self.ptr.set(chunk.start());
+                self.end.set(chunk.end());
+                chunks.push(chunk);
+            }
+        }
+    }
+    /// Clears the arena. Deallocates all but the longest chunk which may be reused.
+    pub fn clear(&mut self) {
+        unsafe {
+            // Clear the last chunk, which is partially filled.
+            let mut chunks_borrow = self.chunks.borrow_mut();
+            let last_idx = chunks_borrow.len() - 1;
+            self.clear_last_chunk(&mut chunks_borrow[last_idx]);
+            // If `T` is ZST, code below has no effect.
+            for mut chunk in chunks_borrow.drain(..last_idx) {
+                let cap = chunk.storage.cap();
+                chunk.destroy(cap);
+            }
+        }
+    }
+
+    // Drops the contents of the last chunk. The last chunk is partially empty, unlike all other
+    // chunks.
+    fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk<T>) {
+        // Determine how much was filled.
+        let start = last_chunk.start() as usize;
+        // We obtain the value of the pointer to the first uninitialized element.
+        let end = self.ptr.get() as usize;
+        // We then calculate the number of elements to be dropped in the last chunk,
+        // which is the filled area's length.
+        let diff = if mem::size_of::<T>() == 0 {
+            // `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get
+            // the number of zero-sized values in the last and only chunk, just out of caution.
+            // Recall that `end` was incremented for each allocated value.
+            end - start
+        } else {
+            (end - start) / mem::size_of::<T>()
+        };
+        // Pass that to the `destroy` method.
+        unsafe {
+            last_chunk.destroy(diff);
         }
+        // Reset the chunk.
+        self.ptr.set(last_chunk.start());
     }
 }
 
@@ -522,23 +546,32 @@ impl<T> Drop for TypedArena<T> {
     fn drop(&mut self) {
         unsafe {
             // Determine how much was filled.
-            let start = self.first.borrow().as_ref().unwrap().start() as usize;
-            let end = self.ptr.get() as usize;
-            let diff = (end - start) / mem::size_of::<T>();
-
-            // Pass that to the `destroy` method.
-            (**self.first.borrow_mut()).destroy(diff)
+            let mut chunks_borrow = self.chunks.borrow_mut();
+            let mut last_chunk = chunks_borrow.pop().unwrap();
+            // Drop the contents of the last chunk.
+            self.clear_last_chunk(&mut last_chunk);
+            // The last chunk will be dropped. Destroy all other chunks.
+            for chunk in chunks_borrow.iter_mut() {
+                let cap = chunk.storage.cap();
+                chunk.destroy(cap);
+            }
+            // RawVec handles deallocation of `last_chunk` and `self.chunks`.
         }
     }
 }
 
+unsafe impl<T: Send> Send for TypedArena<T> {}
+
 #[cfg(test)]
 mod tests {
     extern crate test;
     use self::test::Bencher;
     use super::{Arena, TypedArena};
+    use std::cell::Cell;
+    use std::rc::Rc;
 
     #[allow(dead_code)]
+    #[derive(Debug, Eq, PartialEq)]
     struct Point {
         x: i32,
         y: i32,
@@ -605,7 +638,7 @@ mod tests {
     #[bench]
     pub fn bench_copy_nonarena(b: &mut Bencher) {
         b.iter(|| {
-            let _: Box<_> = box Point { x: 1, y: 2, z: 3 };
+            let _: Box<_> = Box::new(Point { x: 1, y: 2, z: 3 });
         })
     }
 
@@ -632,6 +665,219 @@ mod tests {
         }
     }
 
+    #[test]
+    pub fn test_typed_arena_zero_sized() {
+        let arena = TypedArena::new();
+        for _ in 0..100000 {
+            arena.alloc(());
+        }
+    }
+
+    #[test]
+    pub fn test_arena_zero_sized() {
+        let arena = Arena::new();
+        let mut points = vec![];
+        for _ in 0..1000 {
+            for _ in 0..100 {
+                arena.alloc(|| ());
+            }
+            let point = arena.alloc(|| Point { x: 1, y: 2, z: 3 });
+            points.push(point);
+        }
+        for point in &points {
+            assert_eq!(**point, Point { x: 1, y: 2, z: 3 });
+        }
+    }
+
+    #[test]
+    pub fn test_typed_arena_clear() {
+        let mut arena = TypedArena::new();
+        for _ in 0..10 {
+            arena.clear();
+            for _ in 0..10000 {
+                arena.alloc(Point { x: 1, y: 2, z: 3 });
+            }
+        }
+    }
+
+    #[test]
+    pub fn test_arena_clear() {
+        let mut arena = Arena::new();
+        for _ in 0..10 {
+            arena.clear();
+            for _ in 0..10000 {
+                arena.alloc(|| Point { x: 1, y: 2, z: 3 });
+                arena.alloc(|| {
+                    Noncopy {
+                        string: "hello world".to_string(),
+                        array: vec![],
+                    }
+                });
+            }
+        }
+    }
+
+    #[test]
+    pub fn test_arena_alloc_bytes() {
+        let arena = Arena::new();
+        for i in 0..10000 {
+            arena.alloc(|| Point { x: 1, y: 2, z: 3 });
+            for byte in arena.alloc_bytes(i % 42).iter_mut() {
+                *byte = i as u8;
+            }
+        }
+    }
+
+    #[test]
+    fn test_arena_destructors() {
+        let arena = Arena::new();
+        for i in 0..10 {
+            // Arena allocate something with drop glue to make sure it
+            // doesn't leak.
+            arena.alloc(|| Rc::new(i));
+            // Allocate something with funny size and alignment, to keep
+            // things interesting.
+            arena.alloc(|| [0u8, 1u8, 2u8]);
+        }
+    }
+
+    #[test]
+    #[should_panic]
+    fn test_arena_destructors_fail() {
+        let arena = Arena::new();
+        // Put some stuff in the arena.
+        for i in 0..10 {
+            // Arena allocate something with drop glue to make sure it
+            // doesn't leak.
+            arena.alloc(|| Rc::new(i));
+            // Allocate something with funny size and alignment, to keep
+            // things interesting.
+            arena.alloc(|| [0u8, 1, 2]);
+        }
+        // Now, panic while allocating
+        arena.alloc::<Rc<i32>, _>(|| {
+            panic!();
+        });
+    }
+
+    // Drop tests
+
+    struct DropCounter<'a> {
+        count: &'a Cell<u32>,
+    }
+
+    impl<'a> Drop for DropCounter<'a> {
+        fn drop(&mut self) {
+            self.count.set(self.count.get() + 1);
+        }
+    }
+
+    #[test]
+    fn test_arena_drop_count() {
+        let counter = Cell::new(0);
+        {
+            let arena = Arena::new();
+            for _ in 0..100 {
+                // Allocate something with drop glue to make sure it doesn't leak.
+                arena.alloc(|| DropCounter { count: &counter });
+                // Allocate something with funny size and alignment, to keep
+                // things interesting.
+                arena.alloc(|| [0u8, 1u8, 2u8]);
+            }
+            // dropping
+        };
+        assert_eq!(counter.get(), 100);
+    }
+
+    #[test]
+    fn test_arena_drop_on_clear() {
+        let counter = Cell::new(0);
+        for i in 0..10 {
+            let mut arena = Arena::new();
+            for _ in 0..100 {
+                // Allocate something with drop glue to make sure it doesn't leak.
+                arena.alloc(|| DropCounter { count: &counter });
+                // Allocate something with funny size and alignment, to keep
+                // things interesting.
+                arena.alloc(|| [0u8, 1u8, 2u8]);
+            }
+            arena.clear();
+            assert_eq!(counter.get(), i * 100 + 100);
+        }
+    }
+
+    #[test]
+    fn test_typed_arena_drop_count() {
+        let counter = Cell::new(0);
+        {
+            let arena: TypedArena<DropCounter> = TypedArena::new();
+            for _ in 0..100 {
+                // Allocate something with drop glue to make sure it doesn't leak.
+                arena.alloc(DropCounter { count: &counter });
+            }
+        };
+        assert_eq!(counter.get(), 100);
+    }
+
+    #[test]
+    fn test_typed_arena_drop_on_clear() {
+        let counter = Cell::new(0);
+        let mut arena: TypedArena<DropCounter> = TypedArena::new();
+        for i in 0..10 {
+            for _ in 0..100 {
+                // Allocate something with drop glue to make sure it doesn't leak.
+                arena.alloc(DropCounter { count: &counter });
+            }
+            arena.clear();
+            assert_eq!(counter.get(), i * 100 + 100);
+        }
+    }
+
+    thread_local! {
+        static DROP_COUNTER: Cell<u32> = Cell::new(0)
+    }
+
+    struct SmallDroppable;
+
+    impl Drop for SmallDroppable {
+        fn drop(&mut self) {
+            DROP_COUNTER.with(|c| c.set(c.get() + 1));
+        }
+    }
+
+    #[test]
+    fn test_arena_drop_small_count() {
+        DROP_COUNTER.with(|c| c.set(0));
+        {
+            let arena = Arena::new();
+            for _ in 0..10 {
+                for _ in 0..10 {
+                    // Allocate something with drop glue to make sure it doesn't leak.
+                    arena.alloc(|| SmallDroppable);
+                }
+                // Allocate something with funny size and alignment, to keep
+                // things interesting.
+                arena.alloc(|| [0u8, 1u8, 2u8]);
+            }
+            // dropping
+        };
+        assert_eq!(DROP_COUNTER.with(|c| c.get()), 100);
+    }
+
+    #[test]
+    fn test_typed_arena_drop_small_count() {
+        DROP_COUNTER.with(|c| c.set(0));
+        {
+            let arena: TypedArena<SmallDroppable> = TypedArena::new();
+            for _ in 0..100 {
+                // Allocate something with drop glue to make sure it doesn't leak.
+                arena.alloc(SmallDroppable);
+            }
+            // dropping
+        };
+        assert_eq!(DROP_COUNTER.with(|c| c.get()), 100);
+    }
+
     #[bench]
     pub fn bench_noncopy(b: &mut Bencher) {
         let arena = TypedArena::new();
@@ -646,10 +892,10 @@ mod tests {
     #[bench]
     pub fn bench_noncopy_nonarena(b: &mut Bencher) {
         b.iter(|| {
-            let _: Box<_> = box Noncopy {
+            let _: Box<_> = Box::new(Noncopy {
                 string: "hello world".to_string(),
                 array: vec![1, 2, 3, 4, 5],
-            };
+            });
         })
     }
 
index c31c651f95c6b3b83bda5f7ca7778988f599c7ae..2afa4705539aa88dd4c28ce42e208fa2b8cb49a5 100644 (file)
@@ -1,3 +1,70 @@
+2016-01-04  Jakub Jelinek  <jakub@redhat.com>
+
+       Update copyright years.
+
+2015-12-18  Andris Pavenis  <andris.pavenis@iki.fi>
+
+       * configure.ac: Specify that DJGPP do not have mmap even when sys/mman.h exists
+       * configure: Regenerate
+
+2015-12-09  John David Anglin  <danglin@gcc.gnu.org>
+
+       PR 68115/libfortran
+       * configure.ac: Set libbacktrace_cv_sys_sync to no on hppa*-*-hpux*.
+       * configure: Regenerate.
+       * elf.c (backtrace_initialize): Cast __sync_bool_compare_and_swap call
+       to void.
+
+2015-09-17  Ian Lance Taylor  <iant@google.com>
+
+       * posix.c (backtrace_open): Cast second argument of open() to int.
+
+2015-09-11  Ian Lance Taylor  <iant@google.com>
+
+       * Makefile.am (backtrace.lo): Depend on internal.h.
+       (sort.lo, stest.lo): Add explicit dependencies.
+       * Makefile.in: Rebuild.
+
+2015-09-09  Hans-Peter Nilsson  <hp@axis.com>
+
+       * backtrace.c: #include <sys/types.h>.
+
+2015-09-08  Ian Lance Taylor  <iant@google.com>
+
+       PR other/67457
+       * backtrace.c: #include "internal.h".
+       (struct backtrace_data): Add can_alloc field.
+       (unwind): If can_alloc is false, don't try to get file/line
+       information.
+       (backtrace_full): Set can_alloc field in bdata.
+       * alloc.c (backtrace_alloc): Don't call error_callback if it is
+       NULL.
+       * mmap.c (backtrace_alloc): Likewise.
+       * internal.h: Update comments for backtrace_alloc and
+       backtrace_free.
+
+2015-09-08  Ian Lance Taylor  <iant@google.com>
+
+       PR other/67457
+       * mmap.c (backtrace_alloc): Correct test for mmap failure.
+
+2015-08-31  Ulrich Weigand  <Ulrich.Weigand@de.ibm.com>
+
+       * configure.ac: For spu-*-* targets, set have_fcntl to no.
+       * configure: Regenerate.
+
+2015-08-27  Ulrich Weigand  <Ulrich.Weigand@de.ibm.com>
+
+       * configure.ac: Remove [disable-shared] argument to LT_INIT.
+       Remove setting PIC_FLAG when building as target library.
+       * configure: Regenerate.
+
+2015-08-26  Hans-Peter Nilsson  <hp@axis.com>
+
+       * configure.ac: Only compile with -fPIC if the target
+       supports it.
+       * configure: Regenerate.
+
 2015-08-24  Ulrich Weigand  <Ulrich.Weigand@de.ibm.com>
 
        * configure.ac: Set have_mmap to no on spu-*-* targets.
 2012-09-17  Ian Lance Taylor  <iant@google.com>
 
        * Initial implementation.
+\f
+Copyright (C) 2012-2016 Free Software Foundation, Inc.
+
+Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.
index 5ab329c696d77d808f8359baa0074376da9c7f0f..6b60e3b3b07382a48c7ffbaf992876426234bdee 100644 (file)
@@ -6,7 +6,7 @@
 
        * configure.ac: Add --enable-host-shared.
        * configure: Regenerate.
-
+\f
 Copyright (C) 2013-2014 Free Software Foundation, Inc.
 
 Copying and distribution of this file, with or without modification,
index ea78c701632166d9832d89d1448f58fbc24954fa..a7df02590982255a96166a4be552f07e6f42cabb 100644 (file)
@@ -1,17 +1,17 @@
 # Makefile.am -- Backtrace Makefile.
-# Copyright (C) 2012-2015 Free Software Foundation, Inc.
+# Copyright (C) 2012-2016 Free Software Foundation, Inc.
 
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
 # met:
 
 #     (1) Redistributions of source code must retain the above copyright
-#     notice, this list of conditions and the following disclaimer.
+#     notice, this list of conditions and the following disclaimer. 
 
 #     (2) Redistributions in binary form must reproduce the above copyright
 #     notice, this list of conditions and the following disclaimer in
 #     the documentation and/or other materials provided with the
-#     distribution.
+#     distribution.  
 
 #     (3) The name of the author may not be used to
 #     endorse or promote products derived from this software without
@@ -116,7 +116,7 @@ endif NATIVE
 
 INCDIR = $(top_srcdir)/../include
 alloc.lo: config.h backtrace.h internal.h
-backtrace.lo: config.h backtrace.h
+backtrace.lo: config.h backtrace.h internal.h
 btest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h
 dwarf.lo: config.h $(INCDIR)/dwarf2.h $(INCDIR)/dwarf2.def \
        $(INCDIR)/filenames.h backtrace.h internal.h
@@ -130,5 +130,7 @@ posix.lo: config.h backtrace.h internal.h
 print.lo: config.h backtrace.h internal.h
 read.lo: config.h backtrace.h internal.h
 simple.lo: config.h backtrace.h internal.h
+sort.lo: config.h backtrace.h internal.h
+stest.lo: config.h backtrace.h internal.h
 state.lo: config.h backtrace.h backtrace-supported.h internal.h
 unknown.lo: config.h backtrace.h internal.h
index 16b1a72712ffd044dcb36849aa678abe8a7242aa..586b6a6eaa10e94d4bf4deb478e063f2653088ac 100644 (file)
 # met:
 
 #     (1) Redistributions of source code must retain the above copyright
-#     notice, this list of conditions and the following disclaimer.
+#     notice, this list of conditions and the following disclaimer. 
 
 #     (2) Redistributions in binary form must reproduce the above copyright
 #     notice, this list of conditions and the following disclaimer in
 #     the documentation and/or other materials provided with the
-#     distribution.
+#     distribution.  
 
 #     (3) The name of the author may not be used to
 #     endorse or promote products derived from this software without
@@ -137,10 +137,10 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
        $(LDFLAGS) -o $@
 SOURCES = $(libbacktrace_la_SOURCES) $(EXTRA_libbacktrace_la_SOURCES) \
        $(btest_SOURCES) $(stest_SOURCES)
-MULTISRCTOP =
-MULTIBUILDTOP =
-MULTIDIRS =
-MULTISUBDIR =
+MULTISRCTOP = 
+MULTIBUILDTOP = 
+MULTIDIRS = 
+MULTISUBDIR = 
 MULTIDO = true
 MULTICLEAN = true
 am__can_run_installinfo = \
@@ -162,6 +162,7 @@ AUTOMAKE = @AUTOMAKE@
 AWK = @AWK@
 BACKTRACE_FILE = @BACKTRACE_FILE@
 BACKTRACE_SUPPORTED = @BACKTRACE_SUPPORTED@
+BACKTRACE_SUPPORTS_DATA = @BACKTRACE_SUPPORTS_DATA@
 BACKTRACE_SUPPORTS_THREADS = @BACKTRACE_SUPPORTS_THREADS@
 BACKTRACE_USES_MALLOC = @BACKTRACE_USES_MALLOC@
 CC = @CC@
@@ -389,7 +390,7 @@ config.h: stamp-h1
 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
        @rm -f stamp-h1
        cd $(top_builddir) && $(SHELL) ./config.status config.h
-$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) 
        ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
        rm -f stamp-h1
        touch $@
@@ -407,7 +408,7 @@ clean-noinstLTLIBRARIES:
          echo "rm -f \"$${dir}/so_locations\""; \
          rm -f "$${dir}/so_locations"; \
        done
-libbacktrace.la: $(libbacktrace_la_OBJECTS) $(libbacktrace_la_DEPENDENCIES) $(EXTRA_libbacktrace_la_DEPENDENCIES)
+libbacktrace.la: $(libbacktrace_la_OBJECTS) $(libbacktrace_la_DEPENDENCIES) $(EXTRA_libbacktrace_la_DEPENDENCIES) 
        $(LINK)  $(libbacktrace_la_OBJECTS) $(libbacktrace_la_LIBADD) $(LIBS)
 
 clean-checkPROGRAMS:
@@ -418,10 +419,10 @@ clean-checkPROGRAMS:
        list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
        echo " rm -f" $$list; \
        rm -f $$list
-btest$(EXEEXT): $(btest_OBJECTS) $(btest_DEPENDENCIES) $(EXTRA_btest_DEPENDENCIES)
+btest$(EXEEXT): $(btest_OBJECTS) $(btest_DEPENDENCIES) $(EXTRA_btest_DEPENDENCIES) 
        @rm -f btest$(EXEEXT)
        $(btest_LINK) $(btest_OBJECTS) $(btest_LDADD) $(LIBS)
-stest$(EXEEXT): $(stest_OBJECTS) $(stest_DEPENDENCIES) $(EXTRA_stest_DEPENDENCIES)
+stest$(EXEEXT): $(stest_OBJECTS) $(stest_DEPENDENCIES) $(EXTRA_stest_DEPENDENCIES) 
        @rm -f stest$(EXEEXT)
        $(LINK) $(stest_OBJECTS) $(stest_LDADD) $(LIBS)
 
@@ -745,7 +746,7 @@ uninstall-am:
        uninstall-am
 
 alloc.lo: config.h backtrace.h internal.h
-backtrace.lo: config.h backtrace.h
+backtrace.lo: config.h backtrace.h internal.h
 btest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h
 dwarf.lo: config.h $(INCDIR)/dwarf2.h $(INCDIR)/dwarf2.def \
        $(INCDIR)/filenames.h backtrace.h internal.h
@@ -759,6 +760,8 @@ posix.lo: config.h backtrace.h internal.h
 print.lo: config.h backtrace.h internal.h
 read.lo: config.h backtrace.h internal.h
 simple.lo: config.h backtrace.h internal.h
+sort.lo: config.h backtrace.h internal.h
+stest.lo: config.h backtrace.h internal.h
 state.lo: config.h backtrace.h backtrace-supported.h internal.h
 unknown.lo: config.h backtrace.h internal.h
 
index c9d6a1406b7b2e58110593e5ef64fe18e1511204..a9f07a013f8feec7a4508b7fc7189fb188c90031 100644 (file)
@@ -1,5 +1,5 @@
 /* alloc.c -- Memory allocation without mmap.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
@@ -44,7 +44,8 @@ POSSIBILITY OF SUCH DAMAGE.  */
    backtrace functions may not be safely invoked from a signal
    handler.  */
 
-/* Allocate memory like malloc.  */
+/* Allocate memory like malloc.  If ERROR_CALLBACK is NULL, don't
+   report an error.  */
 
 void *
 backtrace_alloc (struct backtrace_state *state ATTRIBUTE_UNUSED,
@@ -55,7 +56,10 @@ backtrace_alloc (struct backtrace_state *state ATTRIBUTE_UNUSED,
 
   ret = malloc (size);
   if (ret == NULL)
-    error_callback (data, "malloc", errno);
+    {
+      if (error_callback)
+       error_callback (data, "malloc", errno);
+    }
   return ret;
 }
 
index 0fb23bba792dbb30d852723b9ac9a5d337e3aedc..6e4bfc21f25fba689eeb896486328f45cdc537b4 100644 (file)
@@ -1,7 +1,5 @@
 /* ANSI and traditional C compatability macros
-   Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
-   2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2013
-   Free Software Foundation, Inc.
+   Copyright (C) 1991-2015 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
 This program is free software; you can redistribute it and/or modify
@@ -276,6 +274,15 @@ So instead we use the macro below and test it against specific values.  */
 # endif /* GNUC >= 4.3 */
 #endif /* ATTRIBUTE_HOT */
 
+/* Attribute 'no_sanitize_undefined' was valid as of gcc 4.9.  */
+#ifndef ATTRIBUTE_NO_SANITIZE_UNDEFINED
+# if (GCC_VERSION >= 4009)
+#  define ATTRIBUTE_NO_SANITIZE_UNDEFINED __attribute__ ((no_sanitize_undefined))
+# else
+#  define ATTRIBUTE_NO_SANITIZE_UNDEFINED
+# endif /* GNUC >= 4.9 */
+#endif /* ATTRIBUTE_NO_SANITIZE_UNDEFINED */
+
 /* We use __extension__ in some places to suppress -pedantic warnings
    about GCC extensions.  This feature didn't work properly before
    gcc 2.8.  */
@@ -304,6 +311,15 @@ So instead we use the macro below and test it against specific values.  */
 #define ENUM_BITFIELD(TYPE) __extension__ enum TYPE
 #else
 #define ENUM_BITFIELD(TYPE) unsigned int
+#endif
+
+    /* This is used to mark a class or virtual function as final.  */
+#if __cplusplus >= 201103L
+#define GCC_FINAL final
+#elif GCC_VERSION >= 4007
+#define GCC_FINAL __final
+#else
+#define GCC_FINAL
 #endif
 
 #ifdef __cplusplus
index 40e4ff93cf68ace004289f81ff5e347d8e8f96a0..cb0ad0298e68a4bd3fc92ca20efadde0d1a0a8ca 100644 (file)
@@ -1,5 +1,5 @@
 /* atomic.c -- Support for atomic functions if not present.
-   Copyright (C) 2013-2015 Free Software Foundation, Inc.
+   Copyright (C) 2013-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index 976963e71041ba8dce060e56ce4f5949f7303801..ab34199fcd362cceefb43d28f2c28daad07cc1f8 100644 (file)
@@ -1,5 +1,5 @@
 /* backtrace-supported.h.in -- Whether stack backtrace is supported.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index 8941375c6cd5575ceecd08b65a1202218eb28568..b89bf554ac548d2ab2a88fe65aa3907ac354e4a8 100644 (file)
@@ -1,5 +1,5 @@
 /* backtrace.c -- Entry point for stack backtrace library.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
@@ -32,8 +32,11 @@ POSSIBILITY OF SUCH DAMAGE.  */
 
 #include "config.h"
 
+#include <sys/types.h>
+
 #include "unwind.h"
 #include "backtrace.h"
+#include "internal.h"
 
 /* The main backtrace_full routine.  */
 
@@ -53,6 +56,8 @@ struct backtrace_data
   void *data;
   /* Value to return from backtrace_full.  */
   int ret;
+  /* Whether there is any memory available.  */
+  int can_alloc;
 };
 
 /* Unwind library callback routine.  This is passed to
@@ -80,8 +85,11 @@ unwind (struct _Unwind_Context *context, void *vdata)
   if (!ip_before_insn)
     --pc;
 
-  bdata->ret = backtrace_pcinfo (bdata->state, pc, bdata->callback,
-                                bdata->error_callback, bdata->data);
+  if (!bdata->can_alloc)
+    bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, NULL);
+  else
+    bdata->ret = backtrace_pcinfo (bdata->state, pc, bdata->callback,
+                                  bdata->error_callback, bdata->data);
   if (bdata->ret != 0)
     return _URC_END_OF_STACK;
 
@@ -96,6 +104,7 @@ backtrace_full (struct backtrace_state *state, int skip,
                backtrace_error_callback error_callback, void *data)
 {
   struct backtrace_data bdata;
+  void *p;
 
   bdata.skip = skip + 1;
   bdata.state = state;
@@ -103,6 +112,18 @@ backtrace_full (struct backtrace_state *state, int skip,
   bdata.error_callback = error_callback;
   bdata.data = data;
   bdata.ret = 0;
+
+  /* If we can't allocate any memory at all, don't try to produce
+     file/line information.  */
+  p = backtrace_alloc (state, 4096, NULL, NULL);
+  if (p == NULL)
+    bdata.can_alloc = 0;
+  else
+    {
+      backtrace_free (state, p, 4096, NULL, NULL);
+      bdata.can_alloc = 1;
+    }
+
   _Unwind_Backtrace (unwind, &bdata);
   return bdata.ret;
 }
index f16ee36cbce9ad9ab00da24d0e146d017392efd1..d209219d9a419ea82a723d4a4bf4282a7a900a83 100644 (file)
@@ -1,5 +1,5 @@
 /* backtrace.h -- Public header file for stack backtrace library.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index a950a704f071d7caf2e3fd9fa5f04b9ae8d38e0c..0506d2b112186b7b61fa89b3e2ec20b7286a699b 100644 (file)
@@ -1,5 +1,5 @@
 /* btest.c -- Test for libbacktrace library
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
@@ -460,7 +460,7 @@ f23 (int f1line, int f2line)
                       (unsigned int) bdata.index, j + 1);
              bdata.failed = 1;
            }
-       }
+       }      
 
       check ("test3", 0, all, f3line, "f23", &bdata.failed);
       check ("test3", 1, all, f2line, "f22", &bdata.failed);
diff --git a/src/libbacktrace/config.guess b/src/libbacktrace/config.guess
deleted file mode 100755 (executable)
index b79252d..0000000
+++ /dev/null
@@ -1,1558 +0,0 @@
-#! /bin/sh
-# Attempt to guess a canonical system name.
-#   Copyright 1992-2013 Free Software Foundation, Inc.
-
-timestamp='2013-06-10'
-
-# This file is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, see <http://www.gnu.org/licenses/>.
-#
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that
-# program.  This Exception is an additional permission under section 7
-# of the GNU General Public License, version 3 ("GPLv3").
-#
-# Originally written by Per Bothner.
-#
-# You can get the latest version of this script from:
-# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
-#
-# Please send patches with a ChangeLog entry to config-patches@gnu.org.
-
-
-me=`echo "$0" | sed -e 's,.*/,,'`
-
-usage="\
-Usage: $0 [OPTION]
-
-Output the configuration name of the system \`$me' is run on.
-
-Operation modes:
-  -h, --help         print this help, then exit
-  -t, --time-stamp   print date of last modification, then exit
-  -v, --version      print version number, then exit
-
-Report bugs and patches to <config-patches@gnu.org>."
-
-version="\
-GNU config.guess ($timestamp)
-
-Originally written by Per Bothner.
-Copyright 1992-2013 Free Software Foundation, Inc.
-
-This is free software; see the source for copying conditions.  There is NO
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
-
-help="
-Try \`$me --help' for more information."
-
-# Parse command line
-while test $# -gt 0 ; do
-  case $1 in
-    --time-stamp | --time* | -t )
-       echo "$timestamp" ; exit ;;
-    --version | -v )
-       echo "$version" ; exit ;;
-    --help | --h* | -h )
-       echo "$usage"; exit ;;
-    -- )     # Stop option processing
-       shift; break ;;
-    - )        # Use stdin as input.
-       break ;;
-    -* )
-       echo "$me: invalid option $1$help" >&2
-       exit 1 ;;
-    * )
-       break ;;
-  esac
-done
-
-if test $# != 0; then
-  echo "$me: too many arguments$help" >&2
-  exit 1
-fi
-
-trap 'exit 1' 1 2 15
-
-# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
-# compiler to aid in system detection is discouraged as it requires
-# temporary files to be created and, as you can see below, it is a
-# headache to deal with in a portable fashion.
-
-# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
-# use `HOST_CC' if defined, but it is deprecated.
-
-# Portable tmp directory creation inspired by the Autoconf team.
-
-set_cc_for_build='
-trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
-trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
-: ${TMPDIR=/tmp} ;
- { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
- { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
- { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
- { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
-dummy=$tmp/dummy ;
-tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
-case $CC_FOR_BUILD,$HOST_CC,$CC in
- ,,)    echo "int x;" > $dummy.c ;
-       for c in cc gcc c89 c99 ; do
-         if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
-            CC_FOR_BUILD="$c"; break ;
-         fi ;
-       done ;
-       if test x"$CC_FOR_BUILD" = x ; then
-         CC_FOR_BUILD=no_compiler_found ;
-       fi
-       ;;
- ,,*)   CC_FOR_BUILD=$CC ;;
- ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
-esac ; set_cc_for_build= ;'
-
-# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
-# (ghazi@noc.rutgers.edu 1994-08-24)
-if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
-       PATH=$PATH:/.attbin ; export PATH
-fi
-
-UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
-UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
-UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
-UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
-
-case "${UNAME_SYSTEM}" in
-Linux|GNU|GNU/*)
-       # If the system lacks a compiler, then just pick glibc.
-       # We could probably try harder.
-       LIBC=gnu
-
-       eval $set_cc_for_build
-       cat <<-EOF > $dummy.c
-       #include <features.h>
-       #if defined(__UCLIBC__)
-       LIBC=uclibc
-       #elif defined(__dietlibc__)
-       LIBC=dietlibc
-       #else
-       LIBC=gnu
-       #endif
-       EOF
-       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
-       ;;
-esac
-
-# Note: order is significant - the case branches are not exclusive.
-
-case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
-    *:NetBSD:*:*)
-       # NetBSD (nbsd) targets should (where applicable) match one or
-       # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
-       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
-       # switched to ELF, *-*-netbsd* would select the old
-       # object file format.  This provides both forward
-       # compatibility and a consistent mechanism for selecting the
-       # object file format.
-       #
-       # Note: NetBSD doesn't particularly care about the vendor
-       # portion of the name.  We always set it to "unknown".
-       sysctl="sysctl -n hw.machine_arch"
-       UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
-           /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
-       case "${UNAME_MACHINE_ARCH}" in
-           armeb) machine=armeb-unknown ;;
-           arm*) machine=arm-unknown ;;
-           sh3el) machine=shl-unknown ;;
-           sh3eb) machine=sh-unknown ;;
-           sh5el) machine=sh5le-unknown ;;
-           *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
-       esac
-       # The Operating System including object format, if it has switched
-       # to ELF recently, or will in the future.
-       case "${UNAME_MACHINE_ARCH}" in
-           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
-               eval $set_cc_for_build
-               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
-                       | grep -q __ELF__
-               then
-                   # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
-                   # Return netbsd for either.  FIX?
-                   os=netbsd
-               else
-                   os=netbsdelf
-               fi
-               ;;
-           *)
-               os=netbsd
-               ;;
-       esac
-       # The OS release
-       # Debian GNU/NetBSD machines have a different userland, and
-       # thus, need a distinct triplet. However, they do not need
-       # kernel version information, so it can be replaced with a
-       # suitable tag, in the style of linux-gnu.
-       case "${UNAME_VERSION}" in
-           Debian*)
-               release='-gnu'
-               ;;
-           *)
-               release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
-               ;;
-       esac
-       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
-       # contains redundant information, the shorter form:
-       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
-       echo "${machine}-${os}${release}"
-       exit ;;
-    *:Bitrig:*:*)
-       UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
-       echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
-       exit ;;
-    *:OpenBSD:*:*)
-       UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
-       echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
-       exit ;;
-    *:ekkoBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
-       exit ;;
-    *:SolidBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
-       exit ;;
-    macppc:MirBSD:*:*)
-       echo powerpc-unknown-mirbsd${UNAME_RELEASE}
-       exit ;;
-    *:MirBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
-       exit ;;
-    alpha:OSF1:*:*)
-       case $UNAME_RELEASE in
-       *4.0)
-               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
-               ;;
-       *5.*)
-               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
-               ;;
-       esac
-       # According to Compaq, /usr/sbin/psrinfo has been available on
-       # OSF/1 and Tru64 systems produced since 1995.  I hope that
-       # covers most systems running today.  This code pipes the CPU
-       # types through head -n 1, so we only detect the type of CPU 0.
-       ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
-       case "$ALPHA_CPU_TYPE" in
-           "EV4 (21064)")
-               UNAME_MACHINE="alpha" ;;
-           "EV4.5 (21064)")
-               UNAME_MACHINE="alpha" ;;
-           "LCA4 (21066/21068)")
-               UNAME_MACHINE="alpha" ;;
-           "EV5 (21164)")
-               UNAME_MACHINE="alphaev5" ;;
-           "EV5.6 (21164A)")
-               UNAME_MACHINE="alphaev56" ;;
-           "EV5.6 (21164PC)")
-               UNAME_MACHINE="alphapca56" ;;
-           "EV5.7 (21164PC)")
-               UNAME_MACHINE="alphapca57" ;;
-           "EV6 (21264)")
-               UNAME_MACHINE="alphaev6" ;;
-           "EV6.7 (21264A)")
-               UNAME_MACHINE="alphaev67" ;;
-           "EV6.8CB (21264C)")
-               UNAME_MACHINE="alphaev68" ;;
-           "EV6.8AL (21264B)")
-               UNAME_MACHINE="alphaev68" ;;
-           "EV6.8CX (21264D)")
-               UNAME_MACHINE="alphaev68" ;;
-           "EV6.9A (21264/EV69A)")
-               UNAME_MACHINE="alphaev69" ;;
-           "EV7 (21364)")
-               UNAME_MACHINE="alphaev7" ;;
-           "EV7.9 (21364A)")
-               UNAME_MACHINE="alphaev79" ;;
-       esac
-       # A Pn.n version is a patched version.
-       # A Vn.n version is a released version.
-       # A Tn.n version is a released field test version.
-       # A Xn.n version is an unreleased experimental baselevel.
-       # 1.2 uses "1.2" for uname -r.
-       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-       # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
-       exitcode=$?
-       trap '' 0
-       exit $exitcode ;;
-    Alpha\ *:Windows_NT*:*)
-       # How do we know it's Interix rather than the generic POSIX subsystem?
-       # Should we change UNAME_MACHINE based on the output of uname instead
-       # of the specific Alpha model?
-       echo alpha-pc-interix
-       exit ;;
-    21064:Windows_NT:50:3)
-       echo alpha-dec-winnt3.5
-       exit ;;
-    Amiga*:UNIX_System_V:4.0:*)
-       echo m68k-unknown-sysv4
-       exit ;;
-    *:[Aa]miga[Oo][Ss]:*:*)
-       echo ${UNAME_MACHINE}-unknown-amigaos
-       exit ;;
-    *:[Mm]orph[Oo][Ss]:*:*)
-       echo ${UNAME_MACHINE}-unknown-morphos
-       exit ;;
-    *:OS/390:*:*)
-       echo i370-ibm-openedition
-       exit ;;
-    *:z/VM:*:*)
-       echo s390-ibm-zvmoe
-       exit ;;
-    *:OS400:*:*)
-       echo powerpc-ibm-os400
-       exit ;;
-    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
-       echo arm-acorn-riscix${UNAME_RELEASE}
-       exit ;;
-    arm*:riscos:*:*|arm*:RISCOS:*:*)
-       echo arm-unknown-riscos
-       exit ;;
-    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
-       echo hppa1.1-hitachi-hiuxmpp
-       exit ;;
-    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
-       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
-       if test "`(/bin/universe) 2>/dev/null`" = att ; then
-               echo pyramid-pyramid-sysv3
-       else
-               echo pyramid-pyramid-bsd
-       fi
-       exit ;;
-    NILE*:*:*:dcosx)
-       echo pyramid-pyramid-svr4
-       exit ;;
-    DRS?6000:unix:4.0:6*)
-       echo sparc-icl-nx6
-       exit ;;
-    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
-       case `/usr/bin/uname -p` in
-           sparc) echo sparc-icl-nx7; exit ;;
-       esac ;;
-    s390x:SunOS:*:*)
-       echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    sun4H:SunOS:5.*:*)
-       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
-       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
-       echo i386-pc-auroraux${UNAME_RELEASE}
-       exit ;;
-    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
-       eval $set_cc_for_build
-       SUN_ARCH="i386"
-       # If there is a compiler, see if it is configured for 64-bit objects.
-       # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
-       # This test works for both compilers.
-       if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
-           if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
-               (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
-               grep IS_64BIT_ARCH >/dev/null
-           then
-               SUN_ARCH="x86_64"
-           fi
-       fi
-       echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    sun4*:SunOS:6*:*)
-       # According to config.sub, this is the proper way to canonicalize
-       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
-       # it's likely to be more like Solaris than SunOS4.
-       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    sun4*:SunOS:*:*)
-       case "`/usr/bin/arch -k`" in
-           Series*|S4*)
-               UNAME_RELEASE=`uname -v`
-               ;;
-       esac
-       # Japanese Language versions have a version number like `4.1.3-JL'.
-       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
-       exit ;;
-    sun3*:SunOS:*:*)
-       echo m68k-sun-sunos${UNAME_RELEASE}
-       exit ;;
-    sun*:*:4.2BSD:*)
-       UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
-       test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
-       case "`/bin/arch`" in
-           sun3)
-               echo m68k-sun-sunos${UNAME_RELEASE}
-               ;;
-           sun4)
-               echo sparc-sun-sunos${UNAME_RELEASE}
-               ;;
-       esac
-       exit ;;
-    aushp:SunOS:*:*)
-       echo sparc-auspex-sunos${UNAME_RELEASE}
-       exit ;;
-    # The situation for MiNT is a little confusing.  The machine name
-    # can be virtually everything (everything which is not
-    # "atarist" or "atariste" at least should have a processor
-    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
-    # to the lowercase version "mint" (or "freemint").  Finally
-    # the system name "TOS" denotes a system which is actually not
-    # MiNT.  But MiNT is downward compatible to TOS, so this should
-    # be no problem.
-    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
-       echo m68k-atari-mint${UNAME_RELEASE}
-       exit ;;
-    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
-       echo m68k-atari-mint${UNAME_RELEASE}
-       exit ;;
-    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
-       echo m68k-atari-mint${UNAME_RELEASE}
-       exit ;;
-    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
-       echo m68k-milan-mint${UNAME_RELEASE}
-       exit ;;
-    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
-       echo m68k-hades-mint${UNAME_RELEASE}
-       exit ;;
-    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
-       echo m68k-unknown-mint${UNAME_RELEASE}
-       exit ;;
-    m68k:machten:*:*)
-       echo m68k-apple-machten${UNAME_RELEASE}
-       exit ;;
-    powerpc:machten:*:*)
-       echo powerpc-apple-machten${UNAME_RELEASE}
-       exit ;;
-    RISC*:Mach:*:*)
-       echo mips-dec-mach_bsd4.3
-       exit ;;
-    RISC*:ULTRIX:*:*)
-       echo mips-dec-ultrix${UNAME_RELEASE}
-       exit ;;
-    VAX*:ULTRIX*:*:*)
-       echo vax-dec-ultrix${UNAME_RELEASE}
-       exit ;;
-    2020:CLIX:*:* | 2430:CLIX:*:*)
-       echo clipper-intergraph-clix${UNAME_RELEASE}
-       exit ;;
-    mips:*:*:UMIPS | mips:*:*:RISCos)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
-#ifdef __cplusplus
-#include <stdio.h>  /* for printf() prototype */
-       int main (int argc, char *argv[]) {
-#else
-       int main (argc, argv) int argc; char *argv[]; {
-#endif
-       #if defined (host_mips) && defined (MIPSEB)
-       #if defined (SYSTYPE_SYSV)
-         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
-       #endif
-       #if defined (SYSTYPE_SVR4)
-         printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
-       #endif
-       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
-         printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
-       #endif
-       #endif
-         exit (-1);
-       }
-EOF
-       $CC_FOR_BUILD -o $dummy $dummy.c &&
-         dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
-         SYSTEM_NAME=`$dummy $dummyarg` &&
-           { echo "$SYSTEM_NAME"; exit; }
-       echo mips-mips-riscos${UNAME_RELEASE}
-       exit ;;
-    Motorola:PowerMAX_OS:*:*)
-       echo powerpc-motorola-powermax
-       exit ;;
-    Motorola:*:4.3:PL8-*)
-       echo powerpc-harris-powermax
-       exit ;;
-    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
-       echo powerpc-harris-powermax
-       exit ;;
-    Night_Hawk:Power_UNIX:*:*)
-       echo powerpc-harris-powerunix
-       exit ;;
-    m88k:CX/UX:7*:*)
-       echo m88k-harris-cxux7
-       exit ;;
-    m88k:*:4*:R4*)
-       echo m88k-motorola-sysv4
-       exit ;;
-    m88k:*:3*:R3*)
-       echo m88k-motorola-sysv3
-       exit ;;
-    AViiON:dgux:*:*)
-       # DG/UX returns AViiON for all architectures
-       UNAME_PROCESSOR=`/usr/bin/uname -p`
-       if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
-       then
-           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
-              [ ${TARGET_BINARY_INTERFACE}x = x ]
-           then
-               echo m88k-dg-dgux${UNAME_RELEASE}
-           else
-               echo m88k-dg-dguxbcs${UNAME_RELEASE}
-           fi
-       else
-           echo i586-dg-dgux${UNAME_RELEASE}
-       fi
-       exit ;;
-    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
-       echo m88k-dolphin-sysv3
-       exit ;;
-    M88*:*:R3*:*)
-       # Delta 88k system running SVR3
-       echo m88k-motorola-sysv3
-       exit ;;
-    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
-       echo m88k-tektronix-sysv3
-       exit ;;
-    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
-       echo m68k-tektronix-bsd
-       exit ;;
-    *:IRIX*:*:*)
-       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
-       exit ;;
-    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
-       echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
-       exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
-    i*86:AIX:*:*)
-       echo i386-ibm-aix
-       exit ;;
-    ia64:AIX:*:*)
-       if [ -x /usr/bin/oslevel ] ; then
-               IBM_REV=`/usr/bin/oslevel`
-       else
-               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
-       fi
-       echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
-       exit ;;
-    *:AIX:2:3)
-       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
-               eval $set_cc_for_build
-               sed 's/^                //' << EOF >$dummy.c
-               #include <sys/systemcfg.h>
-
-               main()
-                       {
-                       if (!__power_pc())
-                               exit(1);
-                       puts("powerpc-ibm-aix3.2.5");
-                       exit(0);
-                       }
-EOF
-               if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
-               then
-                       echo "$SYSTEM_NAME"
-               else
-                       echo rs6000-ibm-aix3.2.5
-               fi
-       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
-               echo rs6000-ibm-aix3.2.4
-       else
-               echo rs6000-ibm-aix3.2
-       fi
-       exit ;;
-    *:AIX:*:[4567])
-       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
-       if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
-               IBM_ARCH=rs6000
-       else
-               IBM_ARCH=powerpc
-       fi
-       if [ -x /usr/bin/oslevel ] ; then
-               IBM_REV=`/usr/bin/oslevel`
-       else
-               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
-       fi
-       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
-       exit ;;
-    *:AIX:*:*)
-       echo rs6000-ibm-aix
-       exit ;;
-    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
-       echo romp-ibm-bsd4.4
-       exit ;;
-    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
-       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
-       exit ;;                             # report: romp-ibm BSD 4.3
-    *:BOSX:*:*)
-       echo rs6000-bull-bosx
-       exit ;;
-    DPX/2?00:B.O.S.:*:*)
-       echo m68k-bull-sysv3
-       exit ;;
-    9000/[34]??:4.3bsd:1.*:*)
-       echo m68k-hp-bsd
-       exit ;;
-    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
-       echo m68k-hp-bsd4.4
-       exit ;;
-    9000/[34678]??:HP-UX:*:*)
-       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
-       case "${UNAME_MACHINE}" in
-           9000/31? )            HP_ARCH=m68000 ;;
-           9000/[34]?? )         HP_ARCH=m68k ;;
-           9000/[678][0-9][0-9])
-               if [ -x /usr/bin/getconf ]; then
-                   sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
-                   sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
-                   case "${sc_cpu_version}" in
-                     523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
-                     528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
-                     532)                      # CPU_PA_RISC2_0
-                       case "${sc_kernel_bits}" in
-                         32) HP_ARCH="hppa2.0n" ;;
-                         64) HP_ARCH="hppa2.0w" ;;
-                         '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
-                       esac ;;
-                   esac
-               fi
-               if [ "${HP_ARCH}" = "" ]; then
-                   eval $set_cc_for_build
-                   sed 's/^            //' << EOF >$dummy.c
-
-               #define _HPUX_SOURCE
-               #include <stdlib.h>
-               #include <unistd.h>
-
-               int main ()
-               {
-               #if defined(_SC_KERNEL_BITS)
-                   long bits = sysconf(_SC_KERNEL_BITS);
-               #endif
-                   long cpu  = sysconf (_SC_CPU_VERSION);
-
-                   switch (cpu)
-                       {
-                       case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
-                       case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
-                       case CPU_PA_RISC2_0:
-               #if defined(_SC_KERNEL_BITS)
-                           switch (bits)
-                               {
-                               case 64: puts ("hppa2.0w"); break;
-                               case 32: puts ("hppa2.0n"); break;
-                               default: puts ("hppa2.0"); break;
-                               } break;
-               #else  /* !defined(_SC_KERNEL_BITS) */
-                           puts ("hppa2.0"); break;
-               #endif
-                       default: puts ("hppa1.0"); break;
-                       }
-                   exit (0);
-               }
-EOF
-                   (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
-                   test -z "$HP_ARCH" && HP_ARCH=hppa
-               fi ;;
-       esac
-       if [ ${HP_ARCH} = "hppa2.0w" ]
-       then
-           eval $set_cc_for_build
-
-           # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
-           # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
-           # generating 64-bit code.  GNU and HP use different nomenclature:
-           #
-           # $ CC_FOR_BUILD=cc ./config.guess
-           # => hppa2.0w-hp-hpux11.23
-           # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
-           # => hppa64-hp-hpux11.23
-
-           if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
-               grep -q __LP64__
-           then
-               HP_ARCH="hppa2.0w"
-           else
-               HP_ARCH="hppa64"
-           fi
-       fi
-       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
-       exit ;;
-    ia64:HP-UX:*:*)
-       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
-       echo ia64-hp-hpux${HPUX_REV}
-       exit ;;
-    3050*:HI-UX:*:*)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
-       #include <unistd.h>
-       int
-       main ()
-       {
-         long cpu = sysconf (_SC_CPU_VERSION);
-         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
-            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
-            results, however.  */
-         if (CPU_IS_PA_RISC (cpu))
-           {
-             switch (cpu)
-               {
-                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
-                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
-                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
-                 default: puts ("hppa-hitachi-hiuxwe2"); break;
-               }
-           }
-         else if (CPU_IS_HP_MC68K (cpu))
-           puts ("m68k-hitachi-hiuxwe2");
-         else puts ("unknown-hitachi-hiuxwe2");
-         exit (0);
-       }
-EOF
-       $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
-               { echo "$SYSTEM_NAME"; exit; }
-       echo unknown-hitachi-hiuxwe2
-       exit ;;
-    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
-       echo hppa1.1-hp-bsd
-       exit ;;
-    9000/8??:4.3bsd:*:*)
-       echo hppa1.0-hp-bsd
-       exit ;;
-    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
-       echo hppa1.0-hp-mpeix
-       exit ;;
-    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
-       echo hppa1.1-hp-osf
-       exit ;;
-    hp8??:OSF1:*:*)
-       echo hppa1.0-hp-osf
-       exit ;;
-    i*86:OSF1:*:*)
-       if [ -x /usr/sbin/sysversion ] ; then
-           echo ${UNAME_MACHINE}-unknown-osf1mk
-       else
-           echo ${UNAME_MACHINE}-unknown-osf1
-       fi
-       exit ;;
-    parisc*:Lites*:*:*)
-       echo hppa1.1-hp-lites
-       exit ;;
-    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
-       echo c1-convex-bsd
-       exit ;;
-    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
-       if getsysinfo -f scalar_acc
-       then echo c32-convex-bsd
-       else echo c2-convex-bsd
-       fi
-       exit ;;
-    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
-       echo c34-convex-bsd
-       exit ;;
-    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
-       echo c38-convex-bsd
-       exit ;;
-    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
-       echo c4-convex-bsd
-       exit ;;
-    CRAY*Y-MP:*:*:*)
-       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    CRAY*[A-Z]90:*:*:*)
-       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
-       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
-             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
-             -e 's/\.[^.]*$/.X/'
-       exit ;;
-    CRAY*TS:*:*:*)
-       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    CRAY*T3E:*:*:*)
-       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    CRAY*SV1:*:*:*)
-       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    *:UNICOS/mp:*:*)
-       echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
-       FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-       FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
-       FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
-       echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
-       exit ;;
-    5000:UNIX_System_V:4.*:*)
-       FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
-       FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
-       echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
-       exit ;;
-    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
-       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
-       exit ;;
-    sparc*:BSD/OS:*:*)
-       echo sparc-unknown-bsdi${UNAME_RELEASE}
-       exit ;;
-    *:BSD/OS:*:*)
-       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
-       exit ;;
-    *:FreeBSD:*:*)
-       UNAME_PROCESSOR=`/usr/bin/uname -p`
-       case ${UNAME_PROCESSOR} in
-           amd64)
-               echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
-           *)
-               echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
-       esac
-       exit ;;
-    i*:CYGWIN*:*)
-       echo ${UNAME_MACHINE}-pc-cygwin
-       exit ;;
-    *:MINGW64*:*)
-       echo ${UNAME_MACHINE}-pc-mingw64
-       exit ;;
-    *:MINGW*:*)
-       echo ${UNAME_MACHINE}-pc-mingw32
-       exit ;;
-    i*:MSYS*:*)
-       echo ${UNAME_MACHINE}-pc-msys
-       exit ;;
-    i*:windows32*:*)
-       # uname -m includes "-pc" on this system.
-       echo ${UNAME_MACHINE}-mingw32
-       exit ;;
-    i*:PW*:*)
-       echo ${UNAME_MACHINE}-pc-pw32
-       exit ;;
-    *:Interix*:*)
-       case ${UNAME_MACHINE} in
-           x86)
-               echo i586-pc-interix${UNAME_RELEASE}
-               exit ;;
-           authenticamd | genuineintel | EM64T)
-               echo x86_64-unknown-interix${UNAME_RELEASE}
-               exit ;;
-           IA64)
-               echo ia64-unknown-interix${UNAME_RELEASE}
-               exit ;;
-       esac ;;
-    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
-       echo i${UNAME_MACHINE}-pc-mks
-       exit ;;
-    8664:Windows_NT:*)
-       echo x86_64-pc-mks
-       exit ;;
-    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
-       # How do we know it's Interix rather than the generic POSIX subsystem?
-       # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
-       # UNAME_MACHINE based on the output of uname instead of i386?
-       echo i586-pc-interix
-       exit ;;
-    i*:UWIN*:*)
-       echo ${UNAME_MACHINE}-pc-uwin
-       exit ;;
-    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
-       echo x86_64-unknown-cygwin
-       exit ;;
-    p*:CYGWIN*:*)
-       echo powerpcle-unknown-cygwin
-       exit ;;
-    prep*:SunOS:5.*:*)
-       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    *:GNU:*:*)
-       # the GNU system
-       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
-       exit ;;
-    *:GNU/*:*:*)
-       # other systems with GNU libc and userland
-       echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
-       exit ;;
-    i*86:Minix:*:*)
-       echo ${UNAME_MACHINE}-pc-minix
-       exit ;;
-    aarch64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    aarch64_be:Linux:*:*)
-       UNAME_MACHINE=aarch64_be
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    alpha:Linux:*:*)
-       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
-         EV5)   UNAME_MACHINE=alphaev5 ;;
-         EV56)  UNAME_MACHINE=alphaev56 ;;
-         PCA56) UNAME_MACHINE=alphapca56 ;;
-         PCA57) UNAME_MACHINE=alphapca56 ;;
-         EV6)   UNAME_MACHINE=alphaev6 ;;
-         EV67)  UNAME_MACHINE=alphaev67 ;;
-         EV68*) UNAME_MACHINE=alphaev68 ;;
-       esac
-       objdump --private-headers /bin/sh | grep -q ld.so.1
-       if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    arc:Linux:*:* | arceb:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    arm*:Linux:*:*)
-       eval $set_cc_for_build
-       if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
-           | grep -q __ARM_EABI__
-       then
-           echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       else
-           if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
-               | grep -q __ARM_PCS_VFP
-           then
-               echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
-           else
-               echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
-           fi
-       fi
-       exit ;;
-    avr32*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    cris:Linux:*:*)
-       echo ${UNAME_MACHINE}-axis-linux-${LIBC}
-       exit ;;
-    crisv32:Linux:*:*)
-       echo ${UNAME_MACHINE}-axis-linux-${LIBC}
-       exit ;;
-    frv:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    hexagon:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    i*86:Linux:*:*)
-       echo ${UNAME_MACHINE}-pc-linux-${LIBC}
-       exit ;;
-    ia64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    m32r*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    m68*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    mips:Linux:*:* | mips64:Linux:*:*)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
-       #undef CPU
-       #undef ${UNAME_MACHINE}
-       #undef ${UNAME_MACHINE}el
-       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
-       CPU=${UNAME_MACHINE}el
-       #else
-       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
-       CPU=${UNAME_MACHINE}
-       #else
-       CPU=
-       #endif
-       #endif
-EOF
-       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
-       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
-       ;;
-    or1k:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    or32:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    padre:Linux:*:*)
-       echo sparc-unknown-linux-${LIBC}
-       exit ;;
-    parisc64:Linux:*:* | hppa64:Linux:*:*)
-       echo hppa64-unknown-linux-${LIBC}
-       exit ;;
-    parisc:Linux:*:* | hppa:Linux:*:*)
-       # Look for CPU level
-       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
-         PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
-         PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
-         *)    echo hppa-unknown-linux-${LIBC} ;;
-       esac
-       exit ;;
-    ppc64:Linux:*:*)
-       echo powerpc64-unknown-linux-${LIBC}
-       exit ;;
-    ppc:Linux:*:*)
-       echo powerpc-unknown-linux-${LIBC}
-       exit ;;
-    ppc64le:Linux:*:*)
-       echo powerpc64le-unknown-linux-${LIBC}
-       exit ;;
-    ppcle:Linux:*:*)
-       echo powerpcle-unknown-linux-${LIBC}
-       exit ;;
-    s390:Linux:*:* | s390x:Linux:*:*)
-       echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
-       exit ;;
-    sh64*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    sh*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    sparc:Linux:*:* | sparc64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    tile*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    vax:Linux:*:*)
-       echo ${UNAME_MACHINE}-dec-linux-${LIBC}
-       exit ;;
-    x86_64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    xtensa*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
-       exit ;;
-    i*86:DYNIX/ptx:4*:*)
-       # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
-       # earlier versions are messed up and put the nodename in both
-       # sysname and nodename.
-       echo i386-sequent-sysv4
-       exit ;;
-    i*86:UNIX_SV:4.2MP:2.*)
-       # Unixware is an offshoot of SVR4, but it has its own version
-       # number series starting with 2...
-       # I am not positive that other SVR4 systems won't match this,
-       # I just have to hope.  -- rms.
-       # Use sysv4.2uw... so that sysv4* matches it.
-       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
-       exit ;;
-    i*86:OS/2:*:*)
-       # If we were able to find `uname', then EMX Unix compatibility
-       # is probably installed.
-       echo ${UNAME_MACHINE}-pc-os2-emx
-       exit ;;
-    i*86:XTS-300:*:STOP)
-       echo ${UNAME_MACHINE}-unknown-stop
-       exit ;;
-    i*86:atheos:*:*)
-       echo ${UNAME_MACHINE}-unknown-atheos
-       exit ;;
-    i*86:syllable:*:*)
-       echo ${UNAME_MACHINE}-pc-syllable
-       exit ;;
-    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
-       echo i386-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    i*86:*DOS:*:*)
-       echo ${UNAME_MACHINE}-pc-msdosdjgpp
-       exit ;;
-    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
-       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
-       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
-               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
-       else
-               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
-       fi
-       exit ;;
-    i*86:*:5:[678]*)
-       # UnixWare 7.x, OpenUNIX and OpenServer 6.
-       case `/bin/uname -X | grep "^Machine"` in
-           *486*)           UNAME_MACHINE=i486 ;;
-           *Pentium)        UNAME_MACHINE=i586 ;;
-           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
-       esac
-       echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
-       exit ;;
-    i*86:*:3.2:*)
-       if test -f /usr/options/cb.name; then
-               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
-               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
-       elif /bin/uname -X 2>/dev/null >/dev/null ; then
-               UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
-               (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
-               (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
-                       && UNAME_MACHINE=i586
-               (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
-                       && UNAME_MACHINE=i686
-               (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
-                       && UNAME_MACHINE=i686
-               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
-       else
-               echo ${UNAME_MACHINE}-pc-sysv32
-       fi
-       exit ;;
-    pc:*:*:*)
-       # Left here for compatibility:
-       # uname -m prints for DJGPP always 'pc', but it prints nothing about
-       # the processor, so we play safe by assuming i586.
-       # Note: whatever this is, it MUST be the same as what config.sub
-       # prints for the "djgpp" host, or else GDB configury will decide that
-       # this is a cross-build.
-       echo i586-pc-msdosdjgpp
-       exit ;;
-    Intel:Mach:3*:*)
-       echo i386-pc-mach3
-       exit ;;
-    paragon:*:*:*)
-       echo i860-intel-osf1
-       exit ;;
-    i860:*:4.*:*) # i860-SVR4
-       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
-         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
-       else # Add other i860-SVR4 vendors below as they are discovered.
-         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
-       fi
-       exit ;;
-    mini*:CTIX:SYS*5:*)
-       # "miniframe"
-       echo m68010-convergent-sysv
-       exit ;;
-    mc68k:UNIX:SYSTEM5:3.51m)
-       echo m68k-convergent-sysv
-       exit ;;
-    M680?0:D-NIX:5.3:*)
-       echo m68k-diab-dnix
-       exit ;;
-    M68*:*:R3V[5678]*:*)
-       test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
-    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
-       OS_REL=''
-       test -r /etc/.relid \
-       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
-       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
-         && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
-       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
-         && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
-    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
-       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
-         && { echo i486-ncr-sysv4; exit; } ;;
-    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
-       OS_REL='.3'
-       test -r /etc/.relid \
-           && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
-       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
-           && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
-       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
-           && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
-       /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
-           && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
-    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
-       echo m68k-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    mc68030:UNIX_System_V:4.*:*)
-       echo m68k-atari-sysv4
-       exit ;;
-    TSUNAMI:LynxOS:2.*:*)
-       echo sparc-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    rs6000:LynxOS:2.*:*)
-       echo rs6000-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
-       echo powerpc-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    SM[BE]S:UNIX_SV:*:*)
-       echo mips-dde-sysv${UNAME_RELEASE}
-       exit ;;
-    RM*:ReliantUNIX-*:*:*)
-       echo mips-sni-sysv4
-       exit ;;
-    RM*:SINIX-*:*:*)
-       echo mips-sni-sysv4
-       exit ;;
-    *:SINIX-*:*:*)
-       if uname -p 2>/dev/null >/dev/null ; then
-               UNAME_MACHINE=`(uname -p) 2>/dev/null`
-               echo ${UNAME_MACHINE}-sni-sysv4
-       else
-               echo ns32k-sni-sysv
-       fi
-       exit ;;
-    PENTIUM:*:4.0*:*)  # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
-                       # says <Richard.M.Bartel@ccMail.Census.GOV>
-       echo i586-unisys-sysv4
-       exit ;;
-    *:UNIX_System_V:4*:FTX*)
-       # From Gerald Hewes <hewes@openmarket.com>.
-       # How about differentiating between stratus architectures? -djm
-       echo hppa1.1-stratus-sysv4
-       exit ;;
-    *:*:*:FTX*)
-       # From seanf@swdc.stratus.com.
-       echo i860-stratus-sysv4
-       exit ;;
-    i*86:VOS:*:*)
-       # From Paul.Green@stratus.com.
-       echo ${UNAME_MACHINE}-stratus-vos
-       exit ;;
-    *:VOS:*:*)
-       # From Paul.Green@stratus.com.
-       echo hppa1.1-stratus-vos
-       exit ;;
-    mc68*:A/UX:*:*)
-       echo m68k-apple-aux${UNAME_RELEASE}
-       exit ;;
-    news*:NEWS-OS:6*:*)
-       echo mips-sony-newsos6
-       exit ;;
-    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
-       if [ -d /usr/nec ]; then
-               echo mips-nec-sysv${UNAME_RELEASE}
-       else
-               echo mips-unknown-sysv${UNAME_RELEASE}
-       fi
-       exit ;;
-    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
-       echo powerpc-be-beos
-       exit ;;
-    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
-       echo powerpc-apple-beos
-       exit ;;
-    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
-       echo i586-pc-beos
-       exit ;;
-    BePC:Haiku:*:*)    # Haiku running on Intel PC compatible.
-       echo i586-pc-haiku
-       exit ;;
-    x86_64:Haiku:*:*)
-       echo x86_64-unknown-haiku
-       exit ;;
-    SX-4:SUPER-UX:*:*)
-       echo sx4-nec-superux${UNAME_RELEASE}
-       exit ;;
-    SX-5:SUPER-UX:*:*)
-       echo sx5-nec-superux${UNAME_RELEASE}
-       exit ;;
-    SX-6:SUPER-UX:*:*)
-       echo sx6-nec-superux${UNAME_RELEASE}
-       exit ;;
-    SX-7:SUPER-UX:*:*)
-       echo sx7-nec-superux${UNAME_RELEASE}
-       exit ;;
-    SX-8:SUPER-UX:*:*)
-       echo sx8-nec-superux${UNAME_RELEASE}
-       exit ;;
-    SX-8R:SUPER-UX:*:*)
-       echo sx8r-nec-superux${UNAME_RELEASE}
-       exit ;;
-    Power*:Rhapsody:*:*)
-       echo powerpc-apple-rhapsody${UNAME_RELEASE}
-       exit ;;
-    *:Rhapsody:*:*)
-       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
-       exit ;;
-    *:Darwin:*:*)
-       UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
-       eval $set_cc_for_build
-       if test "$UNAME_PROCESSOR" = unknown ; then
-           UNAME_PROCESSOR=powerpc
-       fi
-       if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
-           if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
-               (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
-               grep IS_64BIT_ARCH >/dev/null
-           then
-               case $UNAME_PROCESSOR in
-                   i386) UNAME_PROCESSOR=x86_64 ;;
-                   powerpc) UNAME_PROCESSOR=powerpc64 ;;
-               esac
-           fi
-       fi
-       echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
-       exit ;;
-    *:procnto*:*:* | *:QNX:[0123456789]*:*)
-       UNAME_PROCESSOR=`uname -p`
-       if test "$UNAME_PROCESSOR" = "x86"; then
-               UNAME_PROCESSOR=i386
-               UNAME_MACHINE=pc
-       fi
-       echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
-       exit ;;
-    *:QNX:*:4*)
-       echo i386-pc-qnx
-       exit ;;
-    NEO-?:NONSTOP_KERNEL:*:*)
-       echo neo-tandem-nsk${UNAME_RELEASE}
-       exit ;;
-    NSE-*:NONSTOP_KERNEL:*:*)
-       echo nse-tandem-nsk${UNAME_RELEASE}
-       exit ;;
-    NSR-?:NONSTOP_KERNEL:*:*)
-       echo nsr-tandem-nsk${UNAME_RELEASE}
-       exit ;;
-    *:NonStop-UX:*:*)
-       echo mips-compaq-nonstopux
-       exit ;;
-    BS2000:POSIX*:*:*)
-       echo bs2000-siemens-sysv
-       exit ;;
-    DS/*:UNIX_System_V:*:*)
-       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
-       exit ;;
-    *:Plan9:*:*)
-       # "uname -m" is not consistent, so use $cputype instead. 386
-       # is converted to i386 for consistency with other x86
-       # operating systems.
-       if test "$cputype" = "386"; then
-           UNAME_MACHINE=i386
-       else
-           UNAME_MACHINE="$cputype"
-       fi
-       echo ${UNAME_MACHINE}-unknown-plan9
-       exit ;;
-    *:TOPS-10:*:*)
-       echo pdp10-unknown-tops10
-       exit ;;
-    *:TENEX:*:*)
-       echo pdp10-unknown-tenex
-       exit ;;
-    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
-       echo pdp10-dec-tops20
-       exit ;;
-    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
-       echo pdp10-xkl-tops20
-       exit ;;
-    *:TOPS-20:*:*)
-       echo pdp10-unknown-tops20
-       exit ;;
-    *:ITS:*:*)
-       echo pdp10-unknown-its
-       exit ;;
-    SEI:*:*:SEIUX)
-       echo mips-sei-seiux${UNAME_RELEASE}
-       exit ;;
-    *:DragonFly:*:*)
-       echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
-       exit ;;
-    *:*VMS:*:*)
-       UNAME_MACHINE=`(uname -p) 2>/dev/null`
-       case "${UNAME_MACHINE}" in
-           A*) echo alpha-dec-vms ; exit ;;
-           I*) echo ia64-dec-vms ; exit ;;
-           V*) echo vax-dec-vms ; exit ;;
-       esac ;;
-    *:XENIX:*:SysV)
-       echo i386-pc-xenix
-       exit ;;
-    i*86:skyos:*:*)
-       echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
-       exit ;;
-    i*86:rdos:*:*)
-       echo ${UNAME_MACHINE}-pc-rdos
-       exit ;;
-    i*86:AROS:*:*)
-       echo ${UNAME_MACHINE}-pc-aros
-       exit ;;
-    x86_64:VMkernel:*:*)
-       echo ${UNAME_MACHINE}-unknown-esx
-       exit ;;
-esac
-
-eval $set_cc_for_build
-cat >$dummy.c <<EOF
-#ifdef _SEQUENT_
-# include <sys/types.h>
-# include <sys/utsname.h>
-#endif
-main ()
-{
-#if defined (sony)
-#if defined (MIPSEB)
-  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
-     I don't know....  */
-  printf ("mips-sony-bsd\n"); exit (0);
-#else
-#include <sys/param.h>
-  printf ("m68k-sony-newsos%s\n",
-#ifdef NEWSOS4
-       "4"
-#else
-       ""
-#endif
-       ); exit (0);
-#endif
-#endif
-
-#if defined (__arm) && defined (__acorn) && defined (__unix)
-  printf ("arm-acorn-riscix\n"); exit (0);
-#endif
-
-#if defined (hp300) && !defined (hpux)
-  printf ("m68k-hp-bsd\n"); exit (0);
-#endif
-
-#if defined (NeXT)
-#if !defined (__ARCHITECTURE__)
-#define __ARCHITECTURE__ "m68k"
-#endif
-  int version;
-  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
-  if (version < 4)
-    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
-  else
-    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
-  exit (0);
-#endif
-
-#if defined (MULTIMAX) || defined (n16)
-#if defined (UMAXV)
-  printf ("ns32k-encore-sysv\n"); exit (0);
-#else
-#if defined (CMU)
-  printf ("ns32k-encore-mach\n"); exit (0);
-#else
-  printf ("ns32k-encore-bsd\n"); exit (0);
-#endif
-#endif
-#endif
-
-#if defined (__386BSD__)
-  printf ("i386-pc-bsd\n"); exit (0);
-#endif
-
-#if defined (sequent)
-#if defined (i386)
-  printf ("i386-sequent-dynix\n"); exit (0);
-#endif
-#if defined (ns32000)
-  printf ("ns32k-sequent-dynix\n"); exit (0);
-#endif
-#endif
-
-#if defined (_SEQUENT_)
-    struct utsname un;
-
-    uname(&un);
-
-    if (strncmp(un.version, "V2", 2) == 0) {
-       printf ("i386-sequent-ptx2\n"); exit (0);
-    }
-    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
-       printf ("i386-sequent-ptx1\n"); exit (0);
-    }
-    printf ("i386-sequent-ptx\n"); exit (0);
-
-#endif
-
-#if defined (vax)
-# if !defined (ultrix)
-#  include <sys/param.h>
-#  if defined (BSD)
-#   if BSD == 43
-      printf ("vax-dec-bsd4.3\n"); exit (0);
-#   else
-#    if BSD == 199006
-      printf ("vax-dec-bsd4.3reno\n"); exit (0);
-#    else
-      printf ("vax-dec-bsd\n"); exit (0);
-#    endif
-#   endif
-#  else
-    printf ("vax-dec-bsd\n"); exit (0);
-#  endif
-# else
-    printf ("vax-dec-ultrix\n"); exit (0);
-# endif
-#endif
-
-#if defined (alliant) && defined (i860)
-  printf ("i860-alliant-bsd\n"); exit (0);
-#endif
-
-  exit (1);
-}
-EOF
-
-$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
-       { echo "$SYSTEM_NAME"; exit; }
-
-# Apollos put the system type in the environment.
-
-test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
-
-# Convex versions that predate uname can use getsysinfo(1)
-
-if [ -x /usr/convex/getsysinfo ]
-then
-    case `getsysinfo -f cpu_type` in
-    c1*)
-       echo c1-convex-bsd
-       exit ;;
-    c2*)
-       if getsysinfo -f scalar_acc
-       then echo c32-convex-bsd
-       else echo c2-convex-bsd
-       fi
-       exit ;;
-    c34*)
-       echo c34-convex-bsd
-       exit ;;
-    c38*)
-       echo c38-convex-bsd
-       exit ;;
-    c4*)
-       echo c4-convex-bsd
-       exit ;;
-    esac
-fi
-
-cat >&2 <<EOF
-$0: unable to guess system type
-
-This script, last modified $timestamp, has failed to recognize
-the operating system you are using. It is advised that you
-download the most up to date version of the config scripts from
-
-  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
-and
-  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
-
-If the version you run ($0) is already up to date, please
-send the following data and any information you think might be
-pertinent to <config-patches@gnu.org> in order to provide the needed
-information to handle your system.
-
-config.guess timestamp = $timestamp
-
-uname -m = `(uname -m) 2>/dev/null || echo unknown`
-uname -r = `(uname -r) 2>/dev/null || echo unknown`
-uname -s = `(uname -s) 2>/dev/null || echo unknown`
-uname -v = `(uname -v) 2>/dev/null || echo unknown`
-
-/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
-/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
-
-hostinfo               = `(hostinfo) 2>/dev/null`
-/bin/universe          = `(/bin/universe) 2>/dev/null`
-/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
-/bin/arch              = `(/bin/arch) 2>/dev/null`
-/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
-/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
-
-UNAME_MACHINE = ${UNAME_MACHINE}
-UNAME_RELEASE = ${UNAME_RELEASE}
-UNAME_SYSTEM  = ${UNAME_SYSTEM}
-UNAME_VERSION = ${UNAME_VERSION}
-EOF
-
-exit 1
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "timestamp='"
-# time-stamp-format: "%:y-%02m-%02d"
-# time-stamp-end: "'"
-# End:
old mode 100755 (executable)
new mode 100644 (file)
index 61cb4bc..da6d1b6
@@ -1,8 +1,8 @@
 #! /bin/sh
 # Configuration validation subroutine script.
-#   Copyright 1992-2013 Free Software Foundation, Inc.
+#   Copyright 1992-2016 Free Software Foundation, Inc.
 
-timestamp='2013-10-01'
+timestamp='2016-01-01'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@ timestamp='2013-10-01'
 # of the GNU General Public License, version 3 ("GPLv3").
 
 
-# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+# Please send patches to <config-patches@gnu.org>.
 #
 # Configuration subroutine to validate and canonicalize a configuration type.
 # Supply the specified configuration type as an argument.
@@ -33,7 +33,7 @@ timestamp='2013-10-01'
 # Otherwise, we print the canonical config type on stdout and succeed.
 
 # You can get the latest version of this script from:
-# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
 
 # This file is supposed to be the same for all GNU packages
 # and recognize all the CPU types, system types and aliases
@@ -53,8 +53,7 @@ timestamp='2013-10-01'
 me=`echo "$0" | sed -e 's,.*/,,'`
 
 usage="\
-Usage: $0 [OPTION] CPU-MFR-OPSYS
-       $0 [OPTION] ALIAS
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
 
 Canonicalize a configuration name.
 
@@ -68,7 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>."
 version="\
 GNU config.sub ($timestamp)
 
-Copyright 1992-2013 Free Software Foundation, Inc.
+Copyright 1992-2016 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -117,7 +116,7 @@ maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
 case $maybe_os in
   nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
   linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
-  knetbsd*-gnu* | netbsd*-gnu* | \
+  knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
   kopensolaris*-gnu* | \
   storm-chaos* | os2-emx* | rtmk-nova*)
     os=-$maybe_os
@@ -255,12 +254,13 @@ case $basic_machine in
        | arc | arceb \
        | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
        | avr | avr32 \
+       | ba \
        | be32 | be64 \
        | bfin \
        | c4x | c8051 | clipper \
        | d10v | d30v | dlx | dsp16xx \
-       | epiphany \
-       | fido | fr30 | frv \
+       | e2k | epiphany \
+       | fido | fr30 | frv | ft32 \
        | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
        | hexagon \
        | i370 | i860 | i960 | ia64 \
@@ -283,8 +283,10 @@ case $basic_machine in
        | mips64vr5900 | mips64vr5900el \
        | mipsisa32 | mipsisa32el \
        | mipsisa32r2 | mipsisa32r2el \
+       | mipsisa32r6 | mipsisa32r6el \
        | mipsisa64 | mipsisa64el \
        | mipsisa64r2 | mipsisa64r2el \
+       | mipsisa64r6 | mipsisa64r6el \
        | mipsisa64sb1 | mipsisa64sb1el \
        | mipsisa64sr71k | mipsisa64sr71kel \
        | mipsr5900 | mipsr5900el \
@@ -296,14 +298,14 @@ case $basic_machine in
        | nds32 | nds32le | nds32be \
        | nios | nios2 | nios2eb | nios2el \
        | ns16k | ns32k \
-       | open8 \
-       | or1k | or32 \
+       | open8 | or1k | or1knd | or32 \
        | pdp10 | pdp11 | pj | pjl \
        | powerpc | powerpc64 | powerpc64le | powerpcle \
        | pyramid \
+       | riscv32 | riscv64 \
        | rl78 | rx \
        | score \
-       | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+       | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
        | sh64 | sh64le \
        | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
        | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
@@ -311,6 +313,7 @@ case $basic_machine in
        | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
        | ubicom32 \
        | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+       | visium \
        | we32k \
        | x86 | xc16x | xstormy16 | xtensa \
        | z8k | z80)
@@ -325,6 +328,9 @@ case $basic_machine in
        c6x)
                basic_machine=tic6x-unknown
                ;;
+       leon|leon[3-9])
+               basic_machine=sparc-$basic_machine
+               ;;
        m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
                basic_machine=$basic_machine-unknown
                os=-none
@@ -370,12 +376,13 @@ case $basic_machine in
        | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
        | arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
        | avr-* | avr32-* \
+       | ba-* \
        | be32-* | be64-* \
        | bfin-* | bs2000-* \
        | c[123]* | c30-* | [cjt]90-* | c4x-* \
        | c8051-* | clipper-* | craynv-* | cydra-* \
        | d10v-* | d30v-* | dlx-* \
-       | elxsi-* \
+       | e2k-* | elxsi-* \
        | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
        | h8300-* | h8500-* \
        | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
@@ -402,8 +409,10 @@ case $basic_machine in
        | mips64vr5900-* | mips64vr5900el-* \
        | mipsisa32-* | mipsisa32el-* \
        | mipsisa32r2-* | mipsisa32r2el-* \
+       | mipsisa32r6-* | mipsisa32r6el-* \
        | mipsisa64-* | mipsisa64el-* \
        | mipsisa64r2-* | mipsisa64r2el-* \
+       | mipsisa64r6-* | mipsisa64r6el-* \
        | mipsisa64sb1-* | mipsisa64sb1el-* \
        | mipsisa64sr71k-* | mipsisa64sr71kel-* \
        | mipsr5900-* | mipsr5900el-* \
@@ -415,16 +424,18 @@ case $basic_machine in
        | nios-* | nios2-* | nios2eb-* | nios2el-* \
        | none-* | np1-* | ns16k-* | ns32k-* \
        | open8-* \
+       | or1k*-* \
        | orion-* \
        | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
        | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
        | pyramid-* \
+       | riscv32-* | riscv64-* \
        | rl78-* | romp-* | rs6000-* | rx-* \
        | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
        | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
        | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
        | sparclite-* \
-       | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+       | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
        | tahoe-* \
        | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
        | tile*-* \
@@ -432,6 +443,7 @@ case $basic_machine in
        | ubicom32-* \
        | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
        | vax-* \
+       | visium-* \
        | we32k-* \
        | x86-* | x86_64-* | xc16x-* | xps100-* \
        | xstormy16-* | xtensa*-* \
@@ -508,6 +520,9 @@ case $basic_machine in
                basic_machine=i386-pc
                os=-aros
                ;;
+       asmjs)
+               basic_machine=asmjs-unknown
+               ;;
        aux)
                basic_machine=m68k-apple
                os=-aux
@@ -769,6 +784,9 @@ case $basic_machine in
                basic_machine=m68k-isi
                os=-sysv
                ;;
+       leon-*|leon[3-9]-*)
+               basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
+               ;;
        m68knommu)
                basic_machine=m68k-unknown
                os=-linux
@@ -824,6 +842,10 @@ case $basic_machine in
                basic_machine=powerpc-unknown
                os=-morphos
                ;;
+       moxiebox)
+               basic_machine=moxie-unknown
+               os=-moxiebox
+               ;;
        msdos)
                basic_machine=i386-pc
                os=-msdos
@@ -1356,7 +1378,7 @@ case $os in
              | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
              | -sym* | -kopensolaris* | -plan9* \
              | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
-             | -aos* | -aros* \
+             | -aos* | -aros* | -cloudabi* | -sortix* \
              | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
              | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
              | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
@@ -1369,14 +1391,15 @@ case $os in
              | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
              | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
              | -linux-newlib* | -linux-musl* | -linux-uclibc* \
-             | -uxpv* | -beos* | -mpeix* | -udk* \
+             | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
              | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
              | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
              | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
              | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
              | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
              | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
-             | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+             | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
+             | -onefs* | -tirtos*)
        # Remember, each alternative MUST END IN *, to match a version number.
                ;;
        -qnx*)
@@ -1594,9 +1617,6 @@ case $basic_machine in
        mips*-*)
                os=-elf
                ;;
-       or1k-*)
-               os=-elf
-               ;;
        or32-*)
                os=-coff
                ;;
index d9e8075845f836b93e3d894d614ffa236ebcf38f..47df55c22d67f26749fe13cc70d432998f273431 100755 (executable)
@@ -1366,7 +1366,7 @@ Optional Features:
   --enable-multilib       build many library versions (default)
   --enable-maintainer-mode  enable make rules and dependencies not useful
                          (and sometimes confusing) to the casual installer
-  --enable-shared[=PKGS]  build shared libraries [default=no]
+  --enable-shared[=PKGS]  build shared libraries [default=yes]
   --enable-static[=PKGS]  build static libraries [default=yes]
   --enable-fast-install[=PKGS]
                           optimize for fast installation [default=yes]
@@ -3974,7 +3974,7 @@ am_lf='
 '
 case `pwd` in
   *[\\\"\#\$\&\'\`$am_lf]*)
-    as_fn_error "unsafe absolute working directory name: \``pwd`'" "$LINENO" 5;;
+    as_fn_error "unsafe absolute working directory name" "$LINENO" 5;;
 esac
 case $srcdir in
   *[\\\"\#\$\&\'\`$am_lf\ \    ]*)
@@ -7658,7 +7658,16 @@ done
 
 
 # Set options
-# Check whether --enable-shared was given.
+
+
+
+        enable_dlopen=no
+
+
+  enable_win32_dll=no
+
+
+            # Check whether --enable-shared was given.
 if test "${enable_shared+set}" = set; then :
   enableval=$enable_shared; p=${PACKAGE-default}
     case $enableval in
@@ -7678,7 +7687,7 @@ if test "${enable_shared+set}" = set; then :
       ;;
     esac
 else
-  enable_shared=no
+  enable_shared=yes
 fi
 
 
@@ -7689,14 +7698,6 @@ fi
 
 
 
-
-        enable_dlopen=no
-
-
-  enable_win32_dll=no
-
-
-
   # Check whether --enable-static was given.
 if test "${enable_static+set}" = set; then :
   enableval=$enable_static; p=${PACKAGE-default}
@@ -9102,7 +9103,7 @@ _LT_EOF
        if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
          export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
        else
-         export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+         export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
        fi
        aix_use_runtimelinking=no
 
@@ -9373,7 +9374,7 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
       ;;
 
     # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
-    freebsd* | dragonfly* | bitrig* | openbsd*)
+    freebsd* | dragonfly* | openbsd* | bitrig*)
       archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
       hardcode_libdir_flag_spec='-R$libdir'
       hardcode_direct=yes
@@ -11130,7 +11131,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11133 "configure"
+#line 11134 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11236,7 +11237,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11239 "configure"
+#line 11240 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11708,17 +11709,12 @@ $as_echo "#define HAVE_GETIPINFO 1" >>confdefs.h
   fi
 fi
 
-# When building as a target library, shared libraries may want to link
-# this in.  We don't want to provide another shared library to
-# complicate dependencies.  Instead, we just compile with -fPIC.
-PIC_FLAG=
-if test -n "${with_target_subdir}"; then
-  PIC_FLAG=-fPIC
-fi
-# Similarly, use -fPIC with --enable-host-shared:
+# Enable --enable-host-shared.
 # Check whether --enable-host-shared was given.
 if test "${enable_host_shared+set}" = set; then :
   enableval=$enable_host_shared; PIC_FLAG=-fPIC
+else
+  PIC_FLAG=
 fi
 
 
@@ -11730,7 +11726,10 @@ if test "${libbacktrace_cv_sys_sync+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   if test -n "${with_target_subdir}"; then
-   libbacktrace_cv_sys_sync=yes
+   case "${host}" in
+   hppa*-*-hpux*) libbacktrace_cv_sys_sync=no ;;
+   *) libbacktrace_cv_sys_sync=yes ;;
+   esac
  else
    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
@@ -12305,9 +12304,10 @@ else
     # simply assume that if we have mman.h, we have mmap.
     have_mmap=yes
     case "${host}" in
-    spu-*-*)
+    spu-*-*|*-*-msdosdjgpp)
         # The SPU does not have mmap, but it has a sys/mman.h header file
         # containing "mmap_eaddr" and the mmap flags, confusing the test.
+        # DJGPP also has sys/man.h, but no mmap
        have_mmap=no ;;
     esac
   else
@@ -12407,6 +12407,7 @@ fi
 if test -n "${with_target_subdir}"; then
    case "${host}" in
    *-*-mingw*) have_fcntl=no ;;
+   spu-*-*) have_fcntl=no ;;
    *) have_fcntl=yes ;;
    esac
 else
index 30d890ef14a9fb44c75e189941877f08366f1658..71e85187ef55a00b29e809c68357bdf331dc7135 100644 (file)
@@ -1,18 +1,18 @@
 # configure.ac -- Backtrace configure script.
-# Copyright (C) 2012-2015 Free Software Foundation, Inc.
+# Copyright (C) 2012-2016 Free Software Foundation, Inc.
 
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
 # met:
 
 #     (1) Redistributions of source code must retain the above copyright
-#     notice, this list of conditions and the following disclaimer.
+#     notice, this list of conditions and the following disclaimer. 
 
 #     (2) Redistributions in binary form must reproduce the above copyright
 #     notice, this list of conditions and the following disclaimer in
 #     the documentation and/or other materials provided with the
-#     distribution.
-
+#     distribution.  
+    
 #     (3) The name of the author may not be used to
 #     endorse or promote products derived from this software without
 #     specific prior written permission.
@@ -79,7 +79,7 @@ case "$AWK" in
 "") AC_MSG_ERROR([can't build without awk]) ;;
 esac
 
-LT_INIT([disable-shared])
+LT_INIT
 AM_PROG_LIBTOOL
 
 backtrace_supported=yes
@@ -161,25 +161,21 @@ else
   fi
 fi
 
-# When building as a target library, shared libraries may want to link
-# this in.  We don't want to provide another shared library to
-# complicate dependencies.  Instead, we just compile with -fPIC.
-PIC_FLAG=
-if test -n "${with_target_subdir}"; then
-  PIC_FLAG=-fPIC
-fi
-# Similarly, use -fPIC with --enable-host-shared:
+# Enable --enable-host-shared.
 AC_ARG_ENABLE(host-shared,
 [AS_HELP_STRING([--enable-host-shared],
                [build host code as shared libraries])],
-[PIC_FLAG=-fPIC], [])
+[PIC_FLAG=-fPIC], [PIC_FLAG=])
 AC_SUBST(PIC_FLAG)
 
 # Test for __sync support.
 AC_CACHE_CHECK([__sync extensions],
 [libbacktrace_cv_sys_sync],
 [if test -n "${with_target_subdir}"; then
-   libbacktrace_cv_sys_sync=yes
+   case "${host}" in
+   hppa*-*-hpux*) libbacktrace_cv_sys_sync=no ;;
+   *) libbacktrace_cv_sys_sync=yes ;;
+   esac
  else
    AC_LINK_IFELSE(
      [AC_LANG_PROGRAM([int i;],
@@ -274,9 +270,10 @@ else
     # simply assume that if we have mman.h, we have mmap.
     have_mmap=yes
     case "${host}" in
-    spu-*-*)
+    spu-*-*|*-*-msdosdjgpp)
         # The SPU does not have mmap, but it has a sys/mman.h header file
         # containing "mmap_eaddr" and the mmap flags, confusing the test.
+        # DJGPP also has sys/man.h, but no mmap
        have_mmap=no ;;
     esac
   else
@@ -332,6 +329,7 @@ fi
 if test -n "${with_target_subdir}"; then
    case "${host}" in
    *-*-mingw*) have_fcntl=no ;;
+   spu-*-*) have_fcntl=no ;;
    *) have_fcntl=yes ;;
    esac
 else
index fd3beac01fbafd8f8f9e60f82a3e436f6e4ba917..55b8d7dc2a56d96e69df0a1be6728bad212754c7 100644 (file)
@@ -1,5 +1,5 @@
 /* dwarf.c -- Get file/line information from DWARF for backtraces.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
@@ -1246,7 +1246,7 @@ add_unit_ranges (struct backtrace_state *state, uintptr_t base_address,
 
 static int
 find_address_ranges (struct backtrace_state *state, uintptr_t base_address,
-                    struct dwarf_buf *unit_buf,
+                    struct dwarf_buf *unit_buf, 
                     const unsigned char *dwarf_str, size_t dwarf_str_size,
                     const unsigned char *dwarf_ranges,
                     size_t dwarf_ranges_size,
@@ -1605,7 +1605,7 @@ read_line_header (struct backtrace_state *state, struct unit *u,
 
   if (!advance (line_buf, hdrlen))
     return 0;
-
+  
   hdr->min_insn_len = read_byte (&hdr_buf);
   if (hdr->version < 4)
     hdr->max_ops_per_insn = 1;
@@ -1614,7 +1614,7 @@ read_line_header (struct backtrace_state *state, struct unit *u,
 
   /* We don't care about default_is_stmt.  */
   read_byte (&hdr_buf);
-
+  
   hdr->line_base = read_sbyte (&hdr_buf);
   hdr->line_range = read_byte (&hdr_buf);
 
index 932ce86435131a1f54aafad359afd6168e45cd50..2dfee5666dea68d8292f04c5100c93e1f1fcf4f4 100644 (file)
@@ -1,9 +1,7 @@
 /* -*- c -*-
    Declarations and definitions of codes relating to the DWARF2 and
    DWARF3 symbolic debugging information formats.
-   Copyright (C) 1992, 1993, 1995, 1996, 1997, 1999, 2000, 2001, 2002,
-   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
-   Free Software Foundation, Inc.
+   Copyright (C) 1992-2015 Free Software Foundation, Inc.
 
    Written by Gary Funck (gary@intrepid.com) The Ada Joint Program
    Office (AJPO), Florida State University and Silicon Graphics Inc.
 
 /* This file declares various DWARF-related constants using a set of
    macros which can be redefined by the including file.
-
+   
    The macros are in sections.  Each section corresponds to a single
    set of DWARF constants and has a corresponding key.  The key is
    used in all the macro names.
-
+   
    The sections are TAG (for DW_TAG_ constants), FORM (DW_FORM_), AT
    (DW_AT_), OP (DW_OP_), ATE (DW_ATE_), and CFA (DW_CFA_).
-
+   
    Using TAG as an example, the following macros may be used for each
    key:
-
+   
    DW_FIRST_TAG(name, value) - Introduce the first DW_TAG constant.
-
+   
    DW_TAG(name, value) - Define a subsequent constant.
-
+   
    DW_TAG_DUP(name, value) - Define a subsequent constant whose value
    is a duplicate of some other constant.  Not all keys use the _DUP
    macro form.  If more than one name shares a value, then the base
    (DW_TAG) form will be the preferred name and DW_TAG_DUP will hold
    any alternate names.
-
+   
    DW_END_TAG - Invoked at the end of the DW_TAG constants.  */
 
 DW_FIRST_TAG (DW_TAG_padding, 0x00)
@@ -133,6 +131,8 @@ DW_TAG (DW_TAG_shared_type, 0x40)
 DW_TAG (DW_TAG_type_unit, 0x41)
 DW_TAG (DW_TAG_rvalue_reference_type, 0x42)
 DW_TAG (DW_TAG_template_alias, 0x43)
+/* DWARF 5.  */
+DW_TAG (DW_TAG_atomic_type, 0x47)
 
 DW_TAG_DUP (DW_TAG_lo_user, 0x4080)
 DW_TAG_DUP (DW_TAG_hi_user, 0xffff)
@@ -308,6 +308,8 @@ DW_AT (DW_AT_data_bit_offset, 0x6b)
 DW_AT (DW_AT_const_expr, 0x6c)
 DW_AT (DW_AT_enum_class, 0x6d)
 DW_AT (DW_AT_linkage_name, 0x6e)
+/* DWARF 5.  */
+DW_AT (DW_AT_noreturn, 0x87)
 
 DW_AT_DUP (DW_AT_lo_user, 0x2000) /* Implementation-defined range start.  */
 DW_AT_DUP (DW_AT_hi_user, 0x3fff) /* Implementation-defined range end.  */
@@ -383,6 +385,8 @@ DW_AT (DW_AT_GNU_all_call_sites, 0x2117)
 DW_AT (DW_AT_GNU_all_source_call_sites, 0x2118)
 /* Section offset into .debug_macro section.  */
 DW_AT (DW_AT_GNU_macros, 0x2119)
+/* Attribute for C++ deleted special member functions (= delete;).  */
+DW_AT (DW_AT_GNU_deleted, 0x211a)
 /* Extensions for Fission.  See http://gcc.gnu.org/wiki/DebugFission.  */
 DW_AT (DW_AT_GNU_dwo_name, 0x2130)
 DW_AT (DW_AT_GNU_dwo_id, 0x2131)
@@ -400,12 +404,33 @@ DW_AT (DW_AT_VMS_rtnbeg_pd_address, 0x2201)
    See http://gcc.gnu.org/wiki/DW_AT_GNAT_descriptive_type .  */
 DW_AT (DW_AT_use_GNAT_descriptive_type, 0x2301)
 DW_AT (DW_AT_GNAT_descriptive_type, 0x2302)
+/* Rational constant extension.
+   See https://gcc.gnu.org/wiki/DW_AT_GNU_numerator_denominator .  */
+DW_TAG (DW_AT_GNU_numerator, 0x2303)
+DW_TAG (DW_AT_GNU_denominator, 0x2304)
+/* Biased integer extension.
+   See https://gcc.gnu.org/wiki/DW_AT_GNU_bias .  */
+DW_TAG (DW_AT_GNU_bias, 0x2305)
 /* UPC extension.  */
 DW_AT (DW_AT_upc_threads_scaled, 0x3210)
 /* PGI (STMicroelectronics) extensions.  */
 DW_AT (DW_AT_PGI_lbase, 0x3a00)
 DW_AT (DW_AT_PGI_soffset, 0x3a01)
 DW_AT (DW_AT_PGI_lstride, 0x3a02)
+/* Apple extensions.  */
+DW_AT (DW_AT_APPLE_optimized, 0x3fe1)
+DW_AT (DW_AT_APPLE_flags, 0x3fe2)
+DW_AT (DW_AT_APPLE_isa, 0x3fe3)
+DW_AT (DW_AT_APPLE_block, 0x3fe4)
+DW_AT (DW_AT_APPLE_major_runtime_vers, 0x3fe5)
+DW_AT (DW_AT_APPLE_runtime_class, 0x3fe6)
+DW_AT (DW_AT_APPLE_omit_frame_ptr, 0x3fe7)
+DW_AT (DW_AT_APPLE_property_name, 0x3fe8)
+DW_AT (DW_AT_APPLE_property_getter, 0x3fe9)
+DW_AT (DW_AT_APPLE_property_setter, 0x3fea)
+DW_AT (DW_AT_APPLE_property_attribute, 0x3feb)
+DW_AT (DW_AT_APPLE_objc_complete_type, 0x3fec)
+DW_AT (DW_AT_APPLE_property, 0x3fed)
 DW_END_AT
 
 DW_FIRST_OP (DW_OP_addr, 0x03)
index c7d49ebb2401b6fb79ce4158e055760fb7d15744..4ada87162fa868eb80c6994e1877c6d9897ae732 100644 (file)
@@ -1,8 +1,6 @@
 /* Declarations and definitions of codes relating to the DWARF2 and
    DWARF3 symbolic debugging information formats.
-   Copyright (C) 1992, 1993, 1995, 1996, 1997, 1999, 2000, 2001, 2002,
-   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
-   Free Software Foundation, Inc.
+   Copyright (C) 1992-2015 Free Software Foundation, Inc.
 
    Written by Gary Funck (gary@intrepid.com) The Ada Joint Program
    Office (AJPO), Florida State University and Silicon Graphics Inc.
@@ -309,6 +307,12 @@ enum dwarf_source_language
     /* DWARF 5.  */
     DW_LANG_Go = 0x0016,
 
+    DW_LANG_C_plus_plus_11 = 0x001a, /* dwarf5.20141029.pdf DRAFT */
+    DW_LANG_C11 = 0x001d,
+    DW_LANG_C_plus_plus_14 = 0x0021,
+    DW_LANG_Fortran03 = 0x0022,
+    DW_LANG_Fortran08 = 0x0023,
+
     DW_LANG_lo_user = 0x8000,  /* Implementation-defined range start.  */
     DW_LANG_hi_user = 0xffff,  /* Implementation-defined range start.  */
 
@@ -352,7 +356,7 @@ enum dwarf_macro_record_type
     DW_MACRO_GNU_lo_user = 0xe0,
     DW_MACRO_GNU_hi_user = 0xff
   };
-
+\f
 /* @@@ For use with GNU frame unwind information.  */
 
 #define DW_EH_PE_absptr                0x00
index f0709c9c355dc4f028d95ffb703c0f894727ce41..05cc5c04734b71cae3ca4d9ca56f22085a566a80 100644 (file)
@@ -1,5 +1,5 @@
 /* elf.c -- Get debug data from an ELF file for backtraces.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
@@ -955,7 +955,8 @@ backtrace_initialize (struct backtrace_state *state, int descriptor,
       if (found_sym)
        backtrace_atomic_store_pointer (&state->syminfo_fn, elf_syminfo);
       else
-       __sync_bool_compare_and_swap (&state->syminfo_fn, NULL, elf_nosyms);
+       (void) __sync_bool_compare_and_swap (&state->syminfo_fn, NULL,
+                                            elf_nosyms);
     }
 
   if (!state->threaded)
index c151147213431f29cedda13f78655835dd93e9ac..27ebbedc21ccf39f86f439138c2e418b556ccbd8 100644 (file)
@@ -1,5 +1,5 @@
 /* fileline.c -- Get file and line number information in a backtrace.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index e799a51b6ad014d74581da6d92ce337be896c86a..1161daaa4f3581c81c4842509aa7c7298d3bbd7b 100644 (file)
@@ -5,7 +5,7 @@
    use forward- and back-slash in path names interchangeably, and
    some of them have case-insensitive file names.
 
-   Copyright 2000, 2001, 2007, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2000-2015 Free Software Foundation, Inc.
 
 This file is part of BFD, the Binary File Descriptor library.
 
@@ -90,6 +90,8 @@ extern hashval_t filename_hash (const void *s);
 
 extern int filename_eq (const void *s1, const void *s2);
 
+extern int canonical_filename_eq (const char *a, const char *b);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/libbacktrace/gstdint.h b/src/libbacktrace/gstdint.h
deleted file mode 100644 (file)
index f6bb897..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/* generated for  gcc (GCC) 4.8.2 20140206 (prerelease) */
-
-#ifndef GCC_GENERATED_STDINT_H
-#define GCC_GENERATED_STDINT_H 1
-
-#include <sys/types.h>
-#include <stdint.h>
-/* glibc uses these symbols as guards to prevent redefinitions.  */
-#ifdef __int8_t_defined
-#define _INT8_T
-#define _INT16_T
-#define _INT32_T
-#endif
-#ifdef __uint32_t_defined
-#define _UINT32_T
-#endif
-
-
-/* Some systems have guard macros to prevent redefinitions, define them.  */
-#ifndef _INT8_T
-#define _INT8_T
-#endif
-#ifndef _INT16_T
-#define _INT16_T
-#endif
-#ifndef _INT32_T
-#define _INT32_T
-#endif
-#ifndef _UINT8_T
-#define _UINT8_T
-#endif
-#ifndef _UINT16_T
-#define _UINT16_T
-#endif
-#ifndef _UINT32_T
-#define _UINT32_T
-#endif
-
-/* system headers have good uint64_t and int64_t */
-#ifndef _INT64_T
-#define _INT64_T
-#endif
-#ifndef _UINT64_T
-#define _UINT64_T
-#endif
-
-#endif /* GCC_GENERATED_STDINT_H */
index 3736622e67739cf97ea710b95a4fdbd66fbb9921..b1b5877aae7ca83dd2f04cc050c352b82e4a8d35 100644 (file)
@@ -1,6 +1,5 @@
-/* An expandable hash tables datatype.
-   Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2009, 2010
-   Free Software Foundation, Inc.
+/* An expandable hash tables datatype.  
+   Copyright (C) 1999-2015 Free Software Foundation, Inc.
    Contributed by Vladimir Makarov (vmakarov@cygnus.com).
 
 This program is free software; you can redistribute it and/or modify
@@ -39,10 +38,6 @@ extern "C" {
 
 #include "ansidecl.h"
 
-#ifndef GTY
-#define GTY(X)
-#endif
-
 /* The type for a hash code.  */
 typedef unsigned int hashval_t;
 
@@ -61,7 +56,7 @@ typedef int (*htab_eq) (const void *, const void *);
 /* Cleanup function called whenever a live element is removed from
    the hash table.  */
 typedef void (*htab_del) (void *);
-
+  
 /* Function called by htab_traverse for each live element.  The first
    arg is the slot of the element (which can be passed to htab_clear_slot
    if desired), the second arg is the auxiliary pointer handed to
@@ -97,7 +92,7 @@ typedef void (*htab_free_with_arg) (void *, void *);
    functions mentioned below.  The size of this structure is subject to
    change.  */
 
-struct GTY(()) htab {
+struct htab {
   /* Pointer to hash function.  */
   htab_hash hash_f;
 
@@ -108,7 +103,7 @@ struct GTY(()) htab {
   htab_del del_f;
 
   /* Table itself.  */
-  void ** GTY ((use_param, length ("%h.size"))) entries;
+  void **entries;
 
   /* Current size (in entries) of the hash table.  */
   size_t size;
@@ -132,7 +127,7 @@ struct GTY(()) htab {
   htab_free free_f;
 
   /* Alternate allocate/free functions, which take an extra argument.  */
-  void * GTY((skip)) alloc_arg;
+  void *alloc_arg;
   htab_alloc_with_arg alloc_with_arg_f;
   htab_free_with_arg free_with_arg_f;
 
old mode 100755 (executable)
new mode 100644 (file)
index 6781b98..0b0fdcb
@@ -1,7 +1,7 @@
 #!/bin/sh
 # install - install a program, script, or datafile
 
-scriptversion=2009-04-28.21; # UTC
+scriptversion=2013-12-25.23; # UTC
 
 # This originates from X11R5 (mit/util/scripts/install.sh), which was
 # later released in X11R6 (xc/config/util/install.sh) with the
@@ -35,25 +35,21 @@ scriptversion=2009-04-28.21; # UTC
 # FSF changes to this file are in the public domain.
 #
 # Calling this script install-sh is preferred over install.sh, to prevent
-# `make' implicit rules from creating a file called install from it
+# 'make' implicit rules from creating a file called install from it
 # when there is no Makefile.
 #
 # This script is compatible with the BSD install script, but was written
 # from scratch.
 
+tab='  '
 nl='
 '
-IFS=" ""       $nl"
+IFS=" $tab$nl"
 
-# set DOITPROG to echo to test this script
+# Set DOITPROG to "echo" to test this script.
 
-# Don't use :- since 4.3BSD and earlier shells don't like it.
 doit=${DOITPROG-}
-if test -z "$doit"; then
-  doit_exec=exec
-else
-  doit_exec=$doit
-fi
+doit_exec=${doit:-exec}
 
 # Put in absolute file names if you don't have them in your path;
 # or use environment vars.
@@ -68,17 +64,6 @@ mvprog=${MVPROG-mv}
 rmprog=${RMPROG-rm}
 stripprog=${STRIPPROG-strip}
 
-posix_glob='?'
-initialize_posix_glob='
-  test "$posix_glob" != "?" || {
-    if (set -f) 2>/dev/null; then
-      posix_glob=
-    else
-      posix_glob=:
-    fi
-  }
-'
-
 posix_mkdir=
 
 # Desired mode of installed file.
@@ -97,7 +82,7 @@ dir_arg=
 dst_arg=
 
 copy_on_change=false
-no_target_directory=
+is_target_a_directory=possibly
 
 usage="\
 Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
@@ -137,42 +122,57 @@ while test $# -ne 0; do
     -d) dir_arg=true;;
 
     -g) chgrpcmd="$chgrpprog $2"
-       shift;;
+        shift;;
 
     --help) echo "$usage"; exit $?;;
 
     -m) mode=$2
-       case $mode in
-         *' '* | *'    '* | *'
-'*       | *'*'* | *'?'* | *'['*)
-           echo "$0: invalid mode: $mode" >&2
-           exit 1;;
-       esac
-       shift;;
+        case $mode in
+          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+            echo "$0: invalid mode: $mode" >&2
+            exit 1;;
+        esac
+        shift;;
 
     -o) chowncmd="$chownprog $2"
-       shift;;
+        shift;;
 
     -s) stripcmd=$stripprog;;
 
-    -t) dst_arg=$2
-       shift;;
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
+        # Protect names problematic for 'test' and other utilities.
+        case $dst_arg in
+          -* | [=\(\)!]) dst_arg=./$dst_arg;;
+        esac
+        shift;;
 
-    -T) no_target_directory=true;;
+    -T) is_target_a_directory=never;;
 
     --version) echo "$0 $scriptversion"; exit $?;;
 
-    --)        shift
-       break;;
+    --) shift
+        break;;
 
-    -*)        echo "$0: invalid option: $1" >&2
-       exit 1;;
+    -*) echo "$0: invalid option: $1" >&2
+        exit 1;;
 
     *)  break;;
   esac
   shift
 done
 
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
+
 if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
   # When -d is used, all remaining arguments are directories to create.
   # When -t is used, the destination is already specified.
@@ -186,6 +186,10 @@ if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
     fi
     shift # arg
     dst_arg=$arg
+    # Protect names problematic for 'test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
   done
 fi
 
@@ -194,13 +198,26 @@ if test $# -eq 0; then
     echo "$0: no input file specified." >&2
     exit 1
   fi
-  # It's OK to call `install-sh -d' without argument.
+  # It's OK to call 'install-sh -d' without argument.
   # This can happen when creating conditional directories.
   exit 0
 fi
 
 if test -z "$dir_arg"; then
-  trap '(exit $?); exit' 1 2 13 15
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
+
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
 
   # Set umask so as not to create temps with too-generous modes.
   # However, 'strip' requires both read and write access to temps.
@@ -211,16 +228,16 @@ if test -z "$dir_arg"; then
 
     *[0-7])
       if test -z "$stripcmd"; then
-       u_plus_rw=
+        u_plus_rw=
       else
-       u_plus_rw='% 200'
+        u_plus_rw='% 200'
       fi
       cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
     *)
       if test -z "$stripcmd"; then
-       u_plus_rw=
+        u_plus_rw=
       else
-       u_plus_rw=,u+rw
+        u_plus_rw=,u+rw
       fi
       cp_umask=$mode$u_plus_rw;;
   esac
@@ -228,9 +245,9 @@ fi
 
 for src
 do
-  # Protect names starting with `-'.
+  # Protect names problematic for 'test' and other utilities.
   case $src in
-    -*) src=./$src;;
+    -* | [=\(\)!]) src=./$src;;
   esac
 
   if test -n "$dir_arg"; then
@@ -252,51 +269,20 @@ do
       echo "$0: no destination specified." >&2
       exit 1
     fi
-
     dst=$dst_arg
-    # Protect names starting with `-'.
-    case $dst in
-      -*) dst=./$dst;;
-    esac
 
     # If destination is a directory, append the input filename; won't work
     # if double slashes aren't ignored.
     if test -d "$dst"; then
-      if test -n "$no_target_directory"; then
-       echo "$0: $dst_arg: Is a directory" >&2
-       exit 1
+      if test "$is_target_a_directory" = never; then
+        echo "$0: $dst_arg: Is a directory" >&2
+        exit 1
       fi
       dstdir=$dst
       dst=$dstdir/`basename "$src"`
       dstdir_status=0
     else
-      # Prefer dirname, but fall back on a substitute if dirname fails.
-      dstdir=`
-       (dirname "$dst") 2>/dev/null ||
-       expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-            X"$dst" : 'X\(//\)[^/]' \| \
-            X"$dst" : 'X\(//\)$' \| \
-            X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
-       echo X"$dst" |
-           sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
-                  s//\1/
-                  q
-                }
-                /^X\(\/\/\)[^/].*/{
-                  s//\1/
-                  q
-                }
-                /^X\(\/\/\)$/{
-                  s//\1/
-                  q
-                }
-                /^X\(\/\).*/{
-                  s//\1/
-                  q
-                }
-                s/.*/./; q'
-      `
-
+      dstdir=`dirname "$dst"`
       test -d "$dstdir"
       dstdir_status=$?
     fi
@@ -307,74 +293,74 @@ do
   if test $dstdir_status != 0; then
     case $posix_mkdir in
       '')
-       # Create intermediate dirs using mode 755 as modified by the umask.
-       # This is like FreeBSD 'install' as of 1997-10-28.
-       umask=`umask`
-       case $stripcmd.$umask in
-         # Optimize common cases.
-         *[2367][2367]) mkdir_umask=$umask;;
-         .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
-         *[0-7])
-           mkdir_umask=`expr $umask + 22 \
-             - $umask % 100 % 40 + $umask % 20 \
-             - $umask % 10 % 4 + $umask % 2
-           `;;
-         *) mkdir_umask=$umask,go-w;;
-       esac
-
-       # With -d, create the new directory with the user-specified mode.
-       # Otherwise, rely on $mkdir_umask.
-       if test -n "$dir_arg"; then
-         mkdir_mode=-m$mode
-       else
-         mkdir_mode=
-       fi
-
-       posix_mkdir=false
-       case $umask in
-         *[123567][0-7][0-7])
-           # POSIX mkdir -p sets u+wx bits regardless of umask, which
-           # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
-           ;;
-         *)
-           tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
-           trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
-
-           if (umask $mkdir_umask &&
-               exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
-           then
-             if test -z "$dir_arg" || {
-                  # Check for POSIX incompatibilities with -m.
-                  # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
-                  # other-writeable bit of parent directory when it shouldn't.
-                  # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
-                  ls_ld_tmpdir=`ls -ld "$tmpdir"`
-                  case $ls_ld_tmpdir in
-                    d????-?r-*) different_mode=700;;
-                    d????-?--*) different_mode=755;;
-                    *) false;;
-                  esac &&
-                  $mkdirprog -m$different_mode -p -- "$tmpdir" && {
-                    ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
-                    test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
-                  }
-                }
-             then posix_mkdir=:
-             fi
-             rmdir "$tmpdir/d" "$tmpdir"
-           else
-             # Remove any dirs left behind by ancient mkdir implementations.
-             rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
-           fi
-           trap '' 0;;
-       esac;;
+        # Create intermediate dirs using mode 755 as modified by the umask.
+        # This is like FreeBSD 'install' as of 1997-10-28.
+        umask=`umask`
+        case $stripcmd.$umask in
+          # Optimize common cases.
+          *[2367][2367]) mkdir_umask=$umask;;
+          .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+          *[0-7])
+            mkdir_umask=`expr $umask + 22 \
+              - $umask % 100 % 40 + $umask % 20 \
+              - $umask % 10 % 4 + $umask % 2
+            `;;
+          *) mkdir_umask=$umask,go-w;;
+        esac
+
+        # With -d, create the new directory with the user-specified mode.
+        # Otherwise, rely on $mkdir_umask.
+        if test -n "$dir_arg"; then
+          mkdir_mode=-m$mode
+        else
+          mkdir_mode=
+        fi
+
+        posix_mkdir=false
+        case $umask in
+          *[123567][0-7][0-7])
+            # POSIX mkdir -p sets u+wx bits regardless of umask, which
+            # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+            ;;
+          *)
+            tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+            trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+            if (umask $mkdir_umask &&
+                exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+            then
+              if test -z "$dir_arg" || {
+                   # Check for POSIX incompatibilities with -m.
+                   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+                   # other-writable bit of parent directory when it shouldn't.
+                   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+                   ls_ld_tmpdir=`ls -ld "$tmpdir"`
+                   case $ls_ld_tmpdir in
+                     d????-?r-*) different_mode=700;;
+                     d????-?--*) different_mode=755;;
+                     *) false;;
+                   esac &&
+                   $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+                     ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+                     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+                   }
+                 }
+              then posix_mkdir=:
+              fi
+              rmdir "$tmpdir/d" "$tmpdir"
+            else
+              # Remove any dirs left behind by ancient mkdir implementations.
+              rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+            fi
+            trap '' 0;;
+        esac;;
     esac
 
     if
       $posix_mkdir && (
-       umask $mkdir_umask &&
-       $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+        umask $mkdir_umask &&
+        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
       )
     then :
     else
@@ -384,53 +370,51 @@ do
       # directory the slow way, step by step, checking for races as we go.
 
       case $dstdir in
-       /*) prefix='/';;
-       -*) prefix='./';;
-       *)  prefix='';;
+        /*) prefix='/';;
+        [-=\(\)!]*) prefix='./';;
+        *)  prefix='';;
       esac
 
-      eval "$initialize_posix_glob"
-
       oIFS=$IFS
       IFS=/
-      $posix_glob set -f
+      set -f
       set fnord $dstdir
       shift
-      $posix_glob set +f
+      set +f
       IFS=$oIFS
 
       prefixes=
 
       for d
       do
-       test -z "$d" && continue
-
-       prefix=$prefix$d
-       if test -d "$prefix"; then
-         prefixes=
-       else
-         if $posix_mkdir; then
-           (umask=$mkdir_umask &&
-            $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
-           # Don't fail if two instances are running concurrently.
-           test -d "$prefix" || exit 1
-         else
-           case $prefix in
-             *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
-             *) qprefix=$prefix;;
-           esac
-           prefixes="$prefixes '$qprefix'"
-         fi
-       fi
-       prefix=$prefix/
+        test X"$d" = X && continue
+
+        prefix=$prefix$d
+        if test -d "$prefix"; then
+          prefixes=
+        else
+          if $posix_mkdir; then
+            (umask=$mkdir_umask &&
+             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+            # Don't fail if two instances are running concurrently.
+            test -d "$prefix" || exit 1
+          else
+            case $prefix in
+              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+              *) qprefix=$prefix;;
+            esac
+            prefixes="$prefixes '$qprefix'"
+          fi
+        fi
+        prefix=$prefix/
       done
 
       if test -n "$prefixes"; then
-       # Don't fail if two instances are running concurrently.
-       (umask $mkdir_umask &&
-        eval "\$doit_exec \$mkdirprog $prefixes") ||
-         test -d "$dstdir" || exit 1
-       obsolete_mkdir_used=true
+        # Don't fail if two instances are running concurrently.
+        (umask $mkdir_umask &&
+         eval "\$doit_exec \$mkdirprog $prefixes") ||
+          test -d "$dstdir" || exit 1
+        obsolete_mkdir_used=true
       fi
     fi
   fi
@@ -465,15 +449,12 @@ do
 
     # If -C, don't bother to copy if it wouldn't change the file.
     if $copy_on_change &&
-       old=`LC_ALL=C ls -dlL "$dst"    2>/dev/null` &&
-       new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
-
-       eval "$initialize_posix_glob" &&
-       $posix_glob set -f &&
+       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
+       set -f &&
        set X $old && old=:$2:$4:$5:$6 &&
        set X $new && new=:$2:$4:$5:$6 &&
-       $posix_glob set +f &&
-
+       set +f &&
        test "$old" = "$new" &&
        $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
     then
@@ -486,24 +467,24 @@ do
       # to itself, or perhaps because mv is so ancient that it does not
       # support -f.
       {
-       # Now remove or move aside any old file at destination location.
-       # We try this two ways since rm can't unlink itself on some
-       # systems and the destination file might be busy for other
-       # reasons.  In this case, the final cleanup might fail but the new
-       # file should still install successfully.
-       {
-         test ! -f "$dst" ||
-         $doit $rmcmd -f "$dst" 2>/dev/null ||
-         { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
-           { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
-         } ||
-         { echo "$0: cannot unlink or rename $dst" >&2
-           (exit 1); exit 1
-         }
-       } &&
-
-       # Now rename the file to the real destination.
-       $doit $mvcmd "$dsttmp" "$dst"
+        # Now remove or move aside any old file at destination location.
+        # We try this two ways since rm can't unlink itself on some
+        # systems and the destination file might be busy for other
+        # reasons.  In this case, the final cleanup might fail but the new
+        # file should still install successfully.
+        {
+          test ! -f "$dst" ||
+          $doit $rmcmd -f "$dst" 2>/dev/null ||
+          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+            { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+          } ||
+          { echo "$0: cannot unlink or rename $dst" >&2
+            (exit 1); exit 1
+          }
+        } &&
+
+        # Now rename the file to the real destination.
+        $doit $mvcmd "$dsttmp" "$dst"
       }
     fi || exit 1
 
index f6046ee6057c5c621fe0fedd22c7a289e2522903..73728da3f5664db0306a9ffd6e23066fff2df45d 100644 (file)
@@ -1,5 +1,5 @@
 /* internal.h -- Internal header file for stack backtrace library.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
@@ -201,13 +201,15 @@ extern int backtrace_close (int descriptor,
 extern void backtrace_qsort (void *base, size_t count, size_t size,
                             int (*compar) (const void *, const void *));
 
-/* Allocate memory.  This is like malloc.  */
+/* Allocate memory.  This is like malloc.  If ERROR_CALLBACK is NULL,
+   this does not report an error, it just returns NULL.  */
 
 extern void *backtrace_alloc (struct backtrace_state *state, size_t size,
                              backtrace_error_callback error_callback,
                              void *data) ATTRIBUTE_MALLOC;
 
-/* Free memory allocated by backtrace_alloc.  */
+/* Free memory allocated by backtrace_alloc.  If ERROR_CALLBACK is
+   NULL, this does not report an error.  */
 
 extern void backtrace_free (struct backtrace_state *state, void *mem,
                            size_t size,
index 84f5be9249e7caae28e6c16faccaba9f84096f12..eaef55a59332a88c6bcb008a56214d48518c6546 100644 (file)
@@ -8633,3 +8633,4 @@ build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
 # sh-indentation:2
 # End:
 # vi:sw=2
+
index 610548a8a4e220d9b008b7d23a17ff9a0c9c603f..0ed4802d02de424e62449465cd4761996081f75c 100644 (file)
@@ -1,5 +1,5 @@
 /* mmap.c -- Memory allocation with mmap.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
@@ -77,7 +77,8 @@ backtrace_free_locked (struct backtrace_state *state, void *addr, size_t size)
     }
 }
 
-/* Allocate memory like malloc.  */
+/* Allocate memory like malloc.  If ERROR_CALLBACK is NULL, don't
+   report an error.  */
 
 void *
 backtrace_alloc (struct backtrace_state *state,
@@ -139,8 +140,11 @@ backtrace_alloc (struct backtrace_state *state,
       asksize = (size + pagesize - 1) & ~ (pagesize - 1);
       page = mmap (NULL, asksize, PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-      if (page == NULL)
-       error_callback (data, "mmap", errno);
+      if (page == MAP_FAILED)
+       {
+         if (error_callback)
+           error_callback (data, "mmap", errno);
+       }
       else
        {
          size = (size + 7) & ~ (size_t) 7;
index 45f81a8593d9f72197a0001cb8e93e0b920d3fb2..dfdaf6fa52e6e774c1c59e5b4509b8c54a7e110a 100644 (file)
@@ -1,5 +1,5 @@
 /* mmapio.c -- File views using mmap.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index 9952c0bcbfb7bd5b8c452837bd1690612d301d20..448a2049f1d1b46aa393f3b17e60d9a799181fc4 100644 (file)
@@ -1,5 +1,5 @@
 /* backtrace.c -- Entry point for stack backtrace library.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index ba555b56582804ccf1dc6da0e3ceb24a1dd6d29d..31126cf4741c7abb72e56d8e08b8fa94822b004c 100644 (file)
@@ -1,5 +1,5 @@
 /* pecoff.c -- Get debug data from a PE/COFFF file for backtraces.
-   Copyright (C) 2015 Free Software Foundation, Inc.
+   Copyright (C) 2015-2016 Free Software Foundation, Inc.
    Adapted from elf.c by Tristan Gingold, AdaCore.
 
 Redistribution and use in source and binary forms, with or without
@@ -602,7 +602,7 @@ coff_add (struct backtrace_state *state, int descriptor,
   const b_coff_section_header *sects;
   struct backtrace_view str_view;
   int str_view_valid;
-  size_t str_size;
+  uint32_t str_size;
   off_t str_off;
   struct backtrace_view syms_view;
   off_t syms_off;
index f6260a0044eaa0855d6202bd329fa40cf0107569..09f5e95a6e42d824b1ef09d3d4ff489e61644f58 100644 (file)
@@ -1,5 +1,5 @@
 /* posix.c -- POSIX file I/O routines for the backtrace library.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
@@ -64,7 +64,7 @@ backtrace_open (const char *filename, backtrace_error_callback error_callback,
   if (does_not_exist != NULL)
     *does_not_exist = 0;
 
-  descriptor = open (filename, O_RDONLY | O_BINARY | O_CLOEXEC);
+  descriptor = open (filename, (int) (O_RDONLY | O_BINARY | O_CLOEXEC));
   if (descriptor < 0)
     {
       if (does_not_exist != NULL && errno == ENOENT)
index 271f41c0c59bfeebcb6c9d72d2ac659ca0afcacd..74c8fcbee5a1f9bc853a0bb6e3607a15a20f9dba 100644 (file)
@@ -1,5 +1,5 @@
 /* print.c -- Print the current backtrace.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index 70dd91ee97c8fd805e59be4c087a4b9b2669817b..7f0317c3a8ce91e3de6c8bc95cae597e3163faf2 100644 (file)
@@ -1,5 +1,5 @@
 /* read.c -- File views without mmap.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index fc0f4f498012c883b3dea172e5b94423605a8724..018773a7e5dbdb66de407529f2508a5cd5769b54 100644 (file)
@@ -1,5 +1,5 @@
 /* simple.c -- The backtrace_simple function.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index 01b1cb2b8a5553cf397ab3c01ca64088172210d8..68a7df65a47f6e4cd8bcc9171410c269b462033e 100644 (file)
@@ -1,5 +1,5 @@
 /* sort.c -- Sort without allocating memory
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index 373938865c7fff4236ca6fd6a85fd2e5302e478b..93420d9c61b19939ebd28d4fe0dccae745d2af57 100644 (file)
@@ -1,5 +1,5 @@
 /* state.c -- Create the backtrace state.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index 510715291895ca8065ee95e3180df4d06388210d..55ec31d10bc8168343550e57a71da06dbd5f64fd 100644 (file)
@@ -1,5 +1,5 @@
 /* stest.c -- Test for libbacktrace internal sort function
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index 953e96e510e593f33369d1f5bf28d68bd347722c..8d06c31549f70a0fbfa049bd9eda0921591b4a31 100644 (file)
@@ -1,5 +1,5 @@
 /* unknown.c -- used when backtrace configury does not know file format.
-   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Google.
 
 Redistribution and use in source and binary forms, with or without
@@ -7,13 +7,13 @@ modification, are permitted provided that the following conditions are
 met:
 
     (1) Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
+    notice, this list of conditions and the following disclaimer. 
 
     (2) Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
-    distribution.
-
+    distribution.  
+    
     (3) The name of the author may not be used to
     endorse or promote products derived from this software without
     specific prior written permission.
index b8ca48ac75d1a7c5248bf918ce98d84ca7246149..bd329949618e5f4376860f8b3af77db4a041be75 100644 (file)
@@ -66,7 +66,7 @@
 //! // to each node. This implementation isn't memory-efficient as it may leave duplicate
 //! // nodes in the queue. It also uses `usize::MAX` as a sentinel value,
 //! // for a simpler implementation.
-//! fn shortest_path(adj_list: &Vec<Vec<Edge>>, start: usize, goal: usize) -> usize {
+//! fn shortest_path(adj_list: &Vec<Vec<Edge>>, start: usize, goal: usize) -> Option<usize> {
 //!     // dist[node] = current shortest distance from `start` to `node`
 //!     let mut dist: Vec<_> = (0..adj_list.len()).map(|_| usize::MAX).collect();
 //!
@@ -79,7 +79,7 @@
 //!     // Examine the frontier with lower cost nodes first (min-heap)
 //!     while let Some(State { cost, position }) = heap.pop() {
 //!         // Alternatively we could have continued to find all shortest paths
-//!         if position == goal { return cost; }
+//!         if position == goal { return Some(cost); }
 //!
 //!         // Important as we may have already found a better way
 //!         if cost > dist[position] { continue; }
@@ -99,7 +99,7 @@
 //!     }
 //!
 //!     // Goal not reachable
-//!     usize::MAX
+//!     None
 //! }
 //!
 //! fn main() {
 //!         // Node 4
 //!         vec![]];
 //!
-//!     assert_eq!(shortest_path(&graph, 0, 1), 1);
-//!     assert_eq!(shortest_path(&graph, 0, 3), 3);
-//!     assert_eq!(shortest_path(&graph, 3, 0), 7);
-//!     assert_eq!(shortest_path(&graph, 0, 4), 5);
-//!     assert_eq!(shortest_path(&graph, 4, 0), usize::MAX);
+//!     assert_eq!(shortest_path(&graph, 0, 1), Some(1));
+//!     assert_eq!(shortest_path(&graph, 0, 3), Some(3));
+//!     assert_eq!(shortest_path(&graph, 3, 0), Some(7));
+//!     assert_eq!(shortest_path(&graph, 0, 4), Some(5));
+//!     assert_eq!(shortest_path(&graph, 4, 0), None);
 //! }
 //! ```
 
@@ -230,26 +230,6 @@ impl<T: Ord> BinaryHeap<T> {
         BinaryHeap { data: Vec::with_capacity(capacity) }
     }
 
-    /// Creates a `BinaryHeap` from a vector. This is sometimes called
-    /// `heapifying` the vector.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(binary_heap_extras)]
-    /// # #![allow(deprecated)]
-    ///
-    /// use std::collections::BinaryHeap;
-    /// let heap = BinaryHeap::from_vec(vec![9, 1, 2, 7, 3, 2]);
-    /// ```
-    #[unstable(feature = "binary_heap_extras",
-               reason = "needs to be audited",
-               issue = "28147")]
-    #[rustc_deprecated(since = "1.5.0", reason = "use BinaryHeap::from instead")]
-    pub fn from_vec(vec: Vec<T>) -> BinaryHeap<T> {
-        BinaryHeap::from(vec)
-    }
-
     /// Returns an iterator visiting all values in the underlying vector, in
     /// arbitrary order.
     ///
@@ -374,7 +354,7 @@ impl<T: Ord> BinaryHeap<T> {
         self.data.pop().map(|mut item| {
             if !self.is_empty() {
                 swap(&mut item, &mut self.data[0]);
-                self.sift_down(0);
+                self.sift_down_to_bottom(0);
             }
             item
         })
@@ -565,6 +545,31 @@ impl<T: Ord> BinaryHeap<T> {
         self.sift_down_range(pos, len);
     }
 
+    /// Take an element at `pos` and move it all the way down the heap,
+    /// then sift it up to its position.
+    ///
+    /// Note: This is faster when the element is known to be large / should
+    /// be closer to the bottom.
+    fn sift_down_to_bottom(&mut self, mut pos: usize) {
+        let end = self.len();
+        let start = pos;
+        unsafe {
+            let mut hole = Hole::new(&mut self.data, pos);
+            let mut child = 2 * pos + 1;
+            while child < end {
+                let right = child + 1;
+                // compare with the greater of the two children
+                if right < end && !(hole.get(child) > hole.get(right)) {
+                    child = right;
+                }
+                hole.move_to(child);
+                child = 2 * hole.pos() + 1;
+            }
+            pos = hole.pos;
+        }
+        self.sift_up(start, pos);
+    }
+
     /// Returns the length of the binary heap.
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn len(&self) -> usize {
index bfd4c2e96b587acc7a41513c18e7fd8706878e3c..f174cc09bcd3525a324bf64635912b9fd7742c91 100644 (file)
@@ -95,11 +95,13 @@ pub enum Cow<'a, B: ?Sized + 'a>
 {
     /// Borrowed data.
     #[stable(feature = "rust1", since = "1.0.0")]
-    Borrowed(&'a B),
+    Borrowed(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] &'a B),
 
     /// Owned data.
     #[stable(feature = "rust1", since = "1.0.0")]
-    Owned(<B as ToOwned>::Owned),
+    Owned(
+        #[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] <B as ToOwned>::Owned
+    ),
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -245,12 +247,15 @@ impl<'a, B: ?Sized> Hash for Cow<'a, B> where B: Hash + ToOwned {
 /// Trait for moving into a `Cow`.
 #[unstable(feature = "into_cow", reason = "may be replaced by `convert::Into`",
            issue = "27735")]
+#[rustc_deprecated(since = "1.7.0",
+                   reason = "conflicts with Into, may return with specialization")]
 pub trait IntoCow<'a, B: ?Sized> where B: ToOwned {
     /// Moves `self` into `Cow`
     fn into_cow(self) -> Cow<'a, B>;
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
+#[allow(deprecated)]
 impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned {
     fn into_cow(self) -> Cow<'a, B> {
         self
@@ -258,6 +263,7 @@ impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
+#[allow(deprecated)]
 impl<'a, T: ?Sized + ToOwned> AsRef<T> for Cow<'a, T> {
     fn as_ref(&self) -> &T {
         self
index 08b0a39d9b0065a503cfc5f25b19d71489157a61..492263da2bc5be2c2d75900abfc72553f2297b4e 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -8,32 +8,25 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// This implementation is largely based on the high-level description and analysis of B-Trees
-// found in *Open Data Structures* (ODS). Although our implementation does not use any of
-// the source found in ODS, if one wishes to review the high-level design of this structure, it
-// can be freely downloaded at http://opendatastructures.org/. Its contents are as of this
-// writing (August 2014) freely licensed under the following Creative Commons Attribution
-// License: [CC BY 2.5 CA](http://creativecommons.org/licenses/by/2.5/ca/).
-
-use self::Entry::*;
-
 use core::cmp::Ordering;
 use core::fmt::Debug;
 use core::hash::{Hash, Hasher};
-use core::iter::{Map, FromIterator};
+use core::iter::FromIterator;
+use core::marker::PhantomData;
 use core::ops::Index;
-use core::{fmt, mem, usize};
-use Bound::{self, Included, Excluded, Unbounded};
+use core::{fmt, intrinsics, mem, ptr};
 
 use borrow::Borrow;
-use vec_deque::VecDeque;
+use Bound::{self, Included, Excluded, Unbounded};
+
+use super::node::{self, NodeRef, Handle, marker};
+use super::search;
 
-use self::Continuation::{Continue, Finished};
-use self::StackOp::*;
-use super::node::ForceResult::{Leaf, Internal};
-use super::node::TraversalItem::{self, Elem, Edge};
-use super::node::{Traversal, MutTraversal, MoveTraversal};
-use super::node::{self, Node, Found, GoDown};
+use super::node::InsertResult::*;
+use super::node::ForceResult::*;
+use super::search::SearchResult::*;
+use self::UnderflowResult::*;
+use self::Entry::*;
 
 /// A map based on a B-Tree.
 ///
@@ -65,60 +58,178 @@ use super::node::{self, Node, Found, GoDown};
 /// It is a logic error for a key to be modified in such a way that the key's ordering relative to
 /// any other key, as determined by the `Ord` trait, changes while it is in the map. This is
 /// normally only possible through `Cell`, `RefCell`, global state, I/O, or unsafe code.
-#[derive(Clone)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct BTreeMap<K, V> {
-    root: Node<K, V>,
-    length: usize,
-    depth: usize,
-    b: usize,
+    root: node::Root<K, V>,
+    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() { }
+        }
+    }
+}
+
+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> {
+
+            match node.force() {
+                Leaf(leaf) => {
+                    let mut out_tree = BTreeMap {
+                        root: node::Root::new_leaf(),
+                        length: 0
+                    };
+
+                    {
+                        let mut out_node = match out_tree.root.as_mut().force() {
+                            Leaf(leaf) => leaf,
+                            Internal(_) => unreachable!()
+                        };
+
+                        let mut in_edge = leaf.first_edge();
+                        while let Ok(kv) = in_edge.right_kv() {
+                            let (k, v) = kv.into_kv();
+                            in_edge = kv.right_edge();
+
+                            out_node.push(k.clone(), v.clone());
+                            out_tree.length += 1;
+                        }
+                    }
+
+                    out_tree
+                },
+                Internal(internal) => {
+                    let mut out_tree = clone_subtree(internal.first_edge().descend());
+
+                    {
+                        let mut out_node = out_tree.root.push_level();
+                        let mut in_edge = internal.first_edge();
+                        while let Ok(kv) = in_edge.right_kv() {
+                            let (k, v) = kv.into_kv();
+                            in_edge = kv.right_edge();
+
+                            let k = (*k).clone();
+                            let v = (*v).clone();
+                            let subtree = clone_subtree(in_edge.descend());
+
+                            // We can't destructure subtree directly
+                            // because BTreeMap implements Drop
+                            let (subroot, sublength) = unsafe {
+                                let root = ptr::read(&subtree.root);
+                                let length = subtree.length;
+                                mem::forget(subtree);
+                                (root, length)
+                            };
+
+                            out_node.push(k, v, subroot);
+                            out_tree.length += 1 + sublength;
+                        }
+                    }
+
+                    out_tree
+                }
+            }
+        }
+
+        clone_subtree(self.root.as_ref())
+    }
 }
 
-/// An abstract base over-which all other BTree iterators are built.
-#[derive(Clone)]
-struct AbsIter<T> {
-    traversals: VecDeque<T>,
-    size: usize,
+impl<K, Q: ?Sized> super::Recover<Q> for BTreeMap<K, ()>
+    where K: Borrow<Q> + Ord,
+          Q: Ord
+{
+    type Key = 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
+        }
+    }
+
+    fn take(&mut self, key: &Q) -> Option<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
+        }
+    }
+
+    fn replace(&mut self, key: K) -> Option<K> {
+        match search::search_tree::<marker::Mut, K, (), K>(self.root.as_mut(), &key) {
+            Found(handle) => Some(mem::replace(handle.into_kv_mut().0, key)),
+            GoDown(handle) => {
+                VacantEntry {
+                    key: key,
+                    handle: handle,
+                    length: &mut self.length,
+                    _marker: PhantomData,
+                }.insert(());
+                None
+            }
+        }
+    }
 }
 
 /// An iterator over a BTreeMap's entries.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Iter<'a, K: 'a, V: 'a> {
-    inner: AbsIter<Traversal<'a, K, V>>,
+    range: Range<'a, K, V>,
+    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> {
-    inner: AbsIter<MutTraversal<'a, K, V>>,
+    range: RangeMut<'a, K, V>,
+    length: usize
 }
 
 /// An owning iterator over a BTreeMap's entries.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct IntoIter<K, V> {
-    inner: AbsIter<MoveTraversal<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
 }
 
 /// An iterator over a BTreeMap's keys.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Keys<'a, K: 'a, V: 'a> {
-    inner: Map<Iter<'a, K, V>, fn((&'a K, &'a V)) -> &'a K>,
+    inner: Iter<'a, K, V>,
 }
 
 /// An iterator over a BTreeMap's values.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Values<'a, K: 'a, V: 'a> {
-    inner: Map<Iter<'a, K, V>, fn((&'a K, &'a V)) -> &'a V>,
+    inner: Iter<'a, K, V>,
 }
 
 /// An iterator over a sub-range of BTreeMap's entries.
 pub struct Range<'a, K: 'a, V: 'a> {
-    inner: AbsIter<Traversal<'a, K, V>>,
+    front: 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.
 pub struct RangeMut<'a, K: 'a, V: 'a> {
-    inner: AbsIter<MutTraversal<'a, K, V>>,
+    front: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>,
+    back: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>,
+
+    // Be invariant in `K` and `V`
+    _marker: PhantomData<&'a mut (K, V)>,
 }
 
 /// A view into a single entry in a map, which may either be vacant or occupied.
@@ -126,49 +237,50 @@ 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(VacantEntry<'a, K, V>),
+    Vacant(
+        #[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] VacantEntry<'a, K, V>
+    ),
 
     /// An occupied Entry
     #[stable(feature = "rust1", since = "1.0.0")]
-    Occupied(OccupiedEntry<'a, K, V>),
+    Occupied(
+        #[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] OccupiedEntry<'a, K, V>
+    ),
 }
 
 /// A vacant Entry.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct VacantEntry<'a, K: 'a, V: 'a> {
     key: K,
-    stack: stack::SearchStack<'a, K, V, node::handle::Edge, node::handle::Leaf>,
+    handle: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>,
+    length: &'a mut usize,
+
+    // Be invariant in `K` and `V`
+    _marker: PhantomData<&'a mut (K, V)>,
 }
 
 /// An occupied Entry.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
-    stack: stack::SearchStack<'a, K, V, node::handle::KV, node::handle::LeafOrInternal>,
+    handle: Handle<NodeRef<
+        marker::Mut<'a>,
+        K, V,
+        marker::LeafOrInternal
+    >, marker::KV>,
+
+    length: &'a mut usize,
+
+    // Be invariant in `K` and `V`
+    _marker: PhantomData<&'a mut (K, V)>,
 }
 
 impl<K: Ord, V> BTreeMap<K, V> {
     /// Makes a new empty BTreeMap with a reasonable choice for B.
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[allow(deprecated)]
     pub fn new() -> BTreeMap<K, V> {
-        // FIXME(Gankro): Tune this as a function of size_of<K/V>?
-        BTreeMap::with_b(6)
-    }
-
-    /// Makes a new empty BTreeMap with the given B.
-    ///
-    /// B cannot be less than 2.
-    #[unstable(feature = "btree_b",
-               reason = "probably want this to be on the type, eventually",
-               issue = "27795")]
-    #[rustc_deprecated(since = "1.4.0", reason = "niche API")]
-    pub fn with_b(b: usize) -> BTreeMap<K, V> {
-        assert!(b > 1, "B must be greater than 1");
         BTreeMap {
-            length: 0,
-            depth: 1,
-            root: Node::make_leaf_root(b),
-            b: b,
+            root: node::Root::new_leaf(),
+            length: 0
         }
     }
 
@@ -185,21 +297,11 @@ impl<K: Ord, V> BTreeMap<K, V> {
     /// assert!(a.is_empty());
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[allow(deprecated)]
     pub fn clear(&mut self) {
-        let b = self.b;
-        // avoid recursive destructors by manually traversing the tree
-        for _ in mem::replace(self, BTreeMap::with_b(b)) {}
+        // FIXME(gereeter) .clear() allocates
+        *self = BTreeMap::new();
     }
 
-    // Searching in a B-Tree is pretty straightforward.
-    //
-    // Start at the root. Try to find the key in the current node. If we find it, return it.
-    // If it's not in there, follow the edge *before* the smallest key larger than
-    // the search key. If no such key exists (they're *all* smaller), then just take the last
-    // edge in the node. If we're in a leaf and we don't find our key, then it's not
-    // in the tree.
-
     /// Returns a reference to the value corresponding to the key.
     ///
     /// The key may be any borrowed form of the map's key type, but the ordering
@@ -216,24 +318,10 @@ 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
-    {
-        let mut cur_node = &self.root;
-        loop {
-            match Node::search(cur_node, key) {
-                Found(handle) => return Some(handle.into_kv().1),
-                GoDown(handle) => {
-                    match handle.force() {
-                        Leaf(_) => return None,
-                        Internal(internal_handle) => {
-                            cur_node = internal_handle.into_edge();
-                            continue;
-                        }
-                    }
-                }
-            }
+    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
         }
     }
 
@@ -253,10 +341,7 @@ 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()
     }
 
@@ -279,55 +364,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
-    {
-        // temp_node is a Borrowck hack for having a mutable value outlive a loop iteration
-        let mut temp_node = &mut self.root;
-        loop {
-            let cur_node = temp_node;
-            match Node::search(cur_node, key) {
-                Found(handle) => return Some(handle.into_kv_mut().1),
-                GoDown(handle) => {
-                    match handle.force() {
-                        Leaf(_) => return None,
-                        Internal(internal_handle) => {
-                            temp_node = internal_handle.into_edge_mut();
-                            continue;
-                        }
-                    }
-                }
-            }
+    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
         }
     }
 
-    // Insertion in a B-Tree is a bit complicated.
-    //
-    // First we do the same kind of search described in `find`. But we need to maintain a stack of
-    // all the nodes/edges in our search path. If we find a match for the key we're trying to
-    // insert, just swap the vals and return the old ones. However, when we bottom out in a leaf,
-    // we attempt to insert our key-value pair at the same location we would want to follow another
-    // edge.
-    //
-    // If the node has room, then this is done in the obvious way by shifting elements. However,
-    // if the node itself is full, we split node into two, and give its median key-value
-    // pair to its parent to insert the new node with. Of course, the parent may also be
-    // full, and insertion can propagate until we reach the root. If we reach the root, and
-    // it is *also* full, then we split the root and place the two nodes under a newly made root.
-    //
-    // Note that we subtly deviate from Open Data Structures in our implementation of split.
-    // ODS describes inserting into the node *regardless* of its capacity, and then
-    // splitting *afterwards* if it happens to be overfull. However, this is inefficient.
-    // Instead, we split beforehand, and then insert the key-value pair into the appropriate
-    // result node. This has two consequences:
-    //
-    // 1) While ODS produces a left node of size B-1, and a right node of size B,
-    // we may potentially reverse this. However, this shouldn't effect the analysis.
-    //
-    // 2) While ODS may potentially return the pair we *just* inserted after
-    // the split, we will never do this. Again, this shouldn't effect the analysis.
-
     /// Inserts a key-value pair into the map.
     ///
     /// If the map did not have this key present, `None` is returned.
@@ -352,98 +395,16 @@ impl<K: Ord, V> BTreeMap<K, V> {
     /// assert_eq!(map[&37], "c");
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn insert(&mut self, mut key: K, mut value: V) -> Option<V> {
-        // This is a stack of rawptrs to nodes paired with indices, respectively
-        // representing the nodes and edges of our search path. We have to store rawptrs
-        // because as far as Rust is concerned, we can mutate aliased data with such a
-        // stack. It is of course correct, but what it doesn't know is that we will only
-        // be popping and using these ptrs one at a time in child-to-parent order. The alternative
-        // to doing this is to take the Nodes from their parents. This actually makes
-        // borrowck *really* happy and everything is pretty smooth. However, this creates
-        // *tons* of pointless writes, and requires us to always walk all the way back to
-        // the root after an insertion, even if we only needed to change a leaf. Therefore,
-        // we accept this potential unsafety and complexity in the name of performance.
-        //
-        // Regardless, the actual dangerous logic is completely abstracted away from BTreeMap
-        // by the stack module. All it can do is immutably read nodes, and ask the search stack
-        // to proceed down some edge by index. This makes the search logic we'll be reusing in a
-        // few different methods much neater, and of course drastically improves safety.
-        let mut stack = stack::PartialSearchStack::new(self);
-
-        loop {
-            let result = stack.with(move |pusher, node| {
-                // Same basic logic as found in `find`, but with PartialSearchStack mediating the
-                // actual nodes for us
-                match Node::search(node, &key) {
-                    Found(mut handle) => {
-                        // Perfect match, swap the values and return the old one
-                        mem::swap(handle.val_mut(), &mut value);
-                        Finished(Some(value))
-                    }
-                    GoDown(handle) => {
-                        // We need to keep searching, try to get the search stack
-                        // to go down further
-                        match handle.force() {
-                            Leaf(leaf_handle) => {
-                                // We've reached a leaf, perform the insertion here
-                                pusher.seal(leaf_handle).insert(key, value);
-                                Finished(None)
-                            }
-                            Internal(internal_handle) => {
-                                // We've found the subtree to insert this key/value pair in,
-                                // keep searching
-                                Continue((pusher.push(internal_handle), key, value))
-                            }
-                        }
-                    }
-                }
-            });
-            match result {
-                Finished(ret) => return ret,
-                Continue((new_stack, renewed_key, renewed_val)) => {
-                    stack = new_stack;
-                    key = renewed_key;
-                    value = renewed_val;
-                }
+    pub fn insert(&mut self, key: K, value: V) -> Option<V> {
+        match self.entry(key) {
+            Occupied(mut entry) => Some(entry.insert(value)),
+            Vacant(entry) => {
+                entry.insert(value);
+                None
             }
         }
     }
 
-    // Deletion is the most complicated operation for a B-Tree.
-    //
-    // First we do the same kind of search described in
-    // `find`. But we need to maintain a stack of all the nodes/edges in our search path.
-    // If we don't find the key, then we just return `None` and do nothing. If we do find the
-    // key, we perform two operations: remove the item, and then possibly handle underflow.
-    //
-    // # removing the item
-    //      If the node is a leaf, we just remove the item, and shift
-    //      any items after it back to fill the hole.
-    //
-    //      If the node is an internal node, we *swap* the item with the smallest item in
-    //      in its right subtree (which must reside in a leaf), and then revert to the leaf
-    //      case
-    //
-    // # handling underflow
-    //      After removing an item, there may be too few items in the node. We want nodes
-    //      to be mostly full for efficiency, although we make an exception for the root, which
-    //      may have as few as one item. If this is the case, we may first try to steal
-    //      an item from our left or right neighbour.
-    //
-    //      To steal from the left (right) neighbour,
-    //      we take the largest (smallest) item and child from it. We then swap the taken item
-    //      with the item in their mutual parent that separates them, and then insert the
-    //      parent's item and the taken child into the first (last) index of the underflowed node.
-    //
-    //      However, stealing has the possibility of underflowing our neighbour. If this is the
-    //      case, we instead *merge* with our neighbour. This of course reduces the number of
-    //      children in the parent. Therefore, we also steal the item that separates the now
-    //      merged nodes, and insert it into the merged node.
-    //
-    //      Merging may cause the parent to underflow. If this is the case, then we must repeat
-    //      the underflow handling process on the parent. If merging merges the last two children
-    //      of the root, then we replace the root with the merged node.
-
     /// Removes a key from the map, returning the value at the key if the key
     /// was previously in the map.
     ///
@@ -461,73 +422,205 @@ 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
-    {
-        // See `swap` for a more thorough description of the stuff going on in here
-        let mut stack = stack::PartialSearchStack::new(self);
-        loop {
-            let result = stack.with(move |pusher, node| {
-                match Node::search(node, key) {
-                    Found(handle) => {
-                        // Perfect match. Terminate the stack here, and remove the entry
-                        Finished(Some(pusher.seal(handle).remove()))
-                    }
-                    GoDown(handle) => {
-                        // We need to keep searching, try to go down the next edge
-                        match handle.force() {
-                            // We're at a leaf; the key isn't in here
-                            Leaf(_) => Finished(None),
-                            Internal(internal_handle) => Continue(pusher.push(internal_handle)),
-                        }
-                    }
-                }
-            });
-            match result {
-                Finished(ret) => return ret.map(|(_, v)| v),
-                Continue(new_stack) => stack = new_stack,
-            }
+    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
         }
     }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<K, V> IntoIterator for BTreeMap<K, V> {
-    type Item = (K, V);
-    type IntoIter = IntoIter<K, V>;
 
-    /// Gets an owning iterator over the entries of the map.
+    /// Constructs a double-ended iterator over a sub-range of elements in the map, starting
+    /// at min, and ending at max. If min is `Unbounded`, then it will be treated as "negative
+    /// infinity", and if max is `Unbounded`, then it will be treated as "positive infinity".
+    /// Thus range(Unbounded, Unbounded) will yield the whole collection.
     ///
     /// # Examples
     ///
     /// ```
+    /// #![feature(btree_range, collections_bound)]
+    ///
     /// use std::collections::BTreeMap;
+    /// use std::collections::Bound::{Included, Unbounded};
     ///
     /// let mut map = BTreeMap::new();
-    /// map.insert(1, "a");
-    /// map.insert(2, "b");
-    /// map.insert(3, "c");
-    ///
-    /// for (key, value) in map.into_iter() {
+    /// map.insert(3, "a");
+    /// map.insert(5, "b");
+    /// map.insert(8, "c");
+    /// for (&key, &value) in map.range(Included(&4), Included(&8)) {
     ///     println!("{}: {}", key, value);
     /// }
+    /// assert_eq!(Some((&5, &"b")), map.range(Included(&4), Unbounded).next());
     /// ```
-    fn into_iter(self) -> IntoIter<K, V> {
-        let len = self.len();
-        let mut lca = VecDeque::new();
-        lca.push_back(Traverse::traverse(self.root));
-        IntoIter {
-            inner: AbsIter {
-                traversals: lca,
-                size: len,
+    #[unstable(feature = "btree_range",
+               reason = "matches collection reform specification, waiting for dust to settle",
+               issue = "27787")]
+    pub fn range<Min: ?Sized + Ord, Max: ?Sized + Ord>(&self,
+                                                       min: Bound<&Min>,
+                                                       max: Bound<&Max>)
+                                                       -> Range<K, V>
+        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())
+        };
+
+        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())
+        };
+
+        Range {
+            front: front,
+            back: back
+        }
+    }
+
+    /// Constructs a mutable double-ended iterator over a sub-range of elements in the map, starting
+    /// at min, and ending at max. If min is `Unbounded`, then it will be treated as "negative
+    /// infinity", and if max is `Unbounded`, then it will be treated as "positive infinity".
+    /// Thus range(Unbounded, Unbounded) will yield the whole collection.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(btree_range, collections_bound)]
+    ///
+    /// use std::collections::BTreeMap;
+    /// use std::collections::Bound::{Included, Excluded};
+    ///
+    /// let mut map: BTreeMap<&str, i32> = ["Alice", "Bob", "Carol", "Cheryl"].iter()
+    ///                                                                       .map(|&s| (s, 0))
+    ///                                                                       .collect();
+    /// for (_, balance) in map.range_mut(Included("B"), Excluded("Cheryl")) {
+    ///     *balance += 100;
+    /// }
+    /// for (name, balance) in &map {
+    ///     println!("{} => {}", name, balance);
+    /// }
+    /// ```
+    #[unstable(feature = "btree_range",
+               reason = "matches collection reform specification, waiting for dust to settle",
+               issue = "27787")]
+    pub fn range_mut<Min: ?Sized + Ord, Max: ?Sized + Ord>(&mut self,
+                                                           min: Bound<&Min>,
+                                                           max: Bound<&Max>)
+                                                           -> RangeMut<K, V>
+        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)
+        };
+
+        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)
+        };
+
+        RangeMut {
+            front: front,
+            back: back,
+            _marker: PhantomData
+        }
+    }
+
+    /// Gets the given key's corresponding entry in the map for in-place manipulation.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::collections::BTreeMap;
+    ///
+    /// let mut count: BTreeMap<&str, usize> = BTreeMap::new();
+    ///
+    /// // count the number of occurrences of letters in the vec
+    /// for x in vec!["a","b","a","c","a","b"] {
+    ///     *count.entry(x).or_insert(0) += 1;
+    /// }
+    ///
+    /// assert_eq!(count["a"], 3);
+    /// ```
+    #[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,
+            })
         }
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> IntoIterator for &'a BTreeMap<K, V> {
+impl<'a, K: 'a, V: 'a> IntoIterator for &'a BTreeMap<K, V> {
     type Item = (&'a K, &'a V);
     type IntoIter = Iter<'a, K, V>;
 
@@ -536,769 +629,570 @@ impl<'a, K, V> IntoIterator for &'a BTreeMap<K, V> {
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> IntoIterator for &'a mut BTreeMap<K, V> {
-    type Item = (&'a K, &'a mut V);
-    type IntoIter = IterMut<'a, K, V>;
+impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> {
+    type Item = (&'a K, &'a V);
 
-    fn into_iter(mut self) -> IterMut<'a, K, V> {
-        self.iter_mut()
+    fn next(&mut self) -> Option<(&'a K, &'a V)> {
+        if self.length == 0 {
+            None
+        } else {
+            self.length -= 1;
+            unsafe { Some(self.range.next_unchecked()) }
+        }
     }
-}
 
-/// A helper enum useful for deciding whether to continue a loop since we can't
-/// return from a closure
-enum Continuation<A, B> {
-    Continue(A),
-    Finished(B),
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.length, Some(self.length))
+    }
 }
 
-/// The stack module provides a safe interface for constructing and manipulating a stack of ptrs
-/// to nodes. By using this module much better safety guarantees can be made, and more search
-/// boilerplate gets cut out.
-mod stack {
-    use core::marker;
-    use core::mem;
-    use core::ops::{Deref, DerefMut};
-    use super::BTreeMap;
-    use super::super::node::{self, Node, Fit, Split, Internal, Leaf};
-    use super::super::node::handle;
-    use vec::Vec;
-
-    struct InvariantLifetime<'id>(marker::PhantomData<::core::cell::Cell<&'id ()>>);
-
-    impl<'id> InvariantLifetime<'id> {
-        fn new() -> InvariantLifetime<'id> {
-            InvariantLifetime(marker::PhantomData)
+impl<'a, K: 'a, V: 'a> DoubleEndedIterator for Iter<'a, K, V> {
+    fn next_back(&mut self) -> Option<(&'a K, &'a V)> {
+        if self.length == 0 {
+            None
+        } else {
+            self.length -= 1;
+            unsafe { Some(self.range.next_back_unchecked()) }
         }
     }
+}
 
-    /// A generic mutable reference, identical to `&mut` except for the fact that its lifetime
-    /// parameter is invariant. This means that wherever an `IdRef` is expected, only an `IdRef`
-    /// with the exact requested lifetime can be used. This is in contrast to normal references,
-    /// where `&'static` can be used in any function expecting any lifetime reference.
-    pub struct IdRef<'id, T: 'id> {
-        inner: &'id mut T,
-        _marker: InvariantLifetime<'id>,
-    }
-
-    impl<'id, T> Deref for IdRef<'id, T> {
-        type Target = T;
+impl<'a, K: 'a, V: 'a> ExactSizeIterator for Iter<'a, K, V> {
+    fn len(&self) -> usize { self.length }
+}
 
-        fn deref(&self) -> &T {
-            &*self.inner
+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
         }
     }
+}
 
-    impl<'id, T> DerefMut for IdRef<'id, T> {
-        fn deref_mut(&mut self) -> &mut T {
-            &mut *self.inner
-        }
+impl<'a, K: 'a, V: 'a> IntoIterator for &'a mut BTreeMap<K, V> {
+    type Item = (&'a K, &'a mut V);
+    type IntoIter = IterMut<'a, K, V>;
+
+    fn into_iter(self) -> IterMut<'a, K, V> {
+        self.iter_mut()
     }
+}
 
-    type StackItem<K, V> = node::Handle<*mut Node<K, V>, handle::Edge, handle::Internal>;
-    type Stack<K, V> = Vec<StackItem<K, V>>;
+impl<'a, K: 'a, V: 'a> Iterator for IterMut<'a, K, V> {
+    type Item = (&'a K, &'a mut V);
 
-    /// A `PartialSearchStack` handles the construction of a search stack.
-    pub struct PartialSearchStack<'a, K: 'a, V: 'a> {
-        map: &'a mut BTreeMap<K, V>,
-        stack: Stack<K, V>,
-        next: *mut Node<K, V>,
+    fn next(&mut self) -> Option<(&'a K, &'a mut V)> {
+        if self.length == 0 {
+            None
+        } else {
+            self.length -= 1;
+            unsafe { Some(self.range.next_unchecked()) }
+        }
     }
 
-    /// A `SearchStack` represents a full path to an element or an edge of interest. It provides
-    /// methods depending on the type of what the path points to for removing an element, inserting
-    /// a new element, and manipulating to element at the top of the stack.
-    pub struct SearchStack<'a, K: 'a, V: 'a, Type, NodeType> {
-        map: &'a mut BTreeMap<K, V>,
-        stack: Stack<K, V>,
-        top: node::Handle<*mut Node<K, V>, Type, NodeType>,
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.length, Some(self.length))
     }
+}
 
-    /// A `PartialSearchStack` that doesn't hold a reference to the next node, and is just
-    /// just waiting for a `Handle` to that next node to be pushed. See `PartialSearchStack::with`
-    /// for more details.
-    pub struct Pusher<'id, 'a, K: 'a, V: 'a> {
-        map: &'a mut BTreeMap<K, V>,
-        stack: Stack<K, V>,
-        _marker: InvariantLifetime<'id>,
+impl<'a, K: 'a, V: 'a> DoubleEndedIterator for IterMut<'a, K, V> {
+    fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> {
+        if self.length == 0 {
+            None
+        } else {
+            self.length -= 1;
+            unsafe { Some(self.range.next_back_unchecked()) }
+        }
     }
+}
 
-    impl<'a, K, V> PartialSearchStack<'a, K, V> {
-        /// Creates a new PartialSearchStack from a BTreeMap by initializing the stack with the
-        /// root of the tree.
-        pub fn new(map: &'a mut BTreeMap<K, V>) -> PartialSearchStack<'a, K, V> {
-            let depth = map.depth;
+impl<'a, K: 'a, V: 'a> ExactSizeIterator for IterMut<'a, K, V> {
+    fn len(&self) -> usize { self.length }
+}
 
-            PartialSearchStack {
-                next: &mut map.root as *mut _,
-                map: map,
-                stack: Vec::with_capacity(depth),
-            }
-        }
+impl<K, V> IntoIterator for BTreeMap<K, V> {
+    type Item = (K, V);
+    type IntoIter = IntoIter<K, V>;
 
-        /// Breaks up the stack into a `Pusher` and the next `Node`, allowing the given closure
-        /// to interact with, search, and finally push the `Node` onto the stack. The passed in
-        /// closure must be polymorphic on the `'id` lifetime parameter, as this statically
-        /// ensures that only `Handle`s from the correct `Node` can be pushed.
-        ///
-        /// The reason this works is that the `Pusher` has an `'id` parameter, and will only accept
-        /// handles with the same `'id`. The closure could only get references with that lifetime
-        /// through its arguments or through some other `IdRef` that it has lying around. However,
-        /// no other `IdRef` could possibly work - because the `'id` is held in an invariant
-        /// parameter, it would need to have precisely the correct lifetime, which would mean that
-        /// at least one of the calls to `with` wouldn't be properly polymorphic, wanting a
-        /// specific lifetime instead of the one that `with` chooses to give it.
-        ///
-        /// See also Haskell's `ST` monad, which uses a similar trick.
-        pub fn with<T, F: for<'id> FnOnce(Pusher<'id, 'a, K, V>,
-                                          IdRef<'id, Node<K, V>>) -> T>(self, closure: F) -> T {
-            let pusher = Pusher {
-                map: self.map,
-                stack: self.stack,
-                _marker: InvariantLifetime::new(),
-            };
-            let node = IdRef {
-                inner: unsafe { &mut *self.next },
-                _marker: InvariantLifetime::new(),
-            };
+    fn into_iter(self) -> IntoIter<K, V> {
+        let root1 = unsafe { ptr::read(&self.root).into_ref() };
+        let root2 = unsafe { ptr::read(&self.root).into_ref() };
+        let len = self.length;
+        mem::forget(self);
 
-            closure(pusher, node)
+        IntoIter {
+            front: first_leaf_edge(root1),
+            back: last_leaf_edge(root2),
+            length: len
         }
     }
+}
 
-    impl<'id, 'a, K, V> Pusher<'id, 'a, K, V> {
-        /// Pushes the requested child of the stack's current top on top of the stack. If the child
-        /// exists, then a new PartialSearchStack is yielded. Otherwise, a VacantSearchStack is
-        /// yielded.
-        pub fn push(mut self,
-                    mut edge: node::Handle<IdRef<'id, Node<K, V>>, handle::Edge, handle::Internal>)
-                    -> PartialSearchStack<'a, K, V> {
-            self.stack.push(edge.as_raw());
-            PartialSearchStack {
-                map: self.map,
-                stack: self.stack,
-                next: edge.edge_mut() as *mut _,
-            }
-        }
-
-        /// Converts the PartialSearchStack into a SearchStack.
-        pub fn seal<Type, NodeType>(self,
-                                    mut handle: node::Handle<IdRef<'id, Node<K, V>>,
-                                                             Type,
-                                                             NodeType>)
-                                    -> SearchStack<'a, K, V, Type, NodeType> {
-            SearchStack {
-                map: self.map,
-                stack: self.stack,
-                top: handle.as_raw(),
+impl<K, V> Drop for IntoIter<K, V> {
+    fn drop(&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() {
+                let mut cur_node = first_parent.into_node();
+                while let Some(parent) = cur_node.deallocate_and_ascend() {
+                    cur_node = parent.into_node()
+                }
             }
         }
     }
+}
 
-    impl<'a, K, V, NodeType> SearchStack<'a, K, V, handle::KV, NodeType> {
-        /// Gets a reference to the value the stack points to.
-        pub fn peek(&self) -> &V {
-            unsafe { self.top.from_raw().into_kv().1 }
-        }
-
-        /// Gets a mutable reference to the value the stack points to.
-        pub fn peek_mut(&mut self) -> &mut V {
-            unsafe { self.top.from_raw_mut().into_kv_mut().1 }
-        }
+impl<K, V> Iterator for IntoIter<K, V> {
+    type Item = (K, V);
 
-        /// Converts the stack into a mutable reference to the value it points to, with a lifetime
-        /// tied to the original tree.
-        pub fn into_top(mut self) -> &'a mut V {
-            unsafe { &mut *(self.top.from_raw_mut().val_mut() as *mut V) }
+    fn next(&mut self) -> Option<(K, V)> {
+        if self.length == 0 {
+            return None;
+        } else {
+            self.length -= 1;
         }
-    }
 
-    impl<'a, K, V> SearchStack<'a, K, V, handle::KV, handle::Leaf> {
-        /// Removes the key and value in the top element of the stack, then handles underflows as
-        /// described in BTree's pop function.
-        fn remove_leaf(mut self) -> (K, V) {
-            self.map.length -= 1;
+        let handle = unsafe { ptr::read(&self.front) };
 
-            // Remove the key-value pair from the leaf that this search stack points to.
-            // Then, note if the leaf is underfull, and promptly forget the leaf and its ptr
-            // to avoid ownership issues.
-            let (key_val, mut underflow) = unsafe {
-                let key_val = self.top.from_raw_mut().remove_as_leaf();
-                let underflow = self.top.from_raw().node().is_underfull();
-                (key_val, underflow)
-            };
+        let mut cur_handle = match handle.right_kv() {
+            Ok(kv) => {
+                let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
+                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 {
-                match self.stack.pop() {
-                    None => {
-                        // We've reached the root, so no matter what, we're done. We manually
-                        // access the root via the tree itself to avoid creating any dangling
-                        // pointers.
-                        if self.map.root.is_empty() && !self.map.root.is_leaf() {
-                            // We've emptied out the root, so make its only child the new root.
-                            // If it's a leaf, we just let it become empty.
-                            self.map.depth -= 1;
-                            self.map.root.hoist_lone_child();
-                        }
-                        return key_val;
-                    }
-                    Some(mut handle) => {
-                        if underflow {
-                            // Underflow! Handle it!
-                            unsafe {
-                                handle.from_raw_mut().handle_underflow();
-                                underflow = handle.from_raw().node().is_underfull();
-                            }
-                        } else {
-                            // All done!
-                            return key_val;
-                        }
-                    }
+        loop {
+            match cur_handle.right_kv() {
+                Ok(kv) => {
+                    let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
+                    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());
                 }
             }
         }
     }
 
-    impl<'a, K, V> SearchStack<'a, K, V, handle::KV, handle::LeafOrInternal> {
-        /// Removes the key and value in the top element of the stack, then handles underflows as
-        /// described in BTree's pop function.
-        pub fn remove(self) -> (K, V) {
-            // Ensure that the search stack goes to a leaf. This is necessary to perform deletion
-            // in a BTree. Note that this may put the tree in an inconsistent state (further
-            // described in into_leaf's comments), but this is immediately fixed by the
-            // removing the value we want to remove
-            self.into_leaf().remove_leaf()
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.length, Some(self.length))
+    }
+}
+
+impl<K, V> DoubleEndedIterator for IntoIter<K, V> {
+    fn next_back(&mut self) -> Option<(K, V)> {
+        if self.length == 0 {
+            return None;
+        } else {
+            self.length -= 1;
         }
 
-        /// Subroutine for removal. Takes a search stack for a key that might terminate at an
-        /// internal node, and mutates the tree and search stack to *make* it a search stack
-        /// for that same key that *does* terminates at a leaf. If the mutation occurs, then this
-        /// leaves the tree in an inconsistent state that must be repaired by the caller by
-        /// removing the entry in question. Specifically the key-value pair and its successor will
-        /// become swapped.
-        fn into_leaf(mut self) -> SearchStack<'a, K, V, handle::KV, handle::Leaf> {
-            unsafe {
-                let mut top_raw = self.top;
-                let mut top = top_raw.from_raw_mut();
-
-                let key_ptr = top.key_mut() as *mut _;
-                let val_ptr = top.val_mut() as *mut _;
-
-                // Try to go into the right subtree of the found key to find its successor
-                match top.force() {
-                    Leaf(mut leaf_handle) => {
-                        // We're a proper leaf stack, nothing to do
-                        return SearchStack {
-                            map: self.map,
-                            stack: self.stack,
-                            top: leaf_handle.as_raw(),
-                        };
-                    }
-                    Internal(mut internal_handle) => {
-                        let mut right_handle = internal_handle.right_edge();
-
-                        // We're not a proper leaf stack, let's get to work.
-                        self.stack.push(right_handle.as_raw());
-
-                        let mut temp_node = right_handle.edge_mut();
-                        loop {
-                            // Walk into the smallest subtree of this node
-                            let node = temp_node;
-
-                            match node.kv_handle(0).force() {
-                                Leaf(mut handle) => {
-                                    // This node is a leaf, do the swap and return
-                                    mem::swap(handle.key_mut(), &mut *key_ptr);
-                                    mem::swap(handle.val_mut(), &mut *val_ptr);
-                                    return SearchStack {
-                                        map: self.map,
-                                        stack: self.stack,
-                                        top: handle.as_raw(),
-                                    };
-                                }
-                                Internal(kv_handle) => {
-                                    // This node is internal, go deeper
-                                    let mut handle = kv_handle.into_left_edge();
-                                    self.stack.push(handle.as_raw());
-                                    temp_node = handle.into_edge_mut();
-                                }
-                            }
-                        }
-                    }
-                }
+        let handle = unsafe { ptr::read(&self.back) };
+
+        let mut cur_handle = match handle.left_kv() {
+            Ok(kv) => {
+                let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
+                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())
             }
-        }
-    }
+        };
 
-    impl<'a, K, V> SearchStack<'a, K, V, handle::Edge, handle::Leaf> {
-        /// Inserts the key and value into the top element in the stack, and if that node has to
-        /// split recursively inserts the split contents into the next element stack until
-        /// splits stop.
-        ///
-        /// Assumes that the stack represents a search path from the root to a leaf.
-        ///
-        /// An &mut V is returned to the inserted value, for callers that want a reference to this.
-        pub fn insert(mut self, key: K, val: V) -> &'a mut V {
-            unsafe {
-                self.map.length += 1;
-
-                // Insert the key and value into the leaf at the top of the stack
-                let (mut insertion, inserted_ptr) = self.top
-                                                        .from_raw_mut()
-                                                        .insert_as_leaf(key, val);
-
-                loop {
-                    match insertion {
-                        Fit => {
-                            // The last insertion went off without a hitch, no splits! We can stop
-                            // inserting now.
-                            return &mut *inserted_ptr;
-                        }
-                        Split(key, val, right) => {
-                            match self.stack.pop() {
-                                // The last insertion triggered a split, so get the next element on
-                                // the stack to recursively insert the split node into.
-                                None => {
-                                    // The stack was empty; we've split the root, and need to make a
-                                    // a new one. This is done in-place because we can't move the
-                                    // root out of a reference to the tree.
-                                    Node::make_internal_root(&mut self.map.root,
-                                                             self.map.b,
-                                                             key,
-                                                             val,
-                                                             right);
-
-                                    self.map.depth += 1;
-                                    return &mut *inserted_ptr;
-                                }
-                                Some(mut handle) => {
-                                    // The stack wasn't empty, do the insertion and recurse
-                                    insertion = handle.from_raw_mut()
-                                                      .insert_as_internal(key, val, right);
-                                    continue;
-                                }
-                            }
-                        }
-                    }
+        loop {
+            match cur_handle.left_kv() {
+                Ok(kv) => {
+                    let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
+                    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());
                 }
             }
         }
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<K: Ord, V> FromIterator<(K, V)> for 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
-    }
+impl<K, V> ExactSizeIterator for IntoIter<K, V> {
+    fn len(&self) -> usize { self.length }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<K: Ord, V> Extend<(K, V)> for BTreeMap<K, V> {
-    #[inline]
-    fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
-        for (k, v) in iter {
-            self.insert(k, v);
-        }
+impl<'a, K, V> Iterator for Keys<'a, K, V> {
+    type Item = &'a K;
+
+    fn next(&mut self) -> Option<&'a K> {
+        self.inner.next().map(|(k, _)| k)
     }
-}
 
-#[stable(feature = "extend_ref", since = "1.2.0")]
-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) {
-        self.extend(iter.into_iter().map(|(&key, &value)| (key, value)));
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.inner.size_hint()
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<K: Hash, V: Hash> Hash for BTreeMap<K, V> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        for elt in self {
-            elt.hash(state);
-        }
+impl<'a, K, V> DoubleEndedIterator for Keys<'a, K, V> {
+    fn next_back(&mut self) -> Option<&'a K> {
+        self.inner.next_back().map(|(k, _)| k)
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<K: Ord, V> Default for BTreeMap<K, V> {
-    fn default() -> BTreeMap<K, V> {
-        BTreeMap::new()
+impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> {
+    fn len(&self) -> usize {
+        self.inner.len()
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-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)
+impl<'a, K, V> Clone for Keys<'a, K, V> {
+    fn clone(&self) -> Keys<'a, K, V> {
+        Keys {
+            inner: self.inner.clone()
+        }
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<K: Eq, V: Eq> Eq for BTreeMap<K, V> {}
+impl<'a, K, V> Iterator for Values<'a, K, V> {
+    type Item = &'a V;
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<K: PartialOrd, V: PartialOrd> PartialOrd for BTreeMap<K, V> {
-    #[inline]
-    fn partial_cmp(&self, other: &BTreeMap<K, V>) -> Option<Ordering> {
-        self.iter().partial_cmp(other.iter())
+    fn next(&mut self) -> Option<&'a V> {
+        self.inner.next().map(|(_, v)| v)
     }
-}
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<K: Ord, V: Ord> Ord for BTreeMap<K, V> {
-    #[inline]
-    fn cmp(&self, other: &BTreeMap<K, V>) -> Ordering {
-        self.iter().cmp(other.iter())
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.inner.size_hint()
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<K: Debug, V: Debug> Debug for BTreeMap<K, V> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        f.debug_map().entries(self.iter()).finish()
+impl<'a, K, V> DoubleEndedIterator for Values<'a, K, V> {
+    fn next_back(&mut self) -> Option<&'a V> {
+        self.inner.next_back().map(|(_, v)| v)
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K: Ord, Q: ?Sized, V> Index<&'a Q> for BTreeMap<K, V>
-    where K: Borrow<Q>,
-          Q: Ord
-{
-    type Output = V;
-
-    #[inline]
-    fn index(&self, key: &Q) -> &V {
-        self.get(key).expect("no entry found for key")
+impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> {
+    fn len(&self) -> usize {
+        self.inner.len()
     }
 }
 
-/// Genericizes over how to get the correct type of iterator from the correct type
-/// of Node ownership.
-trait Traverse<N> {
-    fn traverse(node: N) -> Self;
-}
-
-impl<'a, K, V> Traverse<&'a Node<K, V>> for Traversal<'a, K, V> {
-    fn traverse(node: &'a Node<K, V>) -> Traversal<'a, K, V> {
-        node.iter()
+impl<'a, K, V> Clone for Values<'a, K, V> {
+    fn clone(&self) -> Values<'a, K, V> {
+        Values {
+            inner: self.inner.clone()
+        }
     }
 }
 
-impl<'a, K, V> Traverse<&'a mut Node<K, V>> for MutTraversal<'a, K, V> {
-    fn traverse(node: &'a mut Node<K, V>) -> MutTraversal<'a, K, V> {
-        node.iter_mut()
-    }
-}
+impl<'a, K, V> Iterator for Range<'a, K, V> {
+    type Item = (&'a K, &'a V);
 
-impl<K, V> Traverse<Node<K, V>> for MoveTraversal<K, V> {
-    fn traverse(node: Node<K, V>) -> MoveTraversal<K, V> {
-        node.into_iter()
+    fn next(&mut self) -> Option<(&'a K, &'a V)> {
+        if self.front == self.back {
+            None
+        } else {
+            unsafe { Some(self.next_unchecked()) }
+        }
     }
 }
 
-/// Represents an operation to perform inside the following iterator methods.
-/// This is necessary to use in `next` because we want to modify `self.traversals` inside
-/// a match that borrows it. Similarly in `next_back`. Instead, we use this enum to note
-/// what we want to do, and do it after the match.
-enum StackOp<T> {
-    Push(T),
-    Pop,
-}
-impl<K, V, E, T> Iterator for AbsIter<T>
-    where T: DoubleEndedIterator<Item = TraversalItem<K, V, E>> + Traverse<E>
-{
-    type Item = (K, V);
+impl<'a, K, V> Range<'a, K, V> {
+    unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) {
+        let handle = self.front;
 
-    // Our iterator represents a queue of all ancestors of elements we have
-    // yet to yield, from smallest to largest.  Note that the design of these
-    // iterators permits an *arbitrary* initial pair of min and max, making
-    // these arbitrary sub-range iterators.
-    fn next(&mut self) -> Option<(K, V)> {
-        loop {
-            // We want the smallest element, so try to get the back of the queue
-            let op = match self.traversals.back_mut() {
-                None => return None,
-                // The queue wasn't empty, so continue along the node in its head
-                Some(iter) => {
-                    match iter.next() {
-                        // The head is empty, so Pop it off and continue the process
-                        None => Pop,
-                        // The head yielded an edge, so make that the new head
-                        Some(Edge(next)) => Push(Traverse::traverse(next)),
-                        // The head yielded an entry, so yield that
-                        Some(Elem(kv)) => {
-                            self.size -= 1;
-                            return Some(kv);
-                        }
-                    }
-                }
-            };
+        let mut cur_handle = match handle.right_kv() {
+            Ok(kv) => {
+                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)
+            }
+        };
 
-            // Handle any operation as necessary, without a conflicting borrow of the queue
-            match op {
-                Push(item) => {
-                    self.traversals.push_back(item);
-                }
-                Pop => {
-                    self.traversals.pop_back();
+        loop {
+            match cur_handle.right_kv() {
+                Ok(kv) => {
+                    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);
                 }
             }
         }
     }
+}
 
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (self.size, Some(self.size))
+impl<'a, K, V> DoubleEndedIterator for Range<'a, K, V> {
+    fn next_back(&mut self) -> Option<(&'a K, &'a V)> {
+        if self.front == self.back {
+            None
+        } else {
+            unsafe { Some(self.next_back_unchecked()) }
+        }
     }
 }
 
-impl<K, V, E, T> DoubleEndedIterator for AbsIter<T>
-    where T: DoubleEndedIterator<Item = TraversalItem<K, V, E>> + Traverse<E>
-{
-    // next_back is totally symmetric to next
-    #[inline]
-    fn next_back(&mut self) -> Option<(K, V)> {
-        loop {
-            let op = match self.traversals.front_mut() {
-                None => return None,
-                Some(iter) => {
-                    match iter.next_back() {
-                        None => Pop,
-                        Some(Edge(next)) => Push(Traverse::traverse(next)),
-                        Some(Elem(kv)) => {
-                            self.size -= 1;
-                            return Some(kv);
-                        }
-                    }
-                }
-            };
+impl<'a, K, V> Range<'a, K, V> {
+    unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) {
+        let handle = self.back;
 
-            match op {
-                Push(item) => {
-                    self.traversals.push_front(item);
-                }
-                Pop => {
-                    self.traversals.pop_front();
+        let mut cur_handle = match handle.left_kv() {
+            Ok(kv) => {
+                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)
+            }
+        };
+
+        loop {
+            match cur_handle.left_kv() {
+                Ok(kv) => {
+                    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);
                 }
             }
         }
     }
 }
 
-impl<'a, K, V> Clone for Iter<'a, K, V> {
-    fn clone(&self) -> Iter<'a, K, V> {
-        Iter { inner: self.inner.clone() }
-    }
-}
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> Iterator for Iter<'a, K, V> {
-    type Item = (&'a K, &'a V);
-
-    fn next(&mut self) -> Option<(&'a K, &'a V)> {
-        self.inner.next()
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.inner.size_hint()
-    }
-}
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V> {
-    fn next_back(&mut self) -> Option<(&'a K, &'a V)> {
-        self.inner.next_back()
+impl<'a, K, V> Clone for Range<'a, K, V> {
+    fn clone(&self) -> Range<'a, K, V> {
+        Range {
+            front: self.front,
+            back: self.back
+        }
     }
 }
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> {}
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> Iterator for IterMut<'a, K, V> {
+impl<'a, K, V> Iterator for RangeMut<'a, K, V> {
     type Item = (&'a K, &'a mut V);
 
     fn next(&mut self) -> Option<(&'a K, &'a mut V)> {
-        self.inner.next()
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.inner.size_hint()
-    }
-}
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> DoubleEndedIterator for IterMut<'a, K, V> {
-    fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> {
-        self.inner.next_back()
+        if self.front == self.back {
+            None
+        } else {
+            unsafe { Some (self.next_unchecked()) }
+        }
     }
 }
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> {}
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<K, V> Iterator for IntoIter<K, V> {
-    type Item = (K, V);
+impl<'a, K, V> RangeMut<'a, K, V> {
+    unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) {
+        let handle = ptr::read(&self.front);
 
-    fn next(&mut self) -> Option<(K, V)> {
-        self.inner.next()
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.inner.size_hint()
-    }
-}
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<K, V> DoubleEndedIterator for IntoIter<K, V> {
-    fn next_back(&mut self) -> Option<(K, V)> {
-        self.inner.next_back()
-    }
-}
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<K, V> ExactSizeIterator for IntoIter<K, V> {}
+        let mut cur_handle = match handle.right_kv() {
+            Ok(kv) => {
+                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)
+            }
+        };
 
-impl<'a, K, V> Clone for Keys<'a, K, V> {
-    fn clone(&self) -> Keys<'a, K, V> {
-        Keys { inner: self.inner.clone() }
+        loop {
+            match cur_handle.right_kv() {
+                Ok(kv) => {
+                    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);
+                }
+            }
+        }
     }
 }
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> Iterator for Keys<'a, K, V> {
-    type Item = &'a K;
 
-    fn next(&mut self) -> Option<(&'a K)> {
-        self.inner.next()
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.inner.size_hint()
-    }
-}
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> DoubleEndedIterator for Keys<'a, K, V> {
-    fn next_back(&mut self) -> Option<(&'a K)> {
-        self.inner.next_back()
+impl<'a, K, V> DoubleEndedIterator for RangeMut<'a, K, V> {
+    fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> {
+        if self.front == self.back {
+            None
+        } else {
+            unsafe { Some(self.next_back_unchecked()) }
+        }
     }
 }
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> {}
 
+impl<'a, K, V> RangeMut<'a, K, V> {
+    unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) {
+        let handle = ptr::read(&self.back);
 
-impl<'a, K, V> Clone for Values<'a, K, V> {
-    fn clone(&self) -> Values<'a, K, V> {
-        Values { inner: self.inner.clone() }
-    }
-}
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> Iterator for Values<'a, K, V> {
-    type Item = &'a V;
+        let mut cur_handle = match handle.left_kv() {
+            Ok(kv) => {
+                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)
+            }
+        };
 
-    fn next(&mut self) -> Option<(&'a V)> {
-        self.inner.next()
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.inner.size_hint()
+        loop {
+            match cur_handle.left_kv() {
+                Ok(kv) => {
+                    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);
+                }
+            }
+        }
     }
 }
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> DoubleEndedIterator for Values<'a, K, V> {
-    fn next_back(&mut self) -> Option<(&'a V)> {
-        self.inner.next_back()
+
+impl<K: Ord, V> FromIterator<(K, V)> for 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
     }
 }
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> {}
 
-impl<'a, K, V> Clone for Range<'a, K, V> {
-    fn clone(&self) -> Range<'a, K, V> {
-        Range { inner: self.inner.clone() }
+impl<K: Ord, V> Extend<(K, V)> for BTreeMap<K, V> {
+    #[inline]
+    fn extend<T: IntoIterator<Item=(K, V)>>(&mut self, iter: T) {
+        for (k, v) in iter {
+            self.insert(k, v);
+        }
     }
 }
-impl<'a, K, V> Iterator for Range<'a, K, V> {
-    type Item = (&'a K, &'a V);
 
-    fn next(&mut self) -> Option<(&'a K, &'a V)> {
-        self.inner.next()
+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) {
+        self.extend(iter.into_iter().map(|(&key, &value)| (key, value)));
     }
 }
-impl<'a, K, V> DoubleEndedIterator for Range<'a, K, V> {
-    fn next_back(&mut self) -> Option<(&'a K, &'a V)> {
-        self.inner.next_back()
+
+impl<K: Hash, V: Hash> Hash for BTreeMap<K, V> {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        for elt in self {
+            elt.hash(state);
+        }
     }
 }
 
-impl<'a, K, V> Iterator for RangeMut<'a, K, V> {
-    type Item = (&'a K, &'a mut V);
-
-    fn next(&mut self) -> Option<(&'a K, &'a mut V)> {
-        self.inner.next()
+impl<K: Ord, V> Default for BTreeMap<K, V> {
+    fn default() -> BTreeMap<K, V> {
+        BTreeMap::new()
     }
 }
-impl<'a, K, V> DoubleEndedIterator for RangeMut<'a, K, V> {
-    fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> {
-        self.inner.next_back()
+
+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)
     }
 }
 
-impl<'a, K: Ord, V> Entry<'a, K, V> {
-    #[stable(feature = "rust1", since = "1.0.0")]
-    /// Ensures a value is in the entry by inserting the default if empty, and returns
-    /// a mutable reference to the value in the entry.
-    pub fn or_insert(self, default: V) -> &'a mut V {
-        match self {
-            Occupied(entry) => entry.into_mut(),
-            Vacant(entry) => entry.insert(default),
-        }
-    }
+impl<K: Eq, V: Eq> Eq for BTreeMap<K, V> {}
 
-    #[stable(feature = "rust1", since = "1.0.0")]
-    /// Ensures a value is in the entry by inserting the result of the default function if empty,
-    /// and returns a mutable reference to the value in the entry.
-    pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
-        match self {
-            Occupied(entry) => entry.into_mut(),
-            Vacant(entry) => entry.insert(default()),
-        }
+impl<K: PartialOrd, V: PartialOrd> PartialOrd for BTreeMap<K, V> {
+    #[inline]
+    fn partial_cmp(&self, other: &BTreeMap<K, V>) -> Option<Ordering> {
+        self.iter().partial_cmp(other.iter())
     }
 }
 
-impl<'a, K: Ord, V> VacantEntry<'a, K, V> {
-    /// 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")]
-    pub fn insert(self, value: V) -> &'a mut V {
-        self.stack.insert(self.key, value)
+impl<K: Ord, V: Ord> Ord for BTreeMap<K, V> {
+    #[inline]
+    fn cmp(&self, other: &BTreeMap<K, V>) -> Ordering {
+        self.iter().cmp(other.iter())
     }
 }
 
-impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> {
-    /// Gets a reference to the value in the entry.
-    #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn get(&self) -> &V {
-        self.stack.peek()
+impl<K: Debug, V: Debug> Debug for BTreeMap<K, V> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_map().entries(self.iter()).finish()
     }
+}
 
-    /// Gets a mutable reference to the value in the entry.
-    #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn get_mut(&mut self) -> &mut V {
-        self.stack.peek_mut()
-    }
+impl<'a, K: Ord, Q: ?Sized, V> Index<&'a Q> for BTreeMap<K, V>
+    where K: Borrow<Q>, Q: Ord
+{
+    type Output = V;
 
-    /// Converts the entry into a mutable reference to its value.
-    #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn into_mut(self) -> &'a mut V {
-        self.stack.into_top()
+    #[inline]
+    fn index(&self, key: &Q) -> &V {
+        self.get(key).expect("no entry found for key")
     }
+}
 
-    /// Sets the value of the entry with the OccupiedEntry's key,
-    /// and returns the entry's old value.
-    #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn insert(&mut self, mut value: V) -> V {
-        mem::swap(self.stack.peek_mut(), &mut value);
-        value
+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(),
+            Internal(internal) => {
+                node = internal.first_edge().descend();
+            }
+        }
     }
+}
 
-    /// Takes the value of the entry out of the map, and returns it.
-    #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn remove(self) -> V {
-        self.stack.remove().1
+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(),
+            Internal(internal) => {
+                node = internal.last_edge().descend();
+            }
+        }
     }
 }
 
+#[inline(always)]
+unsafe fn unwrap_unchecked<T>(val: Option<T>) -> T {
+    val.unwrap_or_else(|| {
+        if cfg!(debug_assertions) {
+            panic!("'unchecked' unwrap on None in BTreeMap");
+        } else {
+            intrinsics::unreachable();
+        }
+    })
+}
+
 impl<K, V> BTreeMap<K, V> {
     /// Gets an iterator over the entries of the map.
     ///
@@ -1321,15 +1215,12 @@ impl<K, V> BTreeMap<K, V> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn iter(&self) -> Iter<K, V> {
-        let len = self.len();
-        // NB. The initial capacity for ringbuf is large enough to avoid reallocs in many cases.
-        let mut lca = VecDeque::new();
-        lca.push_back(Traverse::traverse(&self.root));
         Iter {
-            inner: AbsIter {
-                traversals: lca,
-                size: len,
+            range: Range {
+                front: first_leaf_edge(self.root.as_ref()),
+                back: last_leaf_edge(self.root.as_ref())
             },
+            length: self.length
         }
     }
 
@@ -1354,14 +1245,15 @@ impl<K, V> BTreeMap<K, V> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn iter_mut(&mut self) -> IterMut<K, V> {
-        let len = self.len();
-        let mut lca = VecDeque::new();
-        lca.push_back(Traverse::traverse(&mut self.root));
+        let root1 = self.root.as_mut();
+        let root2 = unsafe { ptr::read(&root1) };
         IterMut {
-            inner: AbsIter {
-                traversals: lca,
-                size: len,
+            range: RangeMut {
+                front: first_leaf_edge(root1),
+                back: last_leaf_edge(root2),
+                _marker: PhantomData,
             },
+            length: self.length
         }
     }
 
@@ -1381,12 +1273,7 @@ impl<K, V> BTreeMap<K, V> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn keys<'a>(&'a self) -> Keys<'a, K, V> {
-        fn first<A, B>((a, _): (A, B)) -> A {
-            a
-        }
-        let first: fn((&'a K, &'a V)) -> &'a K = first; // coerce to fn pointer
-
-        Keys { inner: self.iter().map(first) }
+        Keys { inner: self.iter() }
     }
 
     /// Gets an iterator over the values of the map.
@@ -1405,12 +1292,7 @@ impl<K, V> BTreeMap<K, V> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn values<'a>(&'a self) -> Values<'a, K, V> {
-        fn second<A, B>((_, b): (A, B)) -> B {
-            b
-        }
-        let second: fn((&'a K, &'a V)) -> &'a V = second; // coerce to fn pointer
-
-        Values { inner: self.iter().map(second) }
+        Values { inner: self.iter() }
     }
 
     /// Returns the number of elements in the map.
@@ -1448,357 +1330,207 @@ impl<K, V> BTreeMap<K, V> {
     }
 }
 
-macro_rules! range_impl {
-    ($root:expr, $min:expr, $max:expr, $as_slices_internal:ident, $iter:ident, $Range:ident,
-                                       $edges:ident, [$($mutability:ident)*]) => (
-        {
-            // A deque that encodes two search paths containing (left-to-right):
-            // a series of truncated-from-the-left iterators, the LCA's doubly-truncated iterator,
-            // and a series of truncated-from-the-right iterators.
-            let mut traversals = VecDeque::new();
-            let (root, min, max) = ($root, $min, $max);
-
-            let mut leftmost = None;
-            let mut rightmost = None;
+impl<'a, K: Ord, V> Entry<'a, K, V> {
+    /// Ensures a value is in the entry by inserting the default if empty, and returns
+    /// a mutable reference to the value in the entry.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub fn or_insert(self, default: V) -> &'a mut V {
+        match self {
+            Occupied(entry) => entry.into_mut(),
+            Vacant(entry) => entry.insert(default),
+        }
+    }
 
-            match (&min, &max) {
-                (&Unbounded, &Unbounded) => {
-                    traversals.push_back(Traverse::traverse(root))
-                }
-                (&Unbounded, &Included(_)) | (&Unbounded, &Excluded(_)) => {
-                    rightmost = Some(root);
-                }
-                (&Included(_), &Unbounded) | (&Excluded(_), &Unbounded) => {
-                    leftmost = Some(root);
-                }
-                  (&Included(min_key), &Included(max_key))
-                | (&Included(min_key), &Excluded(max_key))
-                | (&Excluded(min_key), &Included(max_key))
-                | (&Excluded(min_key), &Excluded(max_key)) => {
-                    // lca represents the Lowest Common Ancestor, above which we never
-                    // walk, since everything else is outside the range to iterate.
-                    //       ___________________
-                    //      |__0_|_80_|_85_|_90_|  (root)
-                    //      |    |    |    |    |
-                    //           |
-                    //           v
-                    //  ___________________
-                    // |__5_|_15_|_30_|_73_|
-                    // |    |    |    |    |
-                    //                |
-                    //                v
-                    //       ___________________
-                    //      |_33_|_58_|_63_|_68_|  lca for the range [41, 65]
-                    //      |    |\___|___/|    |  iterator at traversals[2]
-                    //           |         |
-                    //           |         v
-                    //           v         rightmost
-                    //           leftmost
-                    let mut is_leaf = root.is_leaf();
-                    let mut lca = root.$as_slices_internal();
-                    loop {
-                        let slice = lca.slice_from(min_key).slice_to(max_key);
-                        if let [ref $($mutability)* edge] = slice.edges {
-                            // Follow the only edge that leads the node that covers the range.
-                            is_leaf = edge.is_leaf();
-                            lca = edge.$as_slices_internal();
-                        } else {
-                            let mut iter = slice.$iter();
-                            if is_leaf {
-                                leftmost = None;
-                                rightmost = None;
-                            } else {
-                                // Only change the state of nodes with edges.
-                                leftmost = iter.next_edge_item();
-                                rightmost = iter.next_edge_item_back();
-                            }
-                            traversals.push_back(iter);
-                            break;
-                        }
-                    }
-                }
-            }
-            // Keep narrowing the range by going down.
-            //               ___________________
-            //              |_38_|_43_|_48_|_53_|
-            //              |    |____|____|____/ iterator at traversals[1]
-            //                   |
-            //                   v
-            //  ___________________
-            // |_39_|_40_|_41_|_42_|  (leaf, the last leftmost)
-            //           \_________|  iterator at traversals[0]
-            match min {
-                Included(key) | Excluded(key) =>
-                    while let Some(left) = leftmost {
-                        let is_leaf = left.is_leaf();
-                        let mut iter = left.$as_slices_internal().slice_from(key).$iter();
-                        leftmost = if is_leaf {
-                            None
-                        } else {
-                            // Only change the state of nodes with edges.
-                            iter.next_edge_item()
-                        };
-                        traversals.push_back(iter);
-                    },
-                _ => {}
-            }
-            // If the leftmost iterator starts with an element, then it was an exact match.
-            if let (Excluded(_), Some(leftmost_iter)) = (min, traversals.back_mut()) {
-                // Drop this excluded element. `next_kv_item` has no effect when
-                // the next item is an edge.
-                leftmost_iter.next_kv_item();
-            }
+    /// Ensures a value is in the entry by inserting the result of the default function if empty,
+    /// and returns a mutable reference to the value in the entry.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
+        match self {
+            Occupied(entry) => entry.into_mut(),
+            Vacant(entry) => entry.insert(default()),
+        }
+    }
+}
 
-            // The code for the right side is similar.
-            match max {
-                Included(key) | Excluded(key) =>
-                    while let Some(right) = rightmost {
-                        let is_leaf = right.is_leaf();
-                        let mut iter = right.$as_slices_internal().slice_to(key).$iter();
-                        rightmost = if is_leaf {
-                            None
-                        } else {
-                            iter.next_edge_item_back()
-                        };
-                        traversals.push_front(iter);
-                    },
-                _ => {}
-            }
-            if let (Excluded(_), Some(rightmost_iter)) = (max, traversals.front_mut()) {
-                rightmost_iter.next_kv_item_back();
+impl<'a, K: Ord, V> VacantEntry<'a, K, V> {
+    /// 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")]
+    pub fn insert(self, value: V) -> &'a mut V {
+        *self.length += 1;
+
+        let out_ptr;
+
+        let mut ins_k;
+        let mut ins_v;
+        let mut ins_edge;
+
+        let mut cur_parent = match self.handle.insert(self.key, value) {
+            (Fit(handle), _) => return handle.into_kv_mut().1,
+            (Split(left, k, v, right), ptr) => {
+                ins_k = k;
+                ins_v = v;
+                ins_edge = right;
+                out_ptr = ptr;
+                left.ascend().map_err(|n| n.into_root_mut())
             }
+        };
 
-            $Range {
-                inner: AbsIter {
-                    traversals: traversals,
-                    size: usize::MAX, // unused
+        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());
+                    }
+                },
+                Err(root) => {
+                    root.push_level().push(ins_k, ins_v, ins_edge);
+                    return unsafe { &mut *out_ptr };
                 }
             }
         }
-    )
+    }
 }
 
-impl<K: Ord, V> BTreeMap<K, V> {
-    /// Constructs a double-ended iterator over a sub-range of elements in the map, starting
-    /// at min, and ending at max. If min is `Unbounded`, then it will be treated as "negative
-    /// infinity", and if max is `Unbounded`, then it will be treated as "positive infinity".
-    /// Thus range(Unbounded, Unbounded) will yield the whole collection.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(btree_range, collections_bound)]
-    ///
-    /// use std::collections::BTreeMap;
-    /// use std::collections::Bound::{Included, Unbounded};
-    ///
-    /// let mut map = BTreeMap::new();
-    /// map.insert(3, "a");
-    /// map.insert(5, "b");
-    /// map.insert(8, "c");
-    /// for (&key, &value) in map.range(Included(&4), Included(&8)) {
-    ///     println!("{}: {}", key, value);
-    /// }
-    /// assert_eq!(Some((&5, &"b")), map.range(Included(&4), Unbounded).next());
-    /// ```
-    #[unstable(feature = "btree_range",
-               reason = "matches collection reform specification, waiting for dust to settle",
-               issue = "27787")]
-    pub fn range<Min: ?Sized + Ord = K, Max: ?Sized + Ord = K>(&self,
-                                                               min: Bound<&Min>,
-                                                               max: Bound<&Max>)
-                                                               -> Range<K, V>
-        where K: Borrow<Min> + Borrow<Max>
-    {
-        range_impl!(&self.root,
-                    min,
-                    max,
-                    as_slices_internal,
-                    iter,
-                    Range,
-                    edges,
-                    [])
+impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> {
+    /// Gets a reference to the value in the entry.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub fn get(&self) -> &V {
+        self.handle.reborrow().into_kv().1
     }
 
-    /// Constructs a mutable double-ended iterator over a sub-range of elements in the map, starting
-    /// at min, and ending at max. If min is `Unbounded`, then it will be treated as "negative
-    /// infinity", and if max is `Unbounded`, then it will be treated as "positive infinity".
-    /// Thus range(Unbounded, Unbounded) will yield the whole collection.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(btree_range, collections_bound)]
-    ///
-    /// use std::collections::BTreeMap;
-    /// use std::collections::Bound::{Included, Excluded};
-    ///
-    /// let mut map: BTreeMap<&str, i32> = ["Alice", "Bob", "Carol", "Cheryl"].iter()
-    ///                                                                       .map(|&s| (s, 0))
-    ///                                                                       .collect();
-    /// for (_, balance) in map.range_mut(Included("B"), Excluded("Cheryl")) {
-    ///     *balance += 100;
-    /// }
-    /// for (name, balance) in &map {
-    ///     println!("{} => {}", name, balance);
-    /// }
-    /// ```
-    #[unstable(feature = "btree_range",
-               reason = "matches collection reform specification, waiting for dust to settle",
-               issue = "27787")]
-    pub fn range_mut<Min: ?Sized + Ord = K, Max: ?Sized + Ord = K>(&mut self,
-                                                                   min: Bound<&Min>,
-                                                                   max: Bound<&Max>)
-                                                                   -> RangeMut<K, V>
-        where K: Borrow<Min> + Borrow<Max>
-    {
-        range_impl!(&mut self.root,
-                    min,
-                    max,
-                    as_slices_internal_mut,
-                    iter_mut,
-                    RangeMut,
-                    edges_mut,
-                    [mut])
+    /// Gets a mutable reference to the value in the entry.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub fn get_mut(&mut self) -> &mut V {
+        self.handle.kv_mut().1
     }
 
-    /// Gets the given key's corresponding entry in the map for in-place manipulation.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::collections::BTreeMap;
-    ///
-    /// let mut count: BTreeMap<&str, usize> = BTreeMap::new();
-    ///
-    /// // count the number of occurrences of letters in the vec
-    /// for x in vec!["a","b","a","c","a","b"] {
-    ///     *count.entry(x).or_insert(0) += 1;
-    /// }
-    ///
-    /// assert_eq!(count["a"], 3);
-    /// ```
+    /// Converts the entry into a mutable reference to its value.
     #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn entry(&mut self, mut key: K) -> Entry<K, V> {
-        // same basic logic of `swap` and `pop`, blended together
-        let mut stack = stack::PartialSearchStack::new(self);
-        loop {
-            let result = stack.with(move |pusher, node| {
-                match Node::search(node, &key) {
-                    Found(handle) => {
-                        // Perfect match
-                        Finished(Occupied(OccupiedEntry { stack: pusher.seal(handle) }))
-                    }
-                    GoDown(handle) => {
-                        match handle.force() {
-                            Leaf(leaf_handle) => {
-                                Finished(Vacant(VacantEntry {
-                                    stack: pusher.seal(leaf_handle),
-                                    key: key,
-                                }))
-                            }
-                            Internal(internal_handle) => {
-                                Continue((pusher.push(internal_handle), key))
-                            }
-                        }
-                    }
-                }
-            });
-            match result {
-                Finished(finished) => return finished,
-                Continue((new_stack, renewed_key)) => {
-                    stack = new_stack;
-                    key = renewed_key;
-                }
-            }
-        }
+    pub fn into_mut(self) -> &'a mut V {
+        self.handle.into_kv_mut().1
     }
-}
 
-impl<K, Q: ?Sized> super::Recover<Q> for BTreeMap<K, ()>
-    where K: Borrow<Q> + Ord,
-          Q: Ord
-{
-    type Key = K;
+    /// Sets the value of the entry with the OccupiedEntry's key,
+    /// and returns the entry's old value.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub fn insert(&mut self, value: V) -> V {
+        mem::replace(self.get_mut(), value)
+    }
 
-    fn get(&self, key: &Q) -> Option<&K> {
-        let mut cur_node = &self.root;
-        loop {
-            match Node::search(cur_node, key) {
-                Found(handle) => return Some(handle.into_kv().0),
-                GoDown(handle) => {
-                    match handle.force() {
-                        Leaf(_) => return None,
-                        Internal(internal_handle) => {
-                            cur_node = internal_handle.into_edge();
-                            continue;
-                        }
-                    }
-                }
-            }
-        }
+    /// Takes the value of the entry out of the map, and returns it.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub fn remove(self) -> V {
+        self.remove_kv().1
     }
 
-    fn take(&mut self, key: &Q) -> Option<K> {
-        // See `remove` for an explanation of this.
+    fn remove_kv(self) -> (K, V) {
+        *self.length -= 1;
 
-        let mut stack = stack::PartialSearchStack::new(self);
-        loop {
-            let result = stack.with(move |pusher, node| {
-                match Node::search(node, key) {
-                    Found(handle) => {
-                        // Perfect match. Terminate the stack here, and remove the entry
-                        Finished(Some(pusher.seal(handle).remove()))
-                    }
-                    GoDown(handle) => {
-                        // We need to keep searching, try to go down the next edge
-                        match handle.force() {
-                            // We're at a leaf; the key isn't in here
-                            Leaf(_) => Finished(None),
-                            Internal(internal_handle) => Continue(pusher.push(internal_handle)),
-                        }
-                    }
-                }
-            });
-            match result {
-                Finished(ret) => return ret.map(|(k, _)| k),
-                Continue(new_stack) => stack = new_stack,
+        let (small_leaf, old_key, old_val) = match self.handle.force() {
+            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;
+
+                let to_remove = first_leaf_edge(internal.right_edge().descend()).right_kv().ok();
+                let to_remove = unsafe { unwrap_unchecked(to_remove) };
+
+                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)
+                };
+
+                (hole.into_node(), old_key, old_val)
+            }
+        };
+
+        // Handle underflow
+        let mut cur_node = small_leaf.forget_type();
+        while cur_node.len() < node::CAPACITY / 2 {
+            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
             }
         }
+
+        (old_key, old_val)
     }
+}
 
-    fn replace(&mut self, mut key: K) -> Option<K> {
-        // See `insert` for an explanation of this.
+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>)
+}
+
+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 {
+        return AtRoot;
+    };
+
+    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());
+            }
+        }
+    };
+
+    if handle.can_merge() {
+        return Merged(handle.merge().into_node());
+    } else {
+        unsafe {
+            let (k, v, edge) = if is_left {
+                handle.reborrow_mut().left_edge().descend().pop()
+            } else {
+                handle.reborrow_mut().right_edge().descend().pop_front()
+            };
 
-        let mut stack = stack::PartialSearchStack::new(self);
+            let k = mem::replace(handle.reborrow_mut().into_kv_mut().0, k);
+            let v = mem::replace(handle.reborrow_mut().into_kv_mut().1, v);
 
-        loop {
-            let result = stack.with(move |pusher, node| {
-                match Node::search::<K, _>(node, &key) {
-                    Found(mut handle) => {
-                        mem::swap(handle.key_mut(), &mut key);
-                        Finished(Some(key))
-                    }
-                    GoDown(handle) => {
-                        match handle.force() {
-                            Leaf(leaf_handle) => {
-                                pusher.seal(leaf_handle).insert(key, ());
-                                Finished(None)
-                            }
-                            Internal(internal_handle) => {
-                                Continue((pusher.push(internal_handle), key, ()))
-                            }
-                        }
-                    }
+            // FIXME: reuse cur_node?
+            if is_left {
+                match handle.reborrow_mut().right_edge().descend().force() {
+                    Leaf(mut leaf) => leaf.push_front(k, v),
+                    Internal(mut internal) => internal.push_front(k, v, edge.unwrap())
                 }
-            });
-            match result {
-                Finished(ret) => return ret,
-                Continue((new_stack, renewed_key, _)) => {
-                    stack = new_stack;
-                    key = renewed_key;
+            } else {
+                match handle.reborrow_mut().left_edge().descend().force() {
+                    Leaf(mut leaf) => leaf.push(k, v),
+                    Internal(mut internal) => internal.push(k, v, edge.unwrap())
                 }
             }
         }
+
+        return Stole(handle.into_node());
     }
 }
index d424cb7e29d2b87f775948882953602a66fcb74e..087c9f228d4448bb6c119675ce28eac9e354a6b4 100644 (file)
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 mod node;
+mod search;
 pub mod map;
 pub mod set;
 
index 26479b3f559b94a9f94733c0edeb36fb0734b279..c8a0f60587e9eaa5a89df18e2888b854016d52e6 100644 (file)
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// This module represents all the internal representation and logic for a B-Tree's node
-// with a safe interface, so that BTreeMap itself does not depend on any of these details.
-
-pub use self::InsertionResult::*;
-pub use self::SearchResult::*;
-pub use self::ForceResult::*;
-pub use self::TraversalItem::*;
+// This is an attempt at an implementation following the ideal
+//
+// ```
+// struct BTreeMap<K, V> {
+//     height: usize,
+//     root: Option<Box<Node<K, V, height>>>
+// }
+//
+// struct Node<K, V, height: usize> {
+//     keys: [K; 2 * B - 1],
+//     vals: [V; 2 * B - 1],
+//     edges: if height > 0 {
+//         [Box<Node<K, V, height - 1>>; 2 * B]
+//     } else { () },
+//     parent: *const Node<K, V, height + 1>,
+//     parent_idx: u16,
+//     len: u16,
+// }
+// ```
+//
+// Since Rust doesn't acutally have dependent types and polymorphic recursion,
+// we make do with lots of unsafety.
 
-use core::cmp::Ordering::{Greater, Less, Equal};
-use core::intrinsics::arith_offset;
-use core::iter::Zip;
+use alloc::heap;
 use core::marker::PhantomData;
-use core::ops::{Deref, DerefMut, Index, IndexMut};
-use core::ptr::Unique;
-use core::{slice, mem, ptr, cmp};
-use alloc::heap::{self, EMPTY};
-
-use borrow::Borrow;
-
-/// Represents the result of an Insertion: either the item fit, or the node had to split
-pub enum InsertionResult<K, V> {
-    /// The inserted element fit
-    Fit,
-    /// The inserted element did not fit, so the node was split
-    Split(K, V, Node<K, V>),
-}
-
-/// Represents the result of a search for a key in a single node
-pub enum SearchResult<NodeRef> {
-    /// The element was found at the given index
-    Found(Handle<NodeRef, handle::KV, handle::LeafOrInternal>),
-    /// The element wasn't found, but if it's anywhere, it must be beyond this edge
-    GoDown(Handle<NodeRef, handle::Edge, handle::LeafOrInternal>),
-}
-
-/// A B-Tree Node. We keep keys/edges/values separate to optimize searching for keys.
-#[unsafe_no_drop_flag]
-pub struct Node<K, V> {
-    // To avoid the need for multiple allocations, we allocate a single buffer with enough space
-    // for `capacity` keys, `capacity` values, and (in internal nodes) `capacity + 1` edges.
-    // Despite this, we store three separate pointers to the three "chunks" of the buffer because
-    // the performance drops significantly if the locations of the vals and edges need to be
-    // recalculated upon access.
-    //
-    // These will never be null during normal usage of a `Node`. However, to avoid the need for a
-    // drop flag, `Node::drop` zeroes `keys`, signaling that the `Node` has already been cleaned
-    // up.
-    keys: Unique<K>,
-    vals: Unique<V>,
-
-    // In leaf nodes, this will be None, and no space will be allocated for edges.
-    edges: Option<Unique<Node<K, V>>>,
-
-    // At any given time, there will be `_len` keys, `_len` values, and (in an internal node)
-    // `_len + 1` edges. In a leaf node, there will never be any edges.
-    //
-    // Note: instead of accessing this field directly, please call the `len()` method, which should
-    // be more stable in the face of representation changes.
-    _len: usize,
-
-    // FIXME(gereeter) It shouldn't be necessary to store the capacity in every node, as it should
-    // be constant throughout the tree. Once a solution to this is found, it might be possible to
-    // also pass down the offsets into the buffer that vals and edges are stored at, removing the
-    // need for those two pointers.
-    //
-    // Note: instead of accessing this field directly, please call the `capacity()` method, which
-    // should be more stable in the face of representation changes.
-    _capacity: usize,
-}
-
-struct NodeSlice<'a, K: 'a, V: 'a> {
-    keys: &'a [K],
-    vals: &'a [V],
-    pub edges: &'a [Node<K, V>],
-    head_is_edge: bool,
-    tail_is_edge: bool,
-    has_edges: bool,
-}
-
-struct MutNodeSlice<'a, K: 'a, V: 'a> {
-    keys: &'a [K],
-    vals: &'a mut [V],
-    pub edges: &'a mut [Node<K, V>],
-    head_is_edge: bool,
-    tail_is_edge: bool,
-    has_edges: bool,
-}
-
-/// Rounds up to a multiple of a power of two. Returns the closest multiple
-/// of `target_alignment` that is higher or equal to `unrounded`.
-///
-/// # Panics
-///
-/// Fails if `target_alignment` is not a power of two.
-#[inline]
-fn round_up_to_next(unrounded: usize, target_alignment: usize) -> usize {
-    assert!(target_alignment.is_power_of_two());
-    (unrounded + target_alignment - 1) & !(target_alignment - 1)
-}
-
-#[test]
-fn test_rounding() {
-    assert_eq!(round_up_to_next(0, 4), 0);
-    assert_eq!(round_up_to_next(1, 4), 4);
-    assert_eq!(round_up_to_next(2, 4), 4);
-    assert_eq!(round_up_to_next(3, 4), 4);
-    assert_eq!(round_up_to_next(4, 4), 4);
-    assert_eq!(round_up_to_next(5, 4), 8);
-}
-
-// Returns a tuple of (val_offset, edge_offset),
-// from the start of a mallocated array.
-#[inline]
-fn calculate_offsets(keys_size: usize,
-                     vals_size: usize,
-                     vals_align: usize,
-                     edges_align: usize)
-                     -> (usize, usize) {
-    let vals_offset = round_up_to_next(keys_size, vals_align);
-    let end_of_vals = vals_offset + vals_size;
-
-    let edges_offset = round_up_to_next(end_of_vals, edges_align);
-
-    (vals_offset, edges_offset)
-}
-
-// Returns a tuple of (minimum required alignment, array_size),
-// from the start of a mallocated array.
-#[inline]
-fn calculate_allocation(keys_size: usize,
-                        keys_align: usize,
-                        vals_size: usize,
-                        vals_align: usize,
-                        edges_size: usize,
-                        edges_align: usize)
-                        -> (usize, usize) {
-    let (_, edges_offset) = calculate_offsets(keys_size, vals_size, vals_align, edges_align);
-    let end_of_edges = edges_offset + edges_size;
-
-    let min_align = cmp::max(keys_align, cmp::max(vals_align, edges_align));
-
-    (min_align, end_of_edges)
+use core::mem;
+use core::nonzero::NonZero;
+use core::ptr::{self, Unique};
+use core::slice;
+
+use boxed::Box;
+
+const B: usize = 6;
+pub const CAPACITY: usize = 2 * B - 1;
+
+struct LeafNode<K, V> {
+    keys: [K; CAPACITY],
+    vals: [V; CAPACITY],
+    parent: *const InternalNode<K, V>,
+    parent_idx: u16,
+    len: u16,
 }
 
-#[test]
-fn test_offset_calculation() {
-    assert_eq!(calculate_allocation(128, 8, 15, 1, 4, 4), (8, 148));
-    assert_eq!(calculate_allocation(3, 1, 2, 1, 1, 1), (1, 6));
-    assert_eq!(calculate_allocation(6, 2, 12, 4, 24, 8), (8, 48));
-    assert_eq!(calculate_offsets(128, 15, 1, 4), (128, 144));
-    assert_eq!(calculate_offsets(3, 2, 1, 1), (3, 5));
-    assert_eq!(calculate_offsets(6, 12, 4, 8), (8, 24));
-}
-
-fn calculate_allocation_generic<K, V>(capacity: usize, is_leaf: bool) -> (usize, usize) {
-    let (keys_size, keys_align) = (capacity * mem::size_of::<K>(), mem::align_of::<K>());
-    let (vals_size, vals_align) = (capacity * mem::size_of::<V>(), mem::align_of::<V>());
-    let (edges_size, edges_align) = if is_leaf {
-        // allocate one edge to ensure that we don't pass size 0 to `heap::allocate`
-        if mem::size_of::<K>() == 0 && mem::size_of::<V>() == 0 {
-            (1, mem::align_of::<Node<K, V>>())
-        } else {
-            (0, 1)
+impl<K, V> LeafNode<K, V> {
+    unsafe fn new() -> Self {
+        LeafNode {
+            keys: mem::uninitialized(),
+            vals: mem::uninitialized(),
+            parent: ptr::null(),
+            parent_idx: mem::uninitialized(),
+            len: 0
         }
-    } else {
-        ((capacity + 1) * mem::size_of::<Node<K, V>>(),
-         mem::align_of::<Node<K, V>>())
-    };
-
-    calculate_allocation(keys_size,
-                         keys_align,
-                         vals_size,
-                         vals_align,
-                         edges_size,
-                         edges_align)
-}
-
-fn calculate_offsets_generic<K, V>(capacity: usize, is_leaf: bool) -> (usize, usize) {
-    let keys_size = capacity * mem::size_of::<K>();
-    let vals_size = capacity * mem::size_of::<V>();
-    let vals_align = mem::align_of::<V>();
-    let edges_align = if is_leaf {
-        1
-    } else {
-        mem::align_of::<Node<K, V>>()
-    };
-
-    calculate_offsets(keys_size, vals_size, vals_align, edges_align)
+    }
 }
 
-/// An iterator over a slice that owns the elements of the slice but not the allocation.
-struct RawItems<T> {
-    head: *const T,
-    tail: *const T,
+// We use repr(C) so that a pointer to an internal node can be
+// directly used as a pointer to a leaf node
+#[repr(C)]
+struct InternalNode<K, V> {
+    data: LeafNode<K, V>,
+    edges: [BoxedNode<K, V>; 2 * B],
 }
 
-impl<T> RawItems<T> {
-    unsafe fn from_slice(slice: &[T]) -> RawItems<T> {
-        RawItems::from_parts(slice.as_ptr(), slice.len())
-    }
-
-    unsafe fn from_parts(ptr: *const T, len: usize) -> RawItems<T> {
-        if mem::size_of::<T>() == 0 {
-            RawItems {
-                head: ptr,
-                tail: arith_offset(ptr as *const i8, len as isize) as *const T,
-            }
-        } else {
-            RawItems {
-                head: ptr,
-                tail: ptr.offset(len as isize),
-            }
-        }
-    }
-
-    unsafe fn push(&mut self, val: T) {
-        ptr::write(self.tail as *mut T, val);
-
-        if mem::size_of::<T>() == 0 {
-            self.tail = arith_offset(self.tail as *const i8, 1) as *const T;
-        } else {
-            self.tail = self.tail.offset(1);
+impl<K, V> InternalNode<K, V> {
+    unsafe fn new() -> Self {
+        InternalNode {
+            data: LeafNode::new(),
+            edges: mem::uninitialized()
         }
     }
 }
 
-impl<T> Iterator for RawItems<T> {
-    type Item = T;
-
-    fn next(&mut self) -> Option<T> {
-        if self.head == self.tail {
-            None
-        } else {
-            unsafe {
-                let ret = Some(ptr::read(self.head));
-
-                if mem::size_of::<T>() == 0 {
-                    self.head = arith_offset(self.head as *const i8, 1) as *const T;
-                } else {
-                    self.head = self.head.offset(1);
-                }
+struct BoxedNode<K, V> {
+    ptr: Unique<LeafNode<K, V>> // we don't know if this points to a leaf node or an internal node
+}
 
-                ret
-            }
+impl<K, V> BoxedNode<K, V> {
+    fn from_leaf(node: Box<LeafNode<K, V>>) -> Self {
+        unsafe {
+            BoxedNode { ptr: Unique::new(Box::into_raw(node)) }
         }
     }
-}
-
-impl<T> DoubleEndedIterator for RawItems<T> {
-    fn next_back(&mut self) -> Option<T> {
-        if self.head == self.tail {
-            None
-        } else {
-            unsafe {
-                if mem::size_of::<T>() == 0 {
-                    self.tail = arith_offset(self.tail as *const i8, -1) as *const T;
-                } else {
-                    self.tail = self.tail.offset(-1);
-                }
 
-                Some(ptr::read(self.tail))
-            }
+    fn from_internal(node: Box<InternalNode<K, V>>) -> Self {
+        unsafe {
+            BoxedNode { ptr: Unique::new(Box::into_raw(node) as *mut LeafNode<K, V>) }
         }
     }
-}
 
-impl<T> Drop for RawItems<T> {
-    #[unsafe_destructor_blind_to_params]
-    fn drop(&mut self) {
-        for _ in self {}
+    unsafe fn from_ptr(ptr: NonZero<*const LeafNode<K, V>>) -> Self {
+        BoxedNode { ptr: Unique::new(*ptr as *mut LeafNode<K, V>) }
     }
-}
-
-impl<K, V> Drop for Node<K, V> {
-    #[unsafe_destructor_blind_to_params]
-    fn drop(&mut self) {
-        if self.keys.is_null() ||
-           (unsafe { self.keys.get() as *const K as usize == mem::POST_DROP_USIZE }) {
-            // Since we have #[unsafe_no_drop_flag], we have to watch
-            // out for the sentinel value being stored in self.keys. (Using
-            // null is technically a violation of the `Unique`
-            // requirements, though.)
-            return;
-        }
 
-        // Do the actual cleanup.
+    fn as_ptr(&self) -> NonZero<*const LeafNode<K, V>> {
         unsafe {
-            drop(RawItems::from_slice(self.keys()));
-            drop(RawItems::from_slice(self.vals()));
-            drop(RawItems::from_slice(self.edges()));
-
-            self.destroy();
+            NonZero::new(*self.ptr as *const LeafNode<K, V>)
         }
-
-        self.keys = unsafe { Unique::new(ptr::null_mut()) };
     }
 }
 
-impl<K, V> Node<K, V> {
-    /// Make a new internal node. The caller must initialize the result to fix the invariant that
-    /// there are `len() + 1` edges.
-    unsafe fn new_internal(capacity: usize) -> Node<K, V> {
-        let (alignment, size) = calculate_allocation_generic::<K, V>(capacity, false);
-
-        let buffer = heap::allocate(size, alignment);
-        if buffer.is_null() {
-            ::alloc::oom();
-        }
-
-        let (vals_offset, edges_offset) = calculate_offsets_generic::<K, V>(capacity, false);
-
-        Node {
-            keys: Unique::new(buffer as *mut K),
-            vals: Unique::new(buffer.offset(vals_offset as isize) as *mut V),
-            edges: Some(Unique::new(buffer.offset(edges_offset as isize) as *mut Node<K, V>)),
-            _len: 0,
-            _capacity: capacity,
-        }
-    }
-
-    /// Make a new leaf node
-    fn new_leaf(capacity: usize) -> Node<K, V> {
-        let (alignment, size) = calculate_allocation_generic::<K, V>(capacity, true);
-
-        let buffer = unsafe { heap::allocate(size, alignment) };
-        if buffer.is_null() {
-            ::alloc::oom();
-        }
+/// An owned tree. Note that despite being owned, this does not have a destructor,
+/// and must be cleaned up manually.
+pub struct Root<K, V> {
+    node: BoxedNode<K, V>,
+    height: usize
+}
 
-        let (vals_offset, _) = calculate_offsets_generic::<K, V>(capacity, true);
+unsafe impl<K: Sync, V: Sync> Sync for Root<K, V> { }
+unsafe impl<K: Send, V: Send> Send for Root<K, V> { }
 
-        Node {
-            keys: unsafe { Unique::new(buffer as *mut K) },
-            vals: unsafe { Unique::new(buffer.offset(vals_offset as isize) as *mut V) },
-            edges: None,
-            _len: 0,
-            _capacity: capacity,
+impl<K, V> Root<K, V> {
+    pub fn new_leaf() -> Self {
+        Root {
+            node: BoxedNode::from_leaf(Box::new(unsafe { LeafNode::new() })),
+            height: 0
         }
     }
 
-    unsafe fn destroy(&mut self) {
-        let (alignment, size) = calculate_allocation_generic::<K, V>(self.capacity(),
-                                                                     self.is_leaf());
-        heap::deallocate(*self.keys as *mut u8, size, alignment);
-    }
-
-    #[inline]
-    pub fn as_slices<'a>(&'a self) -> (&'a [K], &'a [V]) {
-        unsafe {
-            (slice::from_raw_parts(*self.keys, self.len()),
-             slice::from_raw_parts(*self.vals, self.len()))
+    pub fn as_ref(&self)
+            -> NodeRef<marker::Immut, K, V, marker::LeafOrInternal> {
+        NodeRef {
+            height: self.height,
+            node: self.node.as_ptr(),
+            root: self as *const _ as *mut _,
+            _marker: PhantomData,
         }
     }
 
-    #[inline]
-    pub fn as_slices_mut<'a>(&'a mut self) -> (&'a mut [K], &'a mut [V]) {
-        unsafe {
-            (slice::from_raw_parts_mut(*self.keys, self.len()),
-             slice::from_raw_parts_mut(*self.vals, self.len()))
+    pub fn as_mut(&mut self)
+            -> NodeRef<marker::Mut, K, V, marker::LeafOrInternal> {
+        NodeRef {
+            height: self.height,
+            node: self.node.as_ptr(),
+            root: self as *mut _,
+            _marker: PhantomData,
         }
     }
 
-    #[inline]
-    pub fn as_slices_internal<'b>(&'b self) -> NodeSlice<'b, K, V> {
-        let is_leaf = self.is_leaf();
-        let (keys, vals) = self.as_slices();
-        let edges: &[_] = if self.is_leaf() {
-            &[]
-        } else {
-            unsafe {
-                let data = match self.edges {
-                    None => heap::EMPTY as *const Node<K, V>,
-                    Some(ref p) => **p as *const Node<K, V>,
-                };
-                slice::from_raw_parts(data, self.len() + 1)
-            }
-        };
-        NodeSlice {
-            keys: keys,
-            vals: vals,
-            edges: edges,
-            head_is_edge: true,
-            tail_is_edge: true,
-            has_edges: !is_leaf,
+    pub fn into_ref(self)
+            -> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
+        NodeRef {
+            height: self.height,
+            node: self.node.as_ptr(),
+            root: ptr::null_mut(), // FIXME: Is there anything better to do here?
+            _marker: PhantomData,
         }
     }
 
-    #[inline]
-    pub fn as_slices_internal_mut<'b>(&'b mut self) -> MutNodeSlice<'b, K, V> {
-        let len = self.len();
-        let is_leaf = self.is_leaf();
-        let keys = unsafe { slice::from_raw_parts_mut(*self.keys, len) };
-        let vals = unsafe { slice::from_raw_parts_mut(*self.vals, len) };
-        let edges: &mut [_] = if is_leaf {
-            &mut []
-        } else {
-            unsafe {
-                let data = match self.edges {
-                    None => heap::EMPTY as *mut Node<K, V>,
-                    Some(ref mut p) => **p as *mut Node<K, V>,
-                };
-                slice::from_raw_parts_mut(data, len + 1)
-            }
-        };
-        MutNodeSlice {
-            keys: keys,
-            vals: vals,
-            edges: edges,
-            head_is_edge: true,
-            tail_is_edge: true,
-            has_edges: !is_leaf,
-        }
-    }
+    /// Add a new internal node with a single edge, pointing to the previous root, and make that
+    /// new node the root. This increases the height by 1 and is the opposite of `pop_level`.
+    pub fn push_level(&mut self)
+            -> NodeRef<marker::Mut, K, V, marker::Internal> {
+        let mut new_node = Box::new(unsafe { InternalNode::new() });
+        new_node.edges[0] = unsafe { BoxedNode::from_ptr(self.node.as_ptr()) };
 
-    #[inline]
-    pub fn keys<'a>(&'a self) -> &'a [K] {
-        self.as_slices().0
-    }
+        self.node = BoxedNode::from_internal(new_node);
+        self.height += 1;
 
-    #[inline]
-    pub fn keys_mut<'a>(&'a mut self) -> &'a mut [K] {
-        self.as_slices_mut().0
-    }
+        let mut ret = NodeRef {
+            height: self.height,
+            node: self.node.as_ptr(),
+            root: self as *mut _,
+            _marker: PhantomData
+        };
 
-    #[inline]
-    pub fn vals<'a>(&'a self) -> &'a [V] {
-        self.as_slices().1
-    }
+        unsafe {
+            ret.reborrow_mut().first_edge().correct_parent_link();
+        }
 
-    #[inline]
-    pub fn vals_mut<'a>(&'a mut self) -> &'a mut [V] {
-        self.as_slices_mut().1
+        ret
     }
 
-    #[inline]
-    pub fn edges<'a>(&'a self) -> &'a [Node<K, V>] {
-        self.as_slices_internal().edges
-    }
+    /// Remove the root node, using its first child as the new root. This cannot be called when
+    /// the tree consists only of a leaf node. As it is intended only to be called when the root
+    /// has only one edge, no cleanup is done on any of the other children are elements of the root.
+    /// This decreases the height by 1 and is the opposite of `push_level`.
+    pub fn pop_level(&mut self) {
+        debug_assert!(self.height > 0);
 
-    #[inline]
-    pub fn edges_mut<'a>(&'a mut self) -> &'a mut [Node<K, V>] {
-        self.as_slices_internal_mut().edges
-    }
-}
+        let top = *self.node.ptr as *mut u8;
 
-// FIXME(gereeter) Write an efficient clone_from
-impl<K: Clone, V: Clone> Clone for Node<K, V> {
-    fn clone(&self) -> Node<K, V> {
-        let mut ret = if self.is_leaf() {
-            Node::new_leaf(self.capacity())
-        } else {
-            unsafe { Node::new_internal(self.capacity()) }
+        self.node = unsafe {
+            BoxedNode::from_ptr(self.as_mut()
+                                    .cast_unchecked::<marker::Internal>()
+                                    .first_edge()
+                                    .descend()
+                                    .node)
         };
+        self.height -= 1;
+        self.as_mut().as_leaf_mut().parent = ptr::null();
 
         unsafe {
-            // For failure safety
-            let mut keys = RawItems::from_parts(ret.keys().as_ptr(), 0);
-            let mut vals = RawItems::from_parts(ret.vals().as_ptr(), 0);
-            let mut edges = RawItems::from_parts(ret.edges().as_ptr(), 0);
-
-            for key in self.keys() {
-                keys.push(key.clone())
-            }
-            for val in self.vals() {
-                vals.push(val.clone())
-            }
-            for edge in self.edges() {
-                edges.push(edge.clone())
-            }
-
-            mem::forget(keys);
-            mem::forget(vals);
-            mem::forget(edges);
-
-            ret._len = self.len();
+            heap::deallocate(
+                top,
+                mem::size_of::<InternalNode<K, V>>(),
+                mem::align_of::<InternalNode<K, V>>()
+            );
         }
-
-        ret
     }
 }
 
-/// A reference to something in the middle of a `Node`. There are two `Type`s of `Handle`s,
-/// namely `KV` handles, which point to key/value pairs, and `Edge` handles, which point to edges
-/// before or after key/value pairs. Methods are provided for removing pairs, inserting into edges,
-/// accessing the stored values, and moving around the `Node`.
-///
-/// This handle is generic, and can take any sort of reference to a `Node`. The reason for this is
-/// two-fold. First of all, it reduces the amount of repetitive code, implementing functions that
-/// don't need mutability on both mutable and immutable references. Secondly and more importantly,
-/// this allows users of the `Handle` API to associate metadata with the reference. This is used in
-/// `BTreeMap` to give `Node`s temporary "IDs" that persist to when the `Node` is used in a
-/// `Handle`.
-///
-/// # A note on safety
-///
-/// Unfortunately, the extra power afforded by being generic also means that safety can technically
-/// be broken. For sensible implementations of `Deref` and `DerefMut`, these handles are perfectly
-/// safe. As long as repeatedly calling `.deref()` results in the same Node being returned each
-/// time, everything should work fine. However, if the `Deref` implementation swaps in multiple
-/// different nodes, then the indices that are assumed to be in bounds suddenly stop being so. For
-/// example:
-///
-/// ```rust,ignore
-/// struct Nasty<'a> {
-///     first: &'a Node<usize, usize>,
-///     second: &'a Node<usize, usize>,
-///     flag: &'a Cell<bool>,
-/// }
+// N.B. `NodeRef` is always covariant in `K` and `V`, even when the `BorrowType`
+// is `Mut`. This is technically wrong, but cannot result in any unsafety due to
+// internal use of `NodeRef` because we stay completely generic over `K` and `V`.
+// However, whenever a public type wraps `NodeRef`, make sure that it has the
+// correct variance.
+/// A reference to a node.
 ///
-/// impl<'a> Deref for Nasty<'a> {
-///     type Target = Node<usize, usize>;
-///
-///     fn deref(&self) -> &Node<usize, usize> {
-///         if self.flag.get() {
-///             &*self.second
-///         } else {
-///             &*self.first
-///         }
-///     }
-/// }
-///
-/// fn main() {
-///     let flag = Cell::new(false);
-///     let mut small_node = Node::make_leaf_root(3);
-///     let mut large_node = Node::make_leaf_root(100);
-///
-///     for i in 0..100 {
-///         // Insert to the end
-///         large_node.edge_handle(i).insert_as_leaf(i, i);
-///     }
-///
-///     let nasty = Nasty {
-///         first: &large_node,
-///         second: &small_node,
-///         flag: &flag
-///     }
-///
-///     // The handle points at index 75.
-///     let handle = Node::search(nasty, 75);
-///
-///     // Now the handle still points at index 75, but on the small node, which has no index 75.
-///     flag.set(true);
-///
-///     println!("Uninitialized memory: {:?}", handle.into_kv());
-/// }
-/// ```
-#[derive(Copy, Clone)]
-pub struct Handle<NodeRef, Type, NodeType> {
-    node: NodeRef,
-    index: usize,
-    marker: PhantomData<(Type, NodeType)>,
+/// This type has a number of paramaters that controls how it acts:
+/// - `BorrowType`: This can be `Immut<'a>` or `Mut<'a>` for some `'a` or `Owned`.
+///    When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`,
+///    when this is `Mut<'a>`, the `NodeRef` acts roughly like `&'a mut Node`,
+///    and when this is `Owned`, the `NodeRef` acts roughly like `Box<Node>`.
+/// - `K` and `V`: These control what types of things are stored in the nodes.
+/// - `Type`: This can be `Leaf`, `Internal`, or `LeafOrInternal`. When this is
+///   `Leaf`, the `NodeRef` points to a leaf node, when this is `Internal` the
+///   `NodeRef` points to an internal node, and when this is `LeafOrInternal` the
+///   `NodeRef` could be pointing to either type of node.
+pub struct NodeRef<BorrowType, K, V, Type> {
+    height: usize,
+    node: NonZero<*const LeafNode<K, V>>,
+    root: *const Root<K, V>,
+    _marker: PhantomData<(BorrowType, Type)>
 }
 
-pub mod handle {
-    // Handle types.
-    pub enum KV {}
-    pub enum Edge {}
-
-    // Handle node types.
-    pub enum LeafOrInternal {}
-    pub enum Leaf {}
-    pub enum Internal {}
+impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef<marker::Immut<'a>, K, V, Type> { }
+impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef<marker::Immut<'a>, K, V, Type> {
+    fn clone(&self) -> Self {
+        *self
+    }
 }
 
-impl<K: Ord, V> Node<K, V> {
-    /// Searches for the given key in the node. If it finds an exact match,
-    /// `Found` will be yielded with the matching index. If it doesn't find an exact match,
-    /// `GoDown` will be yielded with the index of the subtree the key must lie in.
-    pub fn search<Q: ?Sized, NodeRef: Deref<Target = Node<K, V>>>(node: NodeRef,
-                                                                  key: &Q)
-                                                                  -> SearchResult<NodeRef>
-        where K: Borrow<Q>,
-              Q: Ord
-    {
-        // FIXME(Gankro): Tune when to search linear or binary based on B (and maybe K/V).
-        // For the B configured as of this writing (B = 6), binary search was *significantly*
-        // worse for usizes.
-        match node.as_slices_internal().search_linear(key) {
-            (index, true) => {
-                Found(Handle {
-                    node: node,
-                    index: index,
-                    marker: PhantomData,
-                })
-            }
-            (index, false) => {
-                GoDown(Handle {
-                    node: node,
-                    index: index,
-                    marker: PhantomData,
-                })
-            }
+unsafe impl<BorrowType, K: Sync, V: Sync, Type> Sync
+    for NodeRef<BorrowType, K, V, Type> { }
+
+unsafe impl<'a, K: Sync + 'a, V: Sync + 'a, Type> Send
+   for NodeRef<marker::Immut<'a>, K, V, Type> { }
+unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send
+   for NodeRef<marker::Mut<'a>, K, V, Type> { }
+unsafe impl<K: Send, V: Send, Type> Send
+   for NodeRef<marker::Owned, K, V, Type> { }
+
+impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
+    fn as_internal(&self) -> &InternalNode<K, V> {
+        unsafe {
+            &*(*self.node as *const InternalNode<K, V>)
         }
     }
 }
 
-// Public interface
-impl<K, V> Node<K, V> {
-    /// Make a leaf root from scratch
-    pub fn make_leaf_root(b: usize) -> Node<K, V> {
-        Node::new_leaf(capacity_from_b(b))
-    }
-
-    /// Make an internal root and swap it with an old root
-    pub fn make_internal_root(left_and_out: &mut Node<K, V>,
-                              b: usize,
-                              key: K,
-                              value: V,
-                              right: Node<K, V>) {
-        let node = mem::replace(left_and_out,
-                                unsafe { Node::new_internal(capacity_from_b(b)) });
-        left_and_out._len = 1;
+impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
+    fn as_internal_mut(&mut self) -> &mut InternalNode<K, V> {
         unsafe {
-            ptr::write(left_and_out.keys_mut().get_unchecked_mut(0), key);
-            ptr::write(left_and_out.vals_mut().get_unchecked_mut(0), value);
-            ptr::write(left_and_out.edges_mut().get_unchecked_mut(0), node);
-            ptr::write(left_and_out.edges_mut().get_unchecked_mut(1), right);
+            &mut *(*self.node as *mut InternalNode<K, V>)
         }
     }
+}
+
 
-    /// How many key-value pairs the node contains
+impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
     pub fn len(&self) -> usize {
-        self._len
+        self.as_leaf().len as usize
     }
 
-    /// Does the node not contain any key-value pairs
-    pub fn is_empty(&self) -> bool {
-        self.len() == 0
+    pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
+        NodeRef {
+            height: self.height,
+            node: self.node,
+            root: self.root,
+            _marker: PhantomData
+        }
     }
 
-    /// How many key-value pairs the node can fit
-    pub fn capacity(&self) -> usize {
-        self._capacity
+    fn reborrow<'a>(&'a self) -> NodeRef<marker::Immut<'a>, K, V, Type> {
+        NodeRef {
+            height: self.height,
+            node: self.node,
+            root: self.root,
+            _marker: PhantomData
+        }
     }
 
-    /// If the node has any children
-    pub fn is_leaf(&self) -> bool {
-        self.edges.is_none()
+    fn as_leaf(&self) -> &LeafNode<K, V> {
+        unsafe {
+            &**self.node
+        }
     }
 
-    /// if the node has too few elements
-    pub fn is_underfull(&self) -> bool {
-        self.len() < min_load_from_capacity(self.capacity())
+    pub fn keys(&self) -> &[K] {
+        self.reborrow().into_slices().0
     }
 
-    /// if the node cannot fit any more elements
-    pub fn is_full(&self) -> bool {
-        self.len() == self.capacity()
+    pub fn vals(&self) -> &[V] {
+        self.reborrow().into_slices().1
     }
-}
 
-impl<K, V, NodeRef: Deref<Target = Node<K, V>>, Type, NodeType> Handle<NodeRef, Type, NodeType> {
-    /// Returns a reference to the node that contains the pointed-to edge or key/value pair. This
-    /// is very different from `edge` and `edge_mut` because those return children of the node
-    /// returned by `node`.
-    pub fn node(&self) -> &Node<K, V> {
-        &*self.node
-    }
-}
-
-impl<K, V, NodeRef, Type, NodeType> Handle<NodeRef, Type, NodeType>
-    where NodeRef: Deref<Target = Node<K, V>> + DerefMut
-{
-    /// Converts a handle into one that stores the same information using a raw pointer. This can
-    /// be useful in conjunction with `from_raw` when the type system is insufficient for
-    /// determining the lifetimes of the nodes.
-    pub fn as_raw(&mut self) -> Handle<*mut Node<K, V>, Type, NodeType> {
-        Handle {
-            node: &mut *self.node as *mut _,
-            index: self.index,
-            marker: PhantomData,
+    pub fn ascend(self) -> Result<
+        Handle<
+            NodeRef<
+                BorrowType,
+                K, V,
+                marker::Internal
+            >,
+            marker::Edge
+        >,
+        Self
+    > {
+        if self.as_leaf().parent.is_null() {
+            Err(self)
+        } else {
+            Ok(Handle {
+                node: NodeRef {
+                    height: self.height + 1,
+                    node: unsafe {
+                        NonZero::new(self.as_leaf().parent as *mut LeafNode<K, V>)
+                    },
+                    root: self.root,
+                    _marker: PhantomData
+                },
+                idx: self.as_leaf().parent_idx as usize,
+                _marker: PhantomData
+            })
         }
     }
-}
 
-impl<K, V, Type, NodeType> Handle<*mut Node<K, V>, Type, NodeType> {
-    /// Converts from a handle stored with a raw pointer, which isn't directly usable, to a handle
-    /// stored with a reference. This is an unsafe inverse of `as_raw`, and together they allow
-    /// unsafely extending the lifetime of the reference to the `Node`.
-    pub unsafe fn from_raw<'a>(&'a self) -> Handle<&'a Node<K, V>, Type, NodeType> {
-        Handle {
-            node: &*self.node,
-            index: self.index,
-            marker: PhantomData,
-        }
+    pub fn first_edge(self) -> Handle<Self, marker::Edge> {
+        Handle::new_edge(self, 0)
     }
 
-    /// Converts from a handle stored with a raw pointer, which isn't directly usable, to a handle
-    /// stored with a mutable reference. This is an unsafe inverse of `as_raw`, and together they
-    /// allow unsafely extending the lifetime of the reference to the `Node`.
-    pub unsafe fn from_raw_mut<'a>(&'a mut self) -> Handle<&'a mut Node<K, V>, Type, NodeType> {
-        Handle {
-            node: &mut *self.node,
-            index: self.index,
-            marker: PhantomData,
-        }
+    pub fn last_edge(self) -> Handle<Self, marker::Edge> {
+        let len = self.len();
+        Handle::new_edge(self, len)
     }
 }
 
-impl<'a, K: 'a, V: 'a> Handle<&'a Node<K, V>, handle::Edge, handle::Internal> {
-    /// Turns the handle into a reference to the edge it points at. This is necessary because the
-    /// returned pointer has a larger lifetime than what would be returned by `edge` or `edge_mut`,
-    /// making it more suitable for moving down a chain of nodes.
-    pub fn into_edge(self) -> &'a Node<K, V> {
-        unsafe { self.node.edges().get_unchecked(self.index) }
+impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> {
+    pub unsafe fn deallocate_and_ascend(self) -> Option<
+        Handle<
+            NodeRef<
+                marker::Owned,
+                K, V,
+                marker::Internal
+            >,
+            marker::Edge
+        >
+    > {
+        let ptr = self.as_leaf() as *const LeafNode<K, V> as *const u8 as *mut u8;
+        let ret = self.ascend().ok();
+        heap::deallocate(ptr, mem::size_of::<LeafNode<K, V>>(), mem::align_of::<LeafNode<K, V>>());
+        ret
     }
 }
 
-impl<'a, K: 'a, V: 'a> Handle<&'a mut Node<K, V>, handle::Edge, handle::Internal> {
-    /// Turns the handle into a mutable reference to the edge it points at. This is necessary
-    /// because the returned pointer has a larger lifetime than what would be returned by
-    /// `edge_mut`, making it more suitable for moving down a chain of nodes.
-    pub fn into_edge_mut(self) -> &'a mut Node<K, V> {
-        unsafe { self.node.edges_mut().get_unchecked_mut(self.index) }
+impl<K, V> NodeRef<marker::Owned, K, V, marker::Internal> {
+    pub unsafe fn deallocate_and_ascend(self) -> Option<
+        Handle<
+            NodeRef<
+                marker::Owned,
+                K, V,
+                marker::Internal
+            >,
+            marker::Edge
+        >
+    > {
+        let ptr = self.as_internal() as *const InternalNode<K, V> as *const u8 as *mut u8;
+        let ret = self.ascend().ok();
+        heap::deallocate(
+            ptr,
+            mem::size_of::<InternalNode<K, V>>(),
+            mem::align_of::<InternalNode<K, V>>()
+        );
+        ret
     }
 }
 
-impl<K, V, NodeRef: Deref<Target = Node<K, V>>> Handle<NodeRef, handle::Edge, handle::Internal> {
-    // This doesn't exist because there are no uses for it,
-    // but is fine to add, analogous to edge_mut.
-    //
-    // /// Returns a reference to the edge pointed-to by this handle. This should not be
-    // /// confused with `node`, which references the parent node of what is returned here.
-    // pub fn edge(&self) -> &Node<K, V>
-}
-
-pub enum ForceResult<NodeRef, Type> {
-    Leaf(Handle<NodeRef, Type, handle::Leaf>),
-    Internal(Handle<NodeRef, Type, handle::Internal>),
-}
+impl<'a, K, V, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
+    unsafe fn cast_unchecked<NewType>(&mut self)
+            -> NodeRef<marker::Mut, K, V, NewType> {
 
-impl<K, V, NodeRef: Deref<Target = Node<K, V>>, Type>
-    Handle<NodeRef, Type, handle::LeafOrInternal>
-{
-    /// Figure out whether this handle is pointing to something in a leaf node or to something in
-    /// an internal node, clarifying the type according to the result.
-    pub fn force(self) -> ForceResult<NodeRef, Type> {
-        if self.node.is_leaf() {
-            Leaf(Handle {
-                node: self.node,
-                index: self.index,
-                marker: PhantomData,
-            })
-        } else {
-            Internal(Handle {
-                node: self.node,
-                index: self.index,
-                marker: PhantomData,
-            })
+        NodeRef {
+            height: self.height,
+            node: self.node,
+            root: self.root,
+            _marker: PhantomData
         }
     }
-}
-impl<K, V, NodeRef> Handle<NodeRef, handle::Edge, handle::Leaf>
-    where NodeRef: Deref<Target = Node<K, V>> + DerefMut
-{
-    /// Tries to insert this key-value pair at the given index in this leaf node
-    /// If the node is full, we have to split it.
-    ///
-    /// Returns a *mut V to the inserted value, because the caller may want this when
-    /// they're done mutating the tree, but we don't want to borrow anything for now.
-    pub fn insert_as_leaf(mut self, key: K, value: V) -> (InsertionResult<K, V>, *mut V) {
-        if !self.node.is_full() {
-            // The element can fit, just insert it
-            (Fit, unsafe { self.node.insert_kv(self.index, key, value) as *mut _ })
-        } else {
-            // The element can't fit, this node is full. Split it into two nodes.
-            let (new_key, new_val, mut new_right) = self.node.split();
-            let left_len = self.node.len();
-
-            let ptr = unsafe {
-                if self.index <= left_len {
-                    self.node.insert_kv(self.index, key, value)
-                } else {
-                    // We need to subtract 1 because in splitting we took out new_key and new_val.
-                    // Just being in the right node means we are past left_len k/v pairs in the
-                    // left node and 1 k/v pair in the parent node.
-                    new_right.insert_kv(self.index - left_len - 1, key, value)
-                }
-            } as *mut _;
-
-            (Split(new_key, new_val, new_right), ptr)
-        }
-    }
-}
-
-impl<K, V, NodeRef> Handle<NodeRef, handle::Edge, handle::Internal>
-    where NodeRef: Deref<Target = Node<K, V>> + DerefMut
-{
-    /// Returns a mutable reference to the edge pointed-to by this handle. This should not be
-    /// confused with `node`, which references the parent node of what is returned here.
-    pub fn edge_mut(&mut self) -> &mut Node<K, V> {
-        unsafe { self.node.edges_mut().get_unchecked_mut(self.index) }
-    }
-
-    /// Tries to insert this key-value pair at the given index in this internal node
-    /// If the node is full, we have to split it.
-    pub fn insert_as_internal(mut self,
-                              key: K,
-                              value: V,
-                              right: Node<K, V>)
-                              -> InsertionResult<K, V> {
-        if !self.node.is_full() {
-            // The element can fit, just insert it
-            unsafe {
-                self.node.insert_kv(self.index, key, value);
-                self.node.insert_edge(self.index + 1, right); // +1 to insert to the right
-            }
-            Fit
-        } else {
-            // The element can't fit, this node is full. Split it into two nodes.
-            let (new_key, new_val, mut new_right) = self.node.split();
-            let left_len = self.node.len();
-
-            if self.index <= left_len {
-                unsafe {
-                    self.node.insert_kv(self.index, key, value);
-                    self.node.insert_edge(self.index + 1, right); // +1 to insert to the right
-                }
-            } else {
-                unsafe {
-                    // The -1 here is for the same reason as in insert_as_internal - because we
-                    // split, there are actually left_len + 1 k/v pairs before the right node, with
-                    // the extra 1 being put in the parent.
-                    new_right.insert_kv(self.index - left_len - 1, key, value);
-                    new_right.insert_edge(self.index - left_len, right);
-                }
-            }
 
-            Split(new_key, new_val, new_right)
+    unsafe fn reborrow_mut(&mut self) -> NodeRef<marker::Mut, K, V, Type> {
+        NodeRef {
+            height: self.height,
+            node: self.node,
+            root: self.root,
+            _marker: PhantomData
         }
     }
 
-    /// Handle an underflow in this node's child. We favor handling "to the left" because we know
-    /// we're empty, but our neighbour can be full. Handling to the left means when we choose to
-    /// steal, we pop off the end of our neighbour (always fast) and "unshift" ourselves
-    /// (always slow, but at least faster since we know we're half-empty).
-    /// Handling "to the right" reverses these roles. Of course, we merge whenever possible
-    /// because we want dense nodes, and merging is about equal work regardless of direction.
-    pub fn handle_underflow(mut self) {
+    fn as_leaf_mut(&mut self) -> &mut LeafNode<K, V> {
         unsafe {
-            if self.index > 0 {
-                self.handle_underflow_to_left();
-            } else {
-                self.handle_underflow_to_right();
-            }
+            &mut *(*self.node as *mut LeafNode<K, V>)
         }
     }
 
-    /// Right is underflowed. Tries to steal from left,
-    /// but merges left and right if left is low too.
-    unsafe fn handle_underflow_to_left(&mut self) {
-        let left_len = self.node.edges()[self.index - 1].len();
-        if left_len > min_load_from_capacity(self.node.capacity()) {
-            self.left_kv().steal_rightward();
-        } else {
-            self.left_kv().merge_children();
-        }
+    pub fn keys_mut(&mut self) -> &mut [K] {
+        unsafe { self.reborrow_mut().into_slices_mut().0 }
     }
 
-    /// Left is underflowed. Tries to steal from the right,
-    /// but merges left and right if right is low too.
-    unsafe fn handle_underflow_to_right(&mut self) {
-        let right_len = self.node.edges()[self.index + 1].len();
-        if right_len > min_load_from_capacity(self.node.capacity()) {
-            self.right_kv().steal_leftward();
-        } else {
-            self.right_kv().merge_children();
-        }
+    pub fn vals_mut(&mut self) -> &mut [V] {
+        unsafe { self.reborrow_mut().into_slices_mut().1 }
     }
 }
 
-impl<K, V, NodeRef, NodeType> Handle<NodeRef, handle::Edge, NodeType>
-    where NodeRef: Deref<Target = Node<K, V>> + DerefMut
-{
-    /// Gets the handle pointing to the key/value pair just to the left of the pointed-to edge.
-    /// This is unsafe because the handle might point to the first edge in the node, which has no
-    /// pair to its left.
-    unsafe fn left_kv<'a>(&'a mut self) -> Handle<&'a mut Node<K, V>, handle::KV, NodeType> {
-        Handle {
-            node: &mut *self.node,
-            index: self.index - 1,
-            marker: PhantomData,
+impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Immut<'a>, K, V, Type> {
+    pub fn into_slices(self) -> (&'a [K], &'a [V]) {
+        unsafe {
+            (
+                slice::from_raw_parts(
+                    self.as_leaf().keys.as_ptr(),
+                    self.len()
+                ),
+                slice::from_raw_parts(
+                    self.as_leaf().vals.as_ptr(),
+                    self.len()
+                )
+            )
         }
     }
+}
 
-    /// Gets the handle pointing to the key/value pair just to the right of the pointed-to edge.
-    /// This is unsafe because the handle might point to the last edge in the node, which has no
-    /// pair to its right.
-    unsafe fn right_kv<'a>(&'a mut self) -> Handle<&'a mut Node<K, V>, handle::KV, NodeType> {
-        Handle {
-            node: &mut *self.node,
-            index: self.index,
-            marker: PhantomData,
+impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
+    pub fn into_root_mut(self) -> &'a mut Root<K, V> {
+        unsafe {
+            &mut *(self.root as *mut Root<K, V>)
         }
     }
-}
 
-impl<'a, K: 'a, V: 'a, NodeType> Handle<&'a Node<K, V>, handle::KV, NodeType> {
-    /// Turns the handle into references to the key and value it points at. This is necessary
-    /// because the returned pointers have larger lifetimes than what would be returned by `key`
-    /// or `val`.
-    pub fn into_kv(self) -> (&'a K, &'a V) {
-        let (keys, vals) = self.node.as_slices();
+    pub fn into_slices_mut(mut self) -> (&'a mut [K], &'a mut [V]) {
         unsafe {
-            (keys.get_unchecked(self.index),
-             vals.get_unchecked(self.index))
+            (
+                slice::from_raw_parts_mut(
+                    &mut self.as_leaf_mut().keys as *mut [K] as *mut K,
+                    self.len()
+                ),
+                slice::from_raw_parts_mut(
+                    &mut self.as_leaf_mut().vals as *mut [V] as *mut V,
+                    self.len()
+                )
+            )
         }
     }
 }
 
-impl<'a, K: 'a, V: 'a, NodeType> Handle<&'a mut Node<K, V>, handle::KV, NodeType> {
-    /// Turns the handle into mutable references to the key and value it points at. This is
-    /// necessary because the returned pointers have larger lifetimes than what would be returned
-    /// by `key_mut` or `val_mut`.
-    pub fn into_kv_mut(self) -> (&'a mut K, &'a mut V) {
-        let (keys, vals) = self.node.as_slices_mut();
+impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
+    pub fn push(&mut self, key: K, val: V) {
+        // Necessary for correctness, but this is an internal module
+        debug_assert!(self.len() < CAPACITY);
+
+        let idx = self.len();
+
         unsafe {
-            (keys.get_unchecked_mut(self.index),
-             vals.get_unchecked_mut(self.index))
+            ptr::write(self.keys_mut().get_unchecked_mut(idx), key);
+            ptr::write(self.vals_mut().get_unchecked_mut(idx), val);
         }
+
+        self.as_leaf_mut().len += 1;
     }
 
-    /// Convert this handle into one pointing at the edge immediately to the left of the key/value
-    /// pair pointed-to by this handle. This is useful because it returns a reference with larger
-    /// lifetime than `left_edge`.
-    pub fn into_left_edge(self) -> Handle<&'a mut Node<K, V>, handle::Edge, NodeType> {
-        Handle {
-            node: &mut *self.node,
-            index: self.index,
-            marker: PhantomData,
+    pub fn push_front(&mut self, key: K, val: V) {
+        // Necessary for correctness, but this is an internal module
+        debug_assert!(self.len() < CAPACITY);
+
+        unsafe {
+            slice_insert(self.keys_mut(), 0, key);
+            slice_insert(self.vals_mut(), 0, val);
         }
+
+        self.as_leaf_mut().len += 1;
     }
 }
 
+impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
+    pub fn push(&mut self, key: K, val: V, edge: Root<K, V>) {
+        // Necessary for correctness, but this is an internal module
+        debug_assert!(edge.height == self.height - 1);
+        debug_assert!(self.len() < CAPACITY);
 
-impl<'a, K: 'a, V: 'a, NodeRef: Deref<Target = Node<K, V>> + 'a, NodeType> Handle<NodeRef,
-                                                                                  handle::KV,
-                                                                                  NodeType> {
-    // These are fine to include, but are currently unneeded.
-    //
-    // /// Returns a reference to the key pointed-to by this handle. This doesn't return a
-    // /// reference with a lifetime as large as `into_kv_mut`, but it also does not consume the
-    // /// handle.
-    // pub fn key(&'a self) -> &'a K {
-    //     unsafe { self.node.keys().get_unchecked(self.index) }
-    // }
-    //
-    // /// Returns a reference to the value pointed-to by this handle. This doesn't return a
-    // /// reference with a lifetime as large as `into_kv_mut`, but it also does not consume the
-    // /// handle.
-    // pub fn val(&'a self) -> &'a V {
-    //     unsafe { self.node.vals().get_unchecked(self.index) }
-    // }
-}
+        let idx = self.len();
 
-impl<'a, K: 'a, V: 'a, NodeRef, NodeType> Handle<NodeRef, handle::KV, NodeType>
-    where NodeRef: 'a + Deref<Target = Node<K, V>> + DerefMut
-{
-    /// Returns a mutable reference to the key pointed-to by this handle. This doesn't return a
-    /// reference with a lifetime as large as `into_kv_mut`, but it also does not consume the
-    /// handle.
-    pub fn key_mut(&'a mut self) -> &'a mut K {
-        unsafe { self.node.keys_mut().get_unchecked_mut(self.index) }
-    }
+        unsafe {
+            ptr::write(self.keys_mut().get_unchecked_mut(idx), key);
+            ptr::write(self.vals_mut().get_unchecked_mut(idx), val);
+            ptr::write(self.as_internal_mut().edges.get_unchecked_mut(idx + 1), edge.node);
 
-    /// Returns a mutable reference to the value pointed-to by this handle. This doesn't return a
-    /// reference with a lifetime as large as `into_kv_mut`, but it also does not consume the
-    /// handle.
-    pub fn val_mut(&'a mut self) -> &'a mut V {
-        unsafe { self.node.vals_mut().get_unchecked_mut(self.index) }
-    }
-}
+            self.as_leaf_mut().len += 1;
 
-impl<K, V, NodeRef, NodeType> Handle<NodeRef, handle::KV, NodeType>
-    where NodeRef: Deref<Target = Node<K, V>> + DerefMut
-{
-    /// Gets the handle pointing to the edge immediately to the left of the key/value pair pointed
-    /// to by this handle.
-    pub fn left_edge<'a>(&'a mut self) -> Handle<&'a mut Node<K, V>, handle::Edge, NodeType> {
-        Handle {
-            node: &mut *self.node,
-            index: self.index,
-            marker: PhantomData,
+            Handle::new_edge(self.reborrow_mut(), idx + 1).correct_parent_link();
         }
     }
 
-    /// Gets the handle pointing to the edge immediately to the right of the key/value pair pointed
-    /// to by this handle.
-    pub fn right_edge<'a>(&'a mut self) -> Handle<&'a mut Node<K, V>, handle::Edge, NodeType> {
-        Handle {
-            node: &mut *self.node,
-            index: self.index + 1,
-            marker: PhantomData,
+    pub fn push_front(&mut self, key: K, val: V, edge: Root<K, V>) {
+        // Necessary for correctness, but this is an internal module
+        debug_assert!(edge.height == self.height - 1);
+        debug_assert!(self.len() < CAPACITY);
+
+        unsafe {
+            slice_insert(self.keys_mut(), 0, key);
+            slice_insert(self.vals_mut(), 0, val);
+            slice_insert(
+                slice::from_raw_parts_mut(
+                    self.as_internal_mut().edges.as_mut_ptr(),
+                    self.len()+1
+                ),
+                0,
+                edge.node
+            );
+
+            self.as_leaf_mut().len += 1;
+
+            for i in 0..self.len()+1 {
+                Handle::new_edge(self.reborrow_mut(), i).correct_parent_link();
+            }
         }
-    }
-}
 
-impl<K, V, NodeRef> Handle<NodeRef, handle::KV, handle::Leaf>
-    where NodeRef: Deref<Target = Node<K, V>> + DerefMut
-{
-    /// Removes the key/value pair at the handle's location.
-    ///
-    /// # Panics (in debug build)
-    ///
-    /// Panics if the node containing the pair is not a leaf node.
-    pub fn remove_as_leaf(mut self) -> (K, V) {
-        unsafe { self.node.remove_kv(self.index) }
     }
 }
 
-impl<K, V, NodeRef> Handle<NodeRef, handle::KV, handle::Internal>
-    where NodeRef: Deref<Target = Node<K, V>> + DerefMut
-{
-    /// Steal! Stealing is roughly analogous to a binary tree rotation.
-    /// In this case, we're "rotating" right.
-    unsafe fn steal_rightward(&mut self) {
-        // Take the biggest stuff off left
-        let (mut key, mut val, edge) = {
-            let mut left_handle = self.left_edge();
-            let left = left_handle.edge_mut();
-            let (key, val) = left.pop_kv();
-            let edge = if left.is_leaf() {
-                None
-            } else {
-                Some(left.pop_edge())
+impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> {
+    pub fn pop(&mut self) -> (K, V, Option<Root<K, V>>) {
+        // Necessary for correctness, but this is an internal module
+        debug_assert!(self.len() > 0);
+
+        let idx = self.len() - 1;
+
+        unsafe {
+            let key = ptr::read(self.keys().get_unchecked(idx));
+            let val = ptr::read(self.vals().get_unchecked(idx));
+            let edge = match self.reborrow_mut().force() {
+                ForceResult::Leaf(_) => None,
+                ForceResult::Internal(internal) => {
+                    let edge = ptr::read(internal.as_internal().edges.get_unchecked(idx + 1));
+                    let mut new_root = Root { node: edge, height: internal.height - 1 };
+                    new_root.as_mut().as_leaf_mut().parent = ptr::null();
+                    Some(new_root)
+                }
             };
 
+            self.as_leaf_mut().len -= 1;
             (key, val, edge)
-        };
-
-        // Swap the parent's separating key-value pair with left's
-        mem::swap(&mut key, self.key_mut());
-        mem::swap(&mut val, self.val_mut());
-
-        // Put them at the start of right
-        let mut right_handle = self.right_edge();
-        let right = right_handle.edge_mut();
-        right.insert_kv(0, key, val);
-        match edge {
-            Some(edge) => right.insert_edge(0, edge),
-            None => {}
         }
     }
 
-    /// Steal! Stealing is roughly analogous to a binary tree rotation.
-    /// In this case, we're "rotating" left.
-    unsafe fn steal_leftward(&mut self) {
-        // Take the smallest stuff off right
-        let (mut key, mut val, edge) = {
-            let mut right_handle = self.right_edge();
-            let right = right_handle.edge_mut();
-            let (key, val) = right.remove_kv(0);
-            let edge = if right.is_leaf() {
-                None
-            } else {
-                Some(right.remove_edge(0))
-            };
+    pub fn pop_front(&mut self) -> (K, V, Option<Root<K, V>>) {
+        // Necessary for correctness, but this is an internal module
+        debug_assert!(self.len() > 0);
 
-            (key, val, edge)
-        };
+        let old_len = self.len();
 
-        // Swap the parent's separating key-value pair with right's
-        mem::swap(&mut key, self.key_mut());
-        mem::swap(&mut val, self.val_mut());
-
-        // Put them at the end of left
-        let mut left_handle = self.left_edge();
-        let left = left_handle.edge_mut();
-        left.push_kv(key, val);
-        match edge {
-            Some(edge) => left.push_edge(edge),
-            None => {}
-        }
-    }
+        unsafe {
+            let key = slice_remove(self.keys_mut(), 0);
+            let val = slice_remove(self.vals_mut(), 0);
+            let edge = match self.reborrow_mut().force() {
+                ForceResult::Leaf(_) => None,
+                ForceResult::Internal(mut internal) => {
+                    let edge = slice_remove(
+                        slice::from_raw_parts_mut(
+                            internal.as_internal_mut().edges.as_mut_ptr(),
+                            old_len+1
+                        ),
+                        0
+                    );
+
+                    let mut new_root = Root { node: edge, height: internal.height - 1 };
+                    new_root.as_mut().as_leaf_mut().parent = ptr::null();
+
+                    for i in 0..old_len {
+                        Handle::new_edge(internal.reborrow_mut(), i).correct_parent_link();
+                    }
+
+                    Some(new_root)
+                }
+            };
 
-    /// Merge! Smooshes left and right into one node, along with the key-value
-    /// pair that separated them in their parent.
-    unsafe fn merge_children(mut self) {
-        // Permanently remove right's index, and the key-value pair that separates
-        // left and right
-        let (key, val) = self.node.remove_kv(self.index);
-        let right = self.node.remove_edge(self.index + 1);
+            self.as_leaf_mut().len -= 1;
 
-        // Give left right's stuff.
-        self.left_edge()
-            .edge_mut()
-            .absorb(key, val, right);
+            (key, val, edge)
+        }
     }
 }
 
-impl<K, V> Node<K, V> {
-    /// Returns the mutable handle pointing to the key/value pair at a given index.
-    ///
-    /// # Panics (in debug build)
-    ///
-    /// Panics if the given index is out of bounds.
-    pub fn kv_handle(&mut self,
-                     index: usize)
-                     -> Handle<&mut Node<K, V>, handle::KV, handle::LeafOrInternal> {
-        // Necessary for correctness, but in a private module
-        debug_assert!(index < self.len(), "kv_handle index out of bounds");
-        Handle {
-            node: self,
-            index: index,
-            marker: PhantomData,
+impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
+    pub fn force(self) -> ForceResult<
+        NodeRef<BorrowType, K, V, marker::Leaf>,
+        NodeRef<BorrowType, K, V, marker::Internal>
+    > {
+        if self.height == 0 {
+            ForceResult::Leaf(NodeRef {
+                height: self.height,
+                node: self.node,
+                root: self.root,
+                _marker: PhantomData
+            })
+        } else {
+            ForceResult::Internal(NodeRef {
+                height: self.height,
+                node: self.node,
+                root: self.root,
+                _marker: PhantomData
+            })
         }
     }
+}
 
-    pub fn iter<'a>(&'a self) -> Traversal<'a, K, V> {
-        self.as_slices_internal().iter()
-    }
+pub struct Handle<Node, Type> {
+    node: Node,
+    idx: usize,
+    _marker: PhantomData<Type>
+}
 
-    pub fn iter_mut<'a>(&'a mut self) -> MutTraversal<'a, K, V> {
-        self.as_slices_internal_mut().iter_mut()
+impl<Node: Copy, Type> Copy for Handle<Node, Type> { }
+impl<Node: Copy, Type> Clone for Handle<Node, Type> {
+    fn clone(&self) -> Self {
+        *self
     }
+}
 
-    pub fn into_iter(self) -> MoveTraversal<K, V> {
-        unsafe {
-            let ret = MoveTraversal {
-                inner: MoveTraversalImpl {
-                    keys: RawItems::from_slice(self.keys()),
-                    vals: RawItems::from_slice(self.vals()),
-                    edges: RawItems::from_slice(self.edges()),
-
-                    ptr: Unique::new(*self.keys as *mut u8),
-                    capacity: self.capacity(),
-                    is_leaf: self.is_leaf(),
-                },
-                head_is_edge: true,
-                tail_is_edge: true,
-                has_edges: !self.is_leaf(),
-            };
-            mem::forget(self);
-            ret
-        }
+impl<Node, Type> Handle<Node, Type> {
+    pub fn into_node(self) -> Node {
+        self.node
     }
+}
 
-    /// When a node has no keys or values and only a single edge, extract that edge.
-    pub fn hoist_lone_child(&mut self) {
+impl<BorrowType, K, V, NodeType> Handle<NodeRef<BorrowType, K, V, NodeType>, marker::KV> {
+    pub fn new_kv(node: NodeRef<BorrowType, K, V, NodeType>, idx: usize) -> Self {
         // Necessary for correctness, but in a private module
-        debug_assert!(self.is_empty());
-        debug_assert!(!self.is_leaf());
+        debug_assert!(idx < node.len());
 
-        unsafe {
-            let ret = ptr::read(self.edges().get_unchecked(0));
-            self.destroy();
-            ptr::write(self, ret);
+        Handle {
+            node: node,
+            idx: idx,
+            _marker: PhantomData
         }
     }
-}
-
-// Vector functions (all unchecked)
-impl<K, V> Node<K, V> {
-    // This must be followed by push_edge on an internal node.
-    #[inline]
-    unsafe fn push_kv(&mut self, key: K, val: V) {
-        let len = self.len();
-
-        ptr::write(self.keys_mut().get_unchecked_mut(len), key);
-        ptr::write(self.vals_mut().get_unchecked_mut(len), val);
 
-        self._len += 1;
+    pub fn left_edge(self) -> Handle<NodeRef<BorrowType, K, V, NodeType>, marker::Edge> {
+        Handle::new_edge(self.node, self.idx)
     }
 
-    // This can only be called immediately after a call to push_kv.
-    #[inline]
-    unsafe fn push_edge(&mut self, edge: Node<K, V>) {
-        let len = self.len();
-
-        ptr::write(self.edges_mut().get_unchecked_mut(len), edge);
+    pub fn right_edge(self) -> Handle<NodeRef<BorrowType, K, V, NodeType>, marker::Edge> {
+        Handle::new_edge(self.node, self.idx + 1)
     }
+}
 
-    // This must be followed by insert_edge on an internal node.
-    #[inline]
-    unsafe fn insert_kv(&mut self, index: usize, key: K, val: V) -> &mut V {
-        ptr::copy(self.keys().as_ptr().offset(index as isize),
-                  self.keys_mut().as_mut_ptr().offset(index as isize + 1),
-                  self.len() - index);
-        ptr::copy(self.vals().as_ptr().offset(index as isize),
-                  self.vals_mut().as_mut_ptr().offset(index as isize + 1),
-                  self.len() - index);
+impl<BorrowType, K, V, NodeType, HandleType> PartialEq
+        for Handle<NodeRef<BorrowType, K, V, NodeType>, HandleType> {
 
-        ptr::write(self.keys_mut().get_unchecked_mut(index), key);
-        ptr::write(self.vals_mut().get_unchecked_mut(index), val);
+    fn eq(&self, other: &Self) -> bool {
+        self.node.node == other.node.node && self.idx == other.idx
+    }
+}
 
-        self._len += 1;
+impl<BorrowType, K, V, NodeType, HandleType>
+        Handle<NodeRef<BorrowType, K, V, NodeType>, HandleType> {
 
-        self.vals_mut().get_unchecked_mut(index)
-    }
+    pub fn reborrow(&self)
+            -> Handle<NodeRef<marker::Immut, K, V, NodeType>, HandleType> {
 
-    // This can only be called immediately after a call to insert_kv.
-    #[inline]
-    unsafe fn insert_edge(&mut self, index: usize, edge: Node<K, V>) {
-        ptr::copy(self.edges().as_ptr().offset(index as isize),
-                  self.edges_mut().as_mut_ptr().offset(index as isize + 1),
-                  self.len() - index);
-        ptr::write(self.edges_mut().get_unchecked_mut(index), edge);
+        // We can't use Handle::new_kv or Handle::new_edge because we don't know our type
+        Handle {
+            node: self.node.reborrow(),
+            idx: self.idx,
+            _marker: PhantomData
+        }
     }
+}
 
-    // This must be followed by pop_edge on an internal node.
-    #[inline]
-    unsafe fn pop_kv(&mut self) -> (K, V) {
-        let key = ptr::read(self.keys().get_unchecked(self.len() - 1));
-        let val = ptr::read(self.vals().get_unchecked(self.len() - 1));
+impl<'a, K, V, NodeType, HandleType>
+        Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>, HandleType> {
 
-        self._len -= 1;
+    pub unsafe fn reborrow_mut(&mut self)
+            -> Handle<NodeRef<marker::Mut, K, V, NodeType>, HandleType> {
 
-        (key, val)
+        // We can't use Handle::new_kv or Handle::new_edge because we don't know our type
+        Handle {
+            node: self.node.reborrow_mut(),
+            idx: self.idx,
+            _marker: PhantomData
+        }
     }
+}
 
-    // This can only be called immediately after a call to pop_kv.
-    #[inline]
-    unsafe fn pop_edge(&mut self) -> Node<K, V> {
-        let edge = ptr::read(self.edges().get_unchecked(self.len() + 1));
-
-        edge
-    }
+impl<BorrowType, K, V, NodeType>
+        Handle<NodeRef<BorrowType, K, V, NodeType>, marker::Edge> {
 
-    // This must be followed by remove_edge on an internal node.
-    #[inline]
-    unsafe fn remove_kv(&mut self, index: usize) -> (K, V) {
-        let key = ptr::read(self.keys().get_unchecked(index));
-        let val = ptr::read(self.vals().get_unchecked(index));
+    pub fn new_edge(node: NodeRef<BorrowType, K, V, NodeType>, idx: usize) -> Self {
+        // Necessary for correctness, but in a private module
+        debug_assert!(idx <= node.len());
 
-        ptr::copy(self.keys().as_ptr().offset(index as isize + 1),
-                  self.keys_mut().as_mut_ptr().offset(index as isize),
-                  self.len() - index - 1);
-        ptr::copy(self.vals().as_ptr().offset(index as isize + 1),
-                  self.vals_mut().as_mut_ptr().offset(index as isize),
-                  self.len() - index - 1);
+        Handle {
+            node: node,
+            idx: idx,
+            _marker: PhantomData
+        }
+    }
 
-        self._len -= 1;
+    pub fn left_kv(self)
+            -> Result<Handle<NodeRef<BorrowType, K, V, NodeType>, marker::KV>, Self> {
 
-        (key, val)
+        if self.idx > 0 {
+            Ok(Handle::new_kv(self.node, self.idx - 1))
+        } else {
+            Err(self)
+        }
     }
 
-    // This can only be called immediately after a call to remove_kv.
-    #[inline]
-    unsafe fn remove_edge(&mut self, index: usize) -> Node<K, V> {
-        let edge = ptr::read(self.edges().get_unchecked(index));
-
-        ptr::copy(self.edges().as_ptr().offset(index as isize + 1),
-                  self.edges_mut().as_mut_ptr().offset(index as isize),
-                  // index can be == len+1, so do the +1 first to avoid underflow.
-                  (self.len() + 1) - index);
+    pub fn right_kv(self)
+            -> Result<Handle<NodeRef<BorrowType, K, V, NodeType>, marker::KV>, Self> {
 
-        edge
+        if self.idx < self.node.len() {
+            Ok(Handle::new_kv(self.node, self.idx))
+        } else {
+            Err(self)
+        }
     }
 }
 
-// Private implementation details
-impl<K, V> Node<K, V> {
-    /// Node is full, so split it into two nodes, and yield the middle-most key-value pair
-    /// because we have one too many, and our parent now has one too few
-    fn split(&mut self) -> (K, V, Node<K, V>) {
-        // Necessary for correctness, but in a private function
-        debug_assert!(!self.is_empty());
-
-        let mut right = if self.is_leaf() {
-            Node::new_leaf(self.capacity())
-        } else {
-            unsafe { Node::new_internal(self.capacity()) }
-        };
+impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge> {
+    fn insert_fit(&mut self, key: K, val: V) -> *mut V {
+        // Necessary for correctness, but in a private module
+        debug_assert!(self.node.len() < CAPACITY);
 
         unsafe {
-            right._len = self.len() / 2;
-            let right_offset = self.len() - right.len();
-            ptr::copy_nonoverlapping(self.keys().as_ptr().offset(right_offset as isize),
-                                     right.keys_mut().as_mut_ptr(),
-                                     right.len());
-            ptr::copy_nonoverlapping(self.vals().as_ptr().offset(right_offset as isize),
-                                     right.vals_mut().as_mut_ptr(),
-                                     right.len());
-            if !self.is_leaf() {
-                ptr::copy_nonoverlapping(self.edges().as_ptr().offset(right_offset as isize),
-                                         right.edges_mut().as_mut_ptr(),
-                                         right.len() + 1);
-            }
-
-            let key = ptr::read(self.keys().get_unchecked(right_offset - 1));
-            let val = ptr::read(self.vals().get_unchecked(right_offset - 1));
+            slice_insert(self.node.keys_mut(), self.idx, key);
+            slice_insert(self.node.vals_mut(), self.idx, val);
 
-            self._len = right_offset - 1;
+            self.node.as_leaf_mut().len += 1;
 
-            (key, val, right)
+            self.node.vals_mut().get_unchecked_mut(self.idx)
         }
     }
 
-    /// Take all the values from right, separated by the given key and value
-    fn absorb(&mut self, key: K, val: V, mut right: Node<K, V>) {
-        // Necessary for correctness, but in a private function
-        // Just as a sanity check, make sure we can fit this guy in
-        debug_assert!(self.len() + right.len() <= self.capacity());
-        debug_assert!(self.is_leaf() == right.is_leaf());
+    pub fn insert(mut self, key: K, val: V)
+            -> (InsertResult<'a, K, V, marker::Leaf>, *mut V) {
 
-        unsafe {
-            let old_len = self.len();
-            self._len += right.len() + 1;
-
-            ptr::write(self.keys_mut().get_unchecked_mut(old_len), key);
-            ptr::write(self.vals_mut().get_unchecked_mut(old_len), val);
-
-            ptr::copy_nonoverlapping(right.keys().as_ptr(),
-                                     self.keys_mut().as_mut_ptr().offset(old_len as isize + 1),
-                                     right.len());
-            ptr::copy_nonoverlapping(right.vals().as_ptr(),
-                                     self.vals_mut().as_mut_ptr().offset(old_len as isize + 1),
-                                     right.len());
-            if !self.is_leaf() {
-                ptr::copy_nonoverlapping(right.edges().as_ptr(),
-                                         self.edges_mut()
-                                             .as_mut_ptr()
-                                             .offset(old_len as isize + 1),
-                                         right.len() + 1);
-            }
-
-            right.destroy();
-            mem::forget(right);
+        if self.node.len() < CAPACITY {
+            let ptr = self.insert_fit(key, val);
+            (InsertResult::Fit(Handle::new_kv(self.node, self.idx)), ptr)
+        } else {
+            let middle = Handle::new_kv(self.node, B);
+            let (mut left, k, v, mut right) = middle.split();
+            let ptr = if self.idx <= B {
+                unsafe {
+                    Handle::new_edge(left.reborrow_mut(), self.idx).insert_fit(key, val)
+                }
+            } else {
+                unsafe {
+                    Handle::new_edge(
+                        right.as_mut().cast_unchecked::<marker::Leaf>(),
+                        self.idx - (B + 1)
+                    ).insert_fit(key, val)
+                }
+            };
+            (InsertResult::Split(left, k, v, right), ptr)
         }
     }
 }
 
-/// Get the capacity of a node from the order of the parent B-Tree
-fn capacity_from_b(b: usize) -> usize {
-    2 * b - 1
-}
-
-/// Get the minimum load of a node from its capacity
-fn min_load_from_capacity(cap: usize) -> usize {
-    // B - 1
-    cap / 2
-}
+impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::Edge> {
+    fn correct_parent_link(mut self) {
+        let idx = self.idx as u16;
+        let ptr = self.node.as_internal_mut() as *mut _;
+        let mut child = self.descend();
+        child.as_leaf_mut().parent = ptr;
+        child.as_leaf_mut().parent_idx = idx;
+    }
 
-/// A trait for pairs of `Iterator`s, one over edges and the other over key/value pairs. This is
-/// necessary, as the `MoveTraversalImpl` needs to have a destructor that deallocates the `Node`,
-/// and a pair of `Iterator`s would require two independent destructors.
-trait TraversalImpl {
-    type Item;
-    type Edge;
+    unsafe fn cast_unchecked<NewType>(&mut self)
+            -> Handle<NodeRef<marker::Mut, K, V, NewType>, marker::Edge> {
 
-    fn next_kv(&mut self) -> Option<Self::Item>;
-    fn next_kv_back(&mut self) -> Option<Self::Item>;
+        Handle::new_edge(self.node.cast_unchecked(), self.idx)
+    }
 
-    fn next_edge(&mut self) -> Option<Self::Edge>;
-    fn next_edge_back(&mut self) -> Option<Self::Edge>;
-}
+    fn insert_fit(&mut self, key: K, val: V, edge: Root<K, V>) {
+        // Necessary for correctness, but in an internal module
+        debug_assert!(self.node.len() < CAPACITY);
+        debug_assert!(edge.height == self.node.height - 1);
 
-/// A `TraversalImpl` that actually is backed by two iterators. This works in the non-moving case,
-/// as no deallocation needs to be done.
-#[derive(Clone)]
-struct ElemsAndEdges<Elems, Edges>(Elems, Edges);
+        unsafe {
+            self.cast_unchecked::<marker::Leaf>().insert_fit(key, val);
+
+            slice_insert(
+                slice::from_raw_parts_mut(
+                    self.node.as_internal_mut().edges.as_mut_ptr(),
+                    self.node.len()
+                ),
+                self.idx + 1,
+                edge.node
+            );
+
+            for i in (self.idx+1)..(self.node.len()+1) {
+                Handle::new_edge(self.node.reborrow_mut(), i).correct_parent_link();
+            }
+        }
+    }
 
-impl<K, V, E, Elems: DoubleEndedIterator, Edges: DoubleEndedIterator>
-        TraversalImpl for ElemsAndEdges<Elems, Edges>
-    where Elems : Iterator<Item=(K, V)>, Edges : Iterator<Item=E>
-{
-    type Item = (K, V);
-    type Edge = E;
+    pub fn insert(mut self, key: K, val: V, edge: Root<K, V>)
+            -> InsertResult<'a, K, V, marker::Internal> {
 
-    fn next_kv(&mut self) -> Option<(K, V)> { self.0.next() }
-    fn next_kv_back(&mut self) -> Option<(K, V)> { self.0.next_back() }
+        // Necessary for correctness, but this is an internal module
+        debug_assert!(edge.height == self.node.height - 1);
 
-    fn next_edge(&mut self) -> Option<E> { self.1.next() }
-    fn next_edge_back(&mut self) -> Option<E> { self.1.next_back() }
+        if self.node.len() < CAPACITY {
+            self.insert_fit(key, val, edge);
+            InsertResult::Fit(Handle::new_kv(self.node, self.idx))
+        } else {
+            let middle = Handle::new_kv(self.node, B);
+            let (mut left, k, v, mut right) = middle.split();
+            if self.idx <= B {
+                unsafe {
+                    Handle::new_edge(left.reborrow_mut(), self.idx).insert_fit(key, val, edge);
+                }
+            } else {
+                unsafe {
+                    Handle::new_edge(
+                        right.as_mut().cast_unchecked::<marker::Internal>(),
+                        self.idx - (B + 1)
+                    ).insert_fit(key, val, edge);
+                }
+            }
+            InsertResult::Split(left, k, v, right)
+        }
+    }
 }
 
-/// A `TraversalImpl` taking a `Node` by value.
-struct MoveTraversalImpl<K, V> {
-    keys: RawItems<K>,
-    vals: RawItems<V>,
-    edges: RawItems<Node<K, V>>,
+impl<BorrowType, K, V>
+        Handle<NodeRef<BorrowType, K, V, marker::Internal>, marker::Edge> {
 
-    // For deallocation when we are done iterating.
-    ptr: Unique<u8>,
-    capacity: usize,
-    is_leaf: bool,
+    pub fn descend(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
+        NodeRef {
+            height: self.node.height - 1,
+            node: unsafe { self.node.as_internal().edges.get_unchecked(self.idx).as_ptr() },
+            root: self.node.root,
+            _marker: PhantomData
+        }
+    }
 }
 
-unsafe impl<K: Sync, V: Sync> Sync for MoveTraversalImpl<K, V> {}
-unsafe impl<K: Send, V: Send> Send for MoveTraversalImpl<K, V> {}
-
-impl<K, V> TraversalImpl for MoveTraversalImpl<K, V> {
-    type Item = (K, V);
-    type Edge = Node<K, V>;
+impl<'a, K: 'a, V: 'a, NodeType>
+        Handle<NodeRef<marker::Immut<'a>, K, V, NodeType>, marker::KV> {
 
-    fn next_kv(&mut self) -> Option<(K, V)> {
-        match (self.keys.next(), self.vals.next()) {
-            (Some(k), Some(v)) => Some((k, v)),
-            _ => None,
+    pub fn into_kv(self) -> (&'a K, &'a V) {
+        let (keys, vals) = self.node.into_slices();
+        unsafe {
+            (keys.get_unchecked(self.idx), vals.get_unchecked(self.idx))
         }
     }
+}
 
-    fn next_kv_back(&mut self) -> Option<(K, V)> {
-        match (self.keys.next_back(), self.vals.next_back()) {
-            (Some(k), Some(v)) => Some((k, v)),
-            _ => None,
-        }
-    }
+impl<'a, K: 'a, V: 'a, NodeType>
+        Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>, marker::KV> {
 
-    fn next_edge(&mut self) -> Option<Node<K, V>> {
-        // Necessary for correctness, but in a private module
-        debug_assert!(!self.is_leaf);
-        self.edges.next()
+    pub fn into_kv_mut(self) -> (&'a mut K, &'a mut V) {
+        let (mut keys, mut vals) = self.node.into_slices_mut();
+        unsafe {
+            (keys.get_unchecked_mut(self.idx), vals.get_unchecked_mut(self.idx))
+        }
     }
+}
 
-    fn next_edge_back(&mut self) -> Option<Node<K, V>> {
-        // Necessary for correctness, but in a private module
-        debug_assert!(!self.is_leaf);
-        self.edges.next_back()
+impl<'a, K, V, NodeType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>, marker::KV> {
+    pub fn kv_mut(&mut self) -> (&mut K, &mut V) {
+        unsafe {
+            let (mut keys, mut vals) = self.node.reborrow_mut().into_slices_mut();
+            (keys.get_unchecked_mut(self.idx), vals.get_unchecked_mut(self.idx))
+        }
     }
 }
 
-impl<K, V> Drop for MoveTraversalImpl<K, V> {
-    #[unsafe_destructor_blind_to_params]
-    fn drop(&mut self) {
-        // We need to cleanup the stored values manually, as the RawItems destructor would run
-        // after our deallocation.
-        for _ in self.keys.by_ref() {}
-        for _ in self.vals.by_ref() {}
-        for _ in self.edges.by_ref() {}
+impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::KV> {
+    pub fn split(mut self)
+            -> (NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, K, V, Root<K, V>) {
+        unsafe {
+            let mut new_node = Box::new(LeafNode::new());
+
+            let k = ptr::read(self.node.keys().get_unchecked(self.idx));
+            let v = ptr::read(self.node.vals().get_unchecked(self.idx));
+
+            let new_len = self.node.len() - self.idx - 1;
+
+            ptr::copy_nonoverlapping(
+                self.node.keys().as_ptr().offset(self.idx as isize + 1),
+                new_node.keys.as_mut_ptr(),
+                new_len
+            );
+            ptr::copy_nonoverlapping(
+                self.node.vals().as_ptr().offset(self.idx as isize + 1),
+                new_node.vals.as_mut_ptr(),
+                new_len
+            );
+
+            self.node.as_leaf_mut().len = self.idx as u16;
+            new_node.len = new_len as u16;
+
+            (
+                self.node,
+                k, v,
+                Root {
+                    node: BoxedNode::from_leaf(new_node),
+                    height: 0
+                }
+            )
+        }
+    }
 
-        let (alignment, size) = calculate_allocation_generic::<K, V>(self.capacity, self.is_leaf);
-        unsafe { heap::deallocate(*self.ptr, size, alignment) };
+    pub fn remove(mut self)
+            -> (Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>, K, V) {
+        unsafe {
+            let k = slice_remove(self.node.keys_mut(), self.idx);
+            let v = slice_remove(self.node.vals_mut(), self.idx);
+            self.node.as_leaf_mut().len -= 1;
+            (self.left_edge(), k, v)
+        }
     }
 }
 
-/// An abstraction over all the different kinds of traversals a node supports
-#[derive(Clone)]
-struct AbsTraversal<Impl> {
-    inner: Impl,
-    head_is_edge: bool,
-    tail_is_edge: bool,
-    has_edges: bool,
-}
+impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::KV> {
+    pub fn split(mut self)
+            -> (NodeRef<marker::Mut<'a>, K, V, marker::Internal>, K, V, Root<K, V>) {
+        unsafe {
+            let mut new_node = Box::new(InternalNode::new());
+
+            let k = ptr::read(self.node.keys().get_unchecked(self.idx));
+            let v = ptr::read(self.node.vals().get_unchecked(self.idx));
+
+            let height = self.node.height;
+            let new_len = self.node.len() - self.idx - 1;
+
+            ptr::copy_nonoverlapping(
+                self.node.keys().as_ptr().offset(self.idx as isize + 1),
+                new_node.data.keys.as_mut_ptr(),
+                new_len
+            );
+            ptr::copy_nonoverlapping(
+                self.node.vals().as_ptr().offset(self.idx as isize + 1),
+                new_node.data.vals.as_mut_ptr(),
+                new_len
+            );
+            ptr::copy_nonoverlapping(
+                self.node.as_internal().edges.as_ptr().offset(self.idx as isize + 1),
+                new_node.edges.as_mut_ptr(),
+                new_len + 1
+            );
+
+            self.node.as_leaf_mut().len = self.idx as u16;
+            new_node.data.len = new_len as u16;
+
+            let mut new_root = Root {
+                node: BoxedNode::from_internal(new_node),
+                height: height
+            };
 
-/// A single atomic step in a traversal.
-pub enum TraversalItem<K, V, E> {
-    /// An element is visited. This isn't written as `Elem(K, V)` just because `opt.map(Elem)`
-    /// requires the function to take a single argument. (Enum constructors are functions.)
-    Elem((K, V)),
-    /// An edge is followed.
-    Edge(E),
-}
+            for i in 0..(new_len+1) {
+                Handle::new_edge(new_root.as_mut().cast_unchecked(), i).correct_parent_link();
+            }
 
-/// A traversal over a node's entries and edges
-pub type Traversal<'a, K, V> = AbsTraversal<ElemsAndEdges<Zip<slice::Iter<'a, K>,
-                                                              slice::Iter<'a, V>>,
-                                                          slice::Iter<'a, Node<K, V>>>>;
+            (
+                self.node,
+                k, v,
+                new_root
+            )
+        }
+    }
 
-/// A mutable traversal over a node's entries and edges
-pub type MutTraversal<'a, K, V> = AbsTraversal<ElemsAndEdges<Zip<slice::Iter<'a, K>,
-                                                                 slice::IterMut<'a, V>>,
-                                                             slice::IterMut<'a, Node<K, V>>>>;
+    pub fn can_merge(&self) -> bool {
+        (
+            self.reborrow()
+                .left_edge()
+                .descend()
+                .len()
+          + self.reborrow()
+                .right_edge()
+                .descend()
+                .len()
+          + 1
+        ) <= CAPACITY
+    }
+
+    pub fn merge(mut self)
+            -> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::Edge> {
+        let self1 = unsafe { ptr::read(&self) };
+        let self2 = unsafe { ptr::read(&self) };
+        let mut left_node = self1.left_edge().descend();
+        let left_len = left_node.len();
+        let mut right_node = self2.right_edge().descend();
+        let right_len = right_node.len();
+
+        // necessary for correctness, but in a private module
+        debug_assert!(left_len + right_len + 1 <= CAPACITY);
 
-/// An owning traversal over a node's entries and edges
-pub type MoveTraversal<K, V> = AbsTraversal<MoveTraversalImpl<K, V>>;
+        unsafe {
+            ptr::write(left_node.keys_mut().get_unchecked_mut(left_len),
+                       slice_remove(self.node.keys_mut(), self.idx));
+            ptr::copy_nonoverlapping(
+                right_node.keys().as_ptr(),
+                left_node.keys_mut().as_mut_ptr().offset(left_len as isize + 1),
+                right_len
+            );
+            ptr::write(left_node.vals_mut().get_unchecked_mut(left_len),
+                       slice_remove(self.node.vals_mut(), self.idx));
+            ptr::copy_nonoverlapping(
+                right_node.vals().as_ptr(),
+                left_node.vals_mut().as_mut_ptr().offset(left_len as isize + 1),
+                right_len
+            );
+
+            slice_remove(&mut self.node.as_internal_mut().edges, self.idx + 1);
+            for i in self.idx+1..self.node.len() {
+                Handle::new_edge(self.node.reborrow_mut(), i).correct_parent_link();
+            }
+            self.node.as_leaf_mut().len -= 1;
+
+            if self.node.height > 1 {
+                ptr::copy_nonoverlapping(
+                    right_node.cast_unchecked().as_internal().edges.as_ptr(),
+                    left_node.cast_unchecked()
+                             .as_internal_mut()
+                             .edges
+                             .as_mut_ptr()
+                             .offset(left_len as isize + 1),
+                    right_len + 1
+                );
+
+                for i in left_len+1..left_len+right_len+2 {
+                    Handle::new_edge(
+                        left_node.cast_unchecked().reborrow_mut(),
+                        i
+                    ).correct_parent_link();
+                }
 
+                heap::deallocate(
+                    *right_node.node as *mut u8,
+                    mem::size_of::<InternalNode<K, V>>(),
+                    mem::align_of::<InternalNode<K, V>>()
+                );
+            } else {
+                heap::deallocate(
+                    *right_node.node as *mut u8,
+                    mem::size_of::<LeafNode<K, V>>(),
+                    mem::align_of::<LeafNode<K, V>>()
+                );
+            }
 
-impl<K, V, E, Impl> Iterator for AbsTraversal<Impl>
-    where Impl: TraversalImpl<Item = (K, V), Edge = E>
-{
-    type Item = TraversalItem<K, V, E>;
+            left_node.as_leaf_mut().len += right_len as u16 + 1;
 
-    fn next(&mut self) -> Option<TraversalItem<K, V, E>> {
-        self.next_edge_item().map(Edge).or_else(|| self.next_kv_item().map(Elem))
+            Handle::new_edge(self.node, self.idx)
+        }
     }
 }
 
-impl<K, V, E, Impl> DoubleEndedIterator for AbsTraversal<Impl>
-    where Impl: TraversalImpl<Item = (K, V), Edge = E>
-{
-    fn next_back(&mut self) -> Option<TraversalItem<K, V, E>> {
-        self.next_edge_item_back().map(Edge).or_else(|| self.next_kv_item_back().map(Elem))
+impl<BorrowType, K, V, HandleType>
+        Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, HandleType> {
+
+    pub fn force(self) -> ForceResult<
+        Handle<NodeRef<BorrowType, K, V, marker::Leaf>, HandleType>,
+        Handle<NodeRef<BorrowType, K, V, marker::Internal>, HandleType>
+    > {
+        match self.node.force() {
+            ForceResult::Leaf(node) => ForceResult::Leaf(Handle {
+                node: node,
+                idx: self.idx,
+                _marker: PhantomData
+            }),
+            ForceResult::Internal(node) => ForceResult::Internal(Handle {
+                node: node,
+                idx: self.idx,
+                _marker: PhantomData
+            })
+        }
     }
 }
 
-impl<K, V, E, Impl> AbsTraversal<Impl> where Impl: TraversalImpl<Item = (K, V), Edge = E> {
-    /// Advances the iterator and returns the item if it's an edge. Returns None
-    /// and does nothing if the first item is not an edge.
-    pub fn next_edge_item(&mut self) -> Option<E> {
-        // NB. `&& self.has_edges` might be redundant in this condition.
-        let edge = if self.head_is_edge && self.has_edges {
-            self.inner.next_edge()
-        } else {
-            None
-        };
-        self.head_is_edge = false;
-        edge
-    }
-
-    /// Advances the iterator and returns the item if it's an edge. Returns None
-    /// and does nothing if the last item is not an edge.
-    pub fn next_edge_item_back(&mut self) -> Option<E> {
-        let edge = if self.tail_is_edge && self.has_edges {
-            self.inner.next_edge_back()
-        } else {
-            None
-        };
-        self.tail_is_edge = false;
-        edge
-    }
-
-    /// Advances the iterator and returns the item if it's a key-value pair. Returns None
-    /// and does nothing if the first item is not a key-value pair.
-    pub fn next_kv_item(&mut self) -> Option<(K, V)> {
-        if !self.head_is_edge {
-            self.head_is_edge = true;
-            self.inner.next_kv()
-        } else {
-            None
-        }
-    }
+pub enum ForceResult<Leaf, Internal> {
+    Leaf(Leaf),
+    Internal(Internal)
+}
 
-    /// Advances the iterator and returns the item if it's a key-value pair. Returns None
-    /// and does nothing if the last item is not a key-value pair.
-    pub fn next_kv_item_back(&mut self) -> Option<(K, V)> {
-        if !self.tail_is_edge {
-            self.tail_is_edge = true;
-            self.inner.next_kv_back()
-        } else {
-            None
-        }
-    }
+pub enum InsertResult<'a, K, V, Type> {
+    Fit(Handle<NodeRef<marker::Mut<'a>, K, V, Type>, marker::KV>),
+    Split(NodeRef<marker::Mut<'a>, K, V, Type>, K, V, Root<K, V>)
 }
 
-macro_rules! node_slice_impl {
-    ($NodeSlice:ident, $Traversal:ident,
-     $as_slices_internal:ident, $index:ident, $iter:ident) => {
-        impl<'a, K: Ord + 'a, V: 'a> $NodeSlice<'a, K, V> {
-            /// Performs linear search in a slice. Returns a tuple of (index, is_exact_match).
-            fn search_linear<Q: ?Sized>(&self, key: &Q) -> (usize, bool)
-                    where K: Borrow<Q>, Q: Ord {
-                for (i, k) in self.keys.iter().enumerate() {
-                    match key.cmp(k.borrow()) {
-                        Greater => {},
-                        Equal => return (i, true),
-                        Less => return (i, false),
-                    }
-                }
-                (self.keys.len(), false)
-            }
+pub mod marker {
+    use core::marker::PhantomData;
 
-            /// Returns a sub-slice with elements starting with `min_key`.
-            pub fn slice_from<Q: ?Sized + Ord>(self, min_key: &Q) -> $NodeSlice<'a, K, V> where
-                K: Borrow<Q>,
-            {
-                //  _______________
-                // |_1_|_3_|_5_|_7_|
-                // |   |   |   |   |
-                // 0 0 1 1 2 2 3 3 4  index
-                // |   |   |   |   |
-                // \___|___|___|___/  slice_from(&0); pos = 0
-                //     \___|___|___/  slice_from(&2); pos = 1
-                //     |___|___|___/  slice_from(&3); pos = 1; result.head_is_edge = false
-                //         \___|___/  slice_from(&4); pos = 2
-                //             \___/  slice_from(&6); pos = 3
-                //                \|/ slice_from(&999); pos = 4
-                let (pos, pos_is_kv) = self.search_linear(min_key);
-                $NodeSlice {
-                    has_edges: self.has_edges,
-                    edges: if !self.has_edges {
-                        self.edges
-                    } else {
-                        self.edges.$index(pos ..)
-                    },
-                    keys: &self.keys[pos ..],
-                    vals: self.vals.$index(pos ..),
-                    head_is_edge: !pos_is_kv,
-                    tail_is_edge: self.tail_is_edge,
-                }
-            }
+    pub enum Leaf { }
+    pub enum Internal { }
+    pub enum LeafOrInternal { }
 
-            /// Returns a sub-slice with elements up to and including `max_key`.
-            pub fn slice_to<Q: ?Sized + Ord>(self, max_key: &Q) -> $NodeSlice<'a, K, V> where
-                K: Borrow<Q>,
-            {
-                //  _______________
-                // |_1_|_3_|_5_|_7_|
-                // |   |   |   |   |
-                // 0 0 1 1 2 2 3 3 4  index
-                // |   |   |   |   |
-                //\|/  |   |   |   |  slice_to(&0); pos = 0
-                // \___/   |   |   |  slice_to(&2); pos = 1
-                // \___|___|   |   |  slice_to(&3); pos = 1; result.tail_is_edge = false
-                // \___|___/   |   |  slice_to(&4); pos = 2
-                // \___|___|___/   |  slice_to(&6); pos = 3
-                // \___|___|___|___/  slice_to(&999); pos = 4
-                let (pos, pos_is_kv) = self.search_linear(max_key);
-                let pos = pos + if pos_is_kv { 1 } else { 0 };
-                $NodeSlice {
-                    has_edges: self.has_edges,
-                    edges: if !self.has_edges {
-                        self.edges
-                    } else {
-                        self.edges.$index(.. (pos + 1))
-                    },
-                    keys: &self.keys[..pos],
-                    vals: self.vals.$index(.. pos),
-                    head_is_edge: self.head_is_edge,
-                    tail_is_edge: !pos_is_kv,
-                }
-            }
-        }
+    pub enum Owned { }
+    pub struct Immut<'a>(PhantomData<&'a ()>);
+    pub struct Mut<'a>(PhantomData<&'a mut ()>);
 
-        impl<'a, K: 'a, V: 'a> $NodeSlice<'a, K, V> {
-            /// Returns an iterator over key/value pairs and edges in a slice.
-            #[inline]
-            pub fn $iter(self) -> $Traversal<'a, K, V> {
-                let mut edges = self.edges.$iter();
-                // Skip edges at both ends, if excluded.
-                if !self.head_is_edge { edges.next(); }
-                if !self.tail_is_edge { edges.next_back(); }
-                // The key iterator is always immutable.
-                $Traversal {
-                    inner: ElemsAndEdges(
-                        self.keys.iter().zip(self.vals.$iter()),
-                        edges
-                    ),
-                    head_is_edge: self.head_is_edge,
-                    tail_is_edge: self.tail_is_edge,
-                    has_edges: self.has_edges,
-                }
-            }
-        }
-    }
+    pub enum KV { }
+    pub enum Edge { }
 }
 
-node_slice_impl!(NodeSlice, Traversal, as_slices_internal, index, iter);
-node_slice_impl!(MutNodeSlice, MutTraversal, as_slices_internal_mut, index_mut, iter_mut);
+unsafe fn slice_insert<T>(slice: &mut [T], idx: usize, val: T) {
+    ptr::copy(
+        slice.as_ptr().offset(idx as isize),
+        slice.as_mut_ptr().offset(idx as isize + 1),
+        slice.len() - idx
+    );
+    ptr::write(slice.get_unchecked_mut(idx), val);
+}
+
+unsafe fn slice_remove<T>(slice: &mut [T], idx: usize) -> T {
+    let ret = ptr::read(slice.get_unchecked(idx));
+    ptr::copy(
+        slice.as_ptr().offset(idx as isize + 1),
+        slice.as_mut_ptr().offset(idx as isize),
+        slice.len() - idx - 1
+    );
+    ret
+}
diff --git a/src/libcollections/btree/search.rs b/src/libcollections/btree/search.rs
new file mode 100644 (file)
index 0000000..c94b570
--- /dev/null
@@ -0,0 +1,76 @@
+// 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 core::cmp::Ordering;
+
+use borrow::Borrow;
+
+use super::node::{Handle, NodeRef, marker};
+
+use super::node::ForceResult::*;
+use self::SearchResult::*;
+
+pub enum SearchResult<BorrowType, K, V, FoundType, GoDownType> {
+    Found(Handle<NodeRef<BorrowType, K, V, FoundType>, marker::KV>),
+    GoDown(Handle<NodeRef<BorrowType, K, V, GoDownType>, marker::Edge>)
+}
+
+pub fn search_tree<BorrowType, K, V, Q: ?Sized>(
+    mut node: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
+    key: &Q
+) -> SearchResult<BorrowType, K, V, marker::LeafOrInternal, marker::Leaf>
+        where Q: Ord, K: Borrow<Q> {
+
+    loop {
+        match search_node(node, key) {
+            Found(handle) => return Found(handle),
+            GoDown(handle) => match handle.force() {
+                Leaf(leaf) => return GoDown(leaf),
+                Internal(internal) => {
+                    node = internal.descend();
+                    continue;
+                }
+            }
+        }
+    }
+}
+
+pub fn search_node<BorrowType, K, V, Type, Q: ?Sized>(
+    node: NodeRef<BorrowType, K, V, Type>,
+    key: &Q
+) -> SearchResult<BorrowType, K, V, Type, Type>
+        where Q: Ord, K: Borrow<Q> {
+
+    match search_linear(&node, key) {
+        (idx, true) => Found(
+            Handle::new_kv(node, idx)
+        ),
+        (idx, false) => SearchResult::GoDown(
+            Handle::new_edge(node, idx)
+        )
+    }
+}
+
+fn search_linear<BorrowType, K, V, Type, Q: ?Sized>(
+    node: &NodeRef<BorrowType, K, V, Type>,
+    key: &Q
+) -> (usize, bool)
+        where Q: Ord, K: Borrow<Q> {
+
+    for (i, k) in node.keys().iter().enumerate() {
+        match key.cmp(k.borrow()) {
+            Ordering::Greater => {},
+            Ordering::Equal => return (i, true),
+            Ordering::Less => return (i, false)
+        }
+    }
+    (node.keys().len(), false)
+}
+
index 8d741f9e34a1418503b1b3a11c4c38453e516816..91fc8d8217f53b487921e33e45e56f6e39c61124 100644 (file)
@@ -14,7 +14,7 @@
 use core::cmp::Ordering::{self, Less, Greater, Equal};
 use core::fmt::Debug;
 use core::fmt;
-use core::iter::{Peekable, Map, FromIterator};
+use core::iter::{Peekable, FromIterator};
 use core::ops::{BitOr, BitAnd, BitXor, Sub};
 
 use borrow::Borrow;
@@ -26,12 +26,17 @@ use Bound;
 
 /// A set based on a B-Tree.
 ///
-/// See BTreeMap's documentation for a detailed discussion of this collection's performance
+/// See [`BTreeMap`]'s documentation for a detailed discussion of this collection's performance
 /// benefits and drawbacks.
 ///
 /// It is a logic error for an item to be modified in such a way that the item's ordering relative
-/// to any other item, as determined by the `Ord` trait, changes while it is in the set. This is
-/// normally only possible through `Cell`, `RefCell`, global state, I/O, or unsafe code.
+/// to any other item, as determined by the [`Ord`] trait, changes while it is in the set. This is
+/// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code.
+///
+/// [`BTreeMap`]: ../struct.BTreeMap.html
+/// [`Ord`]: ../../core/cmp/trait.Ord.html
+/// [`Cell`]: ../../std/cell/struct.Cell.html
+/// [`RefCell`]: ../../std/cell/struct.RefCell.html
 #[derive(Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct BTreeSet<T> {
@@ -47,12 +52,12 @@ pub struct Iter<'a, T: 'a> {
 /// An owning iterator over a BTreeSet's items.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct IntoIter<T> {
-    iter: Map<::btree_map::IntoIter<T, ()>, fn((T, ())) -> T>,
+    iter: ::btree_map::IntoIter<T, ()>,
 }
 
 /// An iterator over a sub-range of BTreeSet's items.
 pub struct Range<'a, T: 'a> {
-    iter: Map<::btree_map::Range<'a, T, ()>, fn((&'a T, &'a ())) -> &'a T>,
+    iter: ::btree_map::Range<'a, T, ()>,
 }
 
 /// A lazy iterator producing elements in the set difference (in-order).
@@ -98,18 +103,6 @@ impl<T: Ord> BTreeSet<T> {
     pub fn new() -> BTreeSet<T> {
         BTreeSet { map: BTreeMap::new() }
     }
-
-    /// Makes a new BTreeSet with the given B.
-    ///
-    /// B cannot be less than 2.
-    #[unstable(feature = "btree_b",
-               reason = "probably want this to be on the type, eventually",
-               issue = "27795")]
-    #[rustc_deprecated(since = "1.4.0", reason = "niche API")]
-    #[allow(deprecated)]
-    pub fn with_b(b: usize) -> BTreeSet<T> {
-        BTreeSet { map: BTreeMap::with_b(b) }
-    }
 }
 
 impl<T> BTreeSet<T> {
@@ -161,18 +154,13 @@ impl<T: Ord> BTreeSet<T> {
     #[unstable(feature = "btree_range",
                reason = "matches collection reform specification, waiting for dust to settle",
                issue = "27787")]
-    pub fn range<'a, Min: ?Sized + Ord = T, Max: ?Sized + Ord = T>(&'a self,
-                                                                   min: Bound<&Min>,
-                                                                   max: Bound<&Max>)
-                                                                   -> Range<'a, T>
+    pub fn range<'a, Min: ?Sized + Ord, Max: ?Sized + Ord>(&'a self,
+                                                           min: Bound<&Min>,
+                                                           max: Bound<&Max>)
+                                                           -> Range<'a, T>
         where T: Borrow<Min> + Borrow<Max>
     {
-        fn first<A, B>((a, _): (A, B)) -> A {
-            a
-        }
-        let first: fn((&'a T, &'a ())) -> &'a T = first; // coerce to fn pointer
-
-        Range { iter: self.map.range(min, max).map(first) }
+        Range { iter: self.map.range(min, max) }
     }
 }
 
@@ -460,7 +448,7 @@ impl<T: Ord> BTreeSet<T> {
     ///
     /// If the set did not have a value present, `true` is returned.
     ///
-    /// If the set did have this key present, that value is returned, and the
+    /// If the set did have this key present, `false` is returned, and the
     /// entry is not updated. See the [module-level documentation] for more.
     ///
     /// [module-level documentation]: index.html#insert-and-complex-keys
@@ -555,12 +543,7 @@ impl<T> IntoIterator for BTreeSet<T> {
     /// assert_eq!(v, [1, 2, 3, 4]);
     /// ```
     fn into_iter(self) -> IntoIter<T> {
-        fn first<A, B>((a, _): (A, B)) -> A {
-            a
-        }
-        let first: fn((T, ())) -> T = first; // coerce to fn pointer
-
-        IntoIter { iter: self.map.into_iter().map(first) }
+        IntoIter { iter: self.map.into_iter() }
     }
 }
 
@@ -728,7 +711,7 @@ impl<T> Iterator for IntoIter<T> {
     type Item = T;
 
     fn next(&mut self) -> Option<T> {
-        self.iter.next()
+        self.iter.next().map(|(k, _)| k)
     }
     fn size_hint(&self) -> (usize, Option<usize>) {
         self.iter.size_hint()
@@ -737,7 +720,7 @@ impl<T> Iterator for IntoIter<T> {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> DoubleEndedIterator for IntoIter<T> {
     fn next_back(&mut self) -> Option<T> {
-        self.iter.next_back()
+        self.iter.next_back().map(|(k, _)| k)
     }
 }
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -753,12 +736,12 @@ impl<'a, T> Iterator for Range<'a, T> {
     type Item = &'a T;
 
     fn next(&mut self) -> Option<&'a T> {
-        self.iter.next()
+        self.iter.next().map(|(k, _)| k)
     }
 }
 impl<'a, T> DoubleEndedIterator for Range<'a, T> {
     fn next_back(&mut self) -> Option<&'a T> {
-        self.iter.next_back()
+        self.iter.next_back().map(|(k, _)| k)
     }
 }
 
index 717c1d13af4bd1152c66d036833af68c8a8b346c..8b8ccd526c90f854c7fd0c8b360eda4ebeb7bf14 100644 (file)
@@ -81,6 +81,7 @@ pub trait CLike {
     fn from_usize(usize) -> Self;
 }
 
+#[allow(deprecated)]
 fn bit<E: CLike>(e: &E) -> usize {
     use core::usize;
     let value = e.to_usize();
index 1450b8efb0fb28bab46869444afb6f7756ffdfd2..97b01a607f5e602a06ea3d30f9b373f3c9c8fec3 100644 (file)
 //! The fill character is provided normally in conjunction with the `width`
 //! parameter. This indicates that if the value being formatted is smaller than
 //! `width` some extra characters will be printed around it. The extra
-//! characters are specified by `fill`, and the alignment can be one of two
-//! options:
+//! characters are specified by `fill`, and the alignment can be one of the
+//! following options:
 //!
 //! * `<` - the argument is left-aligned in `width` columns
 //! * `^` - the argument is center-aligned in `width` columns
@@ -490,7 +490,11 @@ pub use core::fmt::{LowerExp, UpperExp};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::fmt::Error;
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use core::fmt::{ArgumentV1, Arguments, write, radix, Radix, RadixFmt};
+pub use core::fmt::{ArgumentV1, Arguments, write};
+#[unstable(feature = "fmt_radix", issue = "27728")]
+#[rustc_deprecated(since = "1.7.0", reason = "not used enough to stabilize")]
+#[allow(deprecated)]
+pub use core::fmt::{radix, Radix, RadixFmt};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
 
index a7797d4b0d0476a93f0bd1c20772fa05d3b3bb1e..6077a4c01045b5b9842f11694e3055f5e09dce88 100644 (file)
 
 //! Collection types.
 //!
-//! See [std::collections](../std/collections) for a detailed discussion of
+//! See [std::collections](../std/collections/index.html) for a detailed discussion of
 //! collections in Rust.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "collections"]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
 #![unstable(feature = "collections",
             reason = "library is unlikely to be stabilized with the current \
 #![allow(trivial_casts)]
 #![cfg_attr(test, allow(deprecated))] // rand
 
-// SNAP 1af31d4
-#![allow(unused_features)]
-// SNAP 1af31d4
-#![allow(unused_attributes)]
-
-#![cfg_attr(stage0, feature(rustc_attrs))]
-#![cfg_attr(stage0, allow(unused_attributes))]
 #![feature(alloc)]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
 #![feature(core_intrinsics)]
+#![feature(decode_utf16)]
+#![feature(drop_in_place)]
+#![feature(dropck_parametricity)]
 #![feature(fmt_internals)]
 #![feature(fmt_radix)]
 #![feature(heap_api)]
 #![feature(iter_arith)]
 #![feature(iter_arith)]
 #![feature(lang_items)]
+#![feature(nonzero)]
 #![feature(num_bits_bytes)]
-#![feature(oom)]
 #![feature(pattern)]
-#![feature(ptr_as_ref)]
-#![feature(ref_slice)]
+#![feature(shared)]
 #![feature(slice_bytes)]
 #![feature(slice_patterns)]
 #![feature(staged_api)]
 #![feature(unboxed_closures)]
 #![feature(unicode)]
 #![feature(unique)]
-#![feature(dropck_parametricity)]
-#![feature(unsafe_no_drop_flag, filling_drop)]
-#![feature(decode_utf16)]
-#![feature(drop_in_place)]
-#![feature(clone_from_slice)]
+#![feature(unsafe_no_drop_flag)]
 #![cfg_attr(test, feature(clone_from_slice, rand, test))]
 
-#![cfg_attr(stage0, feature(no_std))]
 #![no_std]
 
 extern crate rustc_unicode;
index 631857f8e3c56e472d7cf6e706f04ad9631c4a45..1bd5a83d4370860223f13966ab449a006369b14f 100644 (file)
@@ -27,7 +27,7 @@ use core::fmt;
 use core::hash::{Hasher, Hash};
 use core::iter::FromIterator;
 use core::mem;
-use core::ptr;
+use core::ptr::Shared;
 
 /// A doubly-linked list.
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -40,7 +40,7 @@ pub struct LinkedList<T> {
 type Link<T> = Option<Box<Node<T>>>;
 
 struct Rawlink<T> {
-    p: *mut T,
+    p: Option<Shared<T>>,
 }
 
 impl<T> Copy for Rawlink<T> {}
@@ -93,12 +93,12 @@ pub struct IntoIter<T> {
 impl<T> Rawlink<T> {
     /// Like Option::None for Rawlink
     fn none() -> Rawlink<T> {
-        Rawlink { p: ptr::null_mut() }
+        Rawlink { p: None }
     }
 
     /// Like Option::Some for Rawlink
     fn some(n: &mut T) -> Rawlink<T> {
-        Rawlink { p: n }
+        unsafe { Rawlink { p: Some(Shared::new(n)) } }
     }
 
     /// Convert the `Rawlink` into an Option value
@@ -108,7 +108,7 @@ impl<T> Rawlink<T> {
     /// - Dereference of raw pointer.
     /// - Returns reference of arbitrary lifetime.
     unsafe fn resolve<'a>(&self) -> Option<&'a T> {
-        self.p.as_ref()
+        self.p.map(|p| &**p)
     }
 
     /// Convert the `Rawlink` into an Option value
@@ -118,7 +118,7 @@ impl<T> Rawlink<T> {
     /// - Dereference of raw pointer.
     /// - Returns reference of arbitrary lifetime.
     unsafe fn resolve_mut<'a>(&mut self) -> Option<&'a mut T> {
-        self.p.as_mut()
+        self.p.map(|p| &mut **p)
     }
 
     /// Return the `Rawlink` and replace with `Rawlink::none()`
@@ -984,6 +984,14 @@ impl<A: Hash> Hash for LinkedList<A> {
     }
 }
 
+// Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters.
+#[allow(dead_code)]
+fn assert_covariance() {
+    fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> { x }
+    fn b<'i, 'a>(x: Iter<'i, &'static str>) -> Iter<'i, &'a str> { x }
+    fn c<'a>(x: IntoIter<&'static str>) -> IntoIter<&'a str> { x }
+}
+
 #[cfg(test)]
 mod tests {
     use std::clone::Clone;
index c70aa67366b342edd2fca8a398b86a483306ed45..afcd779ddf19f92db8c6b575b2619905675893a3 100644 (file)
@@ -8,8 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![unstable(feature = "collections_range", reason = "was just added",
-            issue = "27711")]
+#![unstable(feature = "collections_range",
+            reason = "waiting for dust to settle on inclusive ranges",
+            issue = "30877")]
 
 //! Range syntax.
 
index 6342ae5c816fd2410018c0a0085f05837ac59d14..8b4497e6f037e2ee3216073e1f952f73c3696e4c 100644 (file)
@@ -110,9 +110,9 @@ pub use core::slice::{Iter, IterMut};
 pub use core::slice::{SplitMut, ChunksMut, Split};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::slice::{SplitN, RSplitN, SplitNMut, RSplitNMut};
-#[unstable(feature = "ref_slice", issue = "27774")]
+#[unstable(feature = "slice_bytes", issue = "27740")]
 #[allow(deprecated)]
-pub use core::slice::{bytes, mut_ref_slice, ref_slice};
+pub use core::slice::bytes;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::slice::{from_raw_parts, from_raw_parts_mut};
 
@@ -788,15 +788,12 @@ impl<T> [T] {
     /// # Examples
     ///
     /// ```rust
-    /// #![feature(slice_sort_by_key)]
-    ///
     /// let mut v = [-5i32, 4, 1, -3, 2];
     ///
     /// v.sort_by_key(|k| k.abs());
     /// assert!(v == [1, 2, -3, 4, -5]);
     /// ```
-    #[unstable(feature = "slice_sort_by_key", reason = "recently added",
-               issue = "27724")]
+    #[stable(feature = "slice_sort_by_key", since = "1.7.0")]
     #[inline]
     pub fn sort_by_key<B, F>(&mut self, mut f: F)
         where F: FnMut(&T) -> B, B: Ord
@@ -829,29 +826,25 @@ impl<T> [T] {
         merge_sort(self, compare)
     }
 
-    /// Copies as many elements from `src` as it can into `self` (the
-    /// shorter of `self.len()` and `src.len()`). Returns the number
-    /// of elements copied.
+    /// Copies the elements from `src` into `self`.
+    ///
+    /// The length of this slice must be the same as the slice passed in.
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if the two slices have different lengths.
     ///
     /// # Example
     ///
     /// ```rust
-    /// #![feature(clone_from_slice)]
-    ///
     /// let mut dst = [0, 0, 0];
-    /// let src = [1, 2];
+    /// let src = [1, 2, 3];
     ///
-    /// assert!(dst.clone_from_slice(&src) == 2);
-    /// assert!(dst == [1, 2, 0]);
-    ///
-    /// let src2 = [3, 4, 5, 6];
-    /// assert!(dst.clone_from_slice(&src2) == 3);
-    /// assert!(dst == [3, 4, 5]);
+    /// dst.clone_from_slice(&src);
+    /// assert!(dst == [1, 2, 3]);
     /// ```
-    #[unstable(feature = "clone_from_slice", issue = "27750")]
-    pub fn clone_from_slice(&mut self, src: &[T]) -> usize
-        where T: Clone
-    {
+    #[stable(feature = "clone_from_slice", since = "1.7.0")]
+    pub fn clone_from_slice(&mut self, src: &[T]) where T: Clone {
         core_slice::SliceExt::clone_from_slice(self, src)
     }
 
@@ -909,15 +902,6 @@ pub trait SliceConcatExt<T: ?Sized> {
     #[stable(feature = "rename_connect_to_join", since = "1.3.0")]
     fn join(&self, sep: &T) -> Self::Output;
 
-    /// Flattens a slice of `T` into a single value `Self::Output`, placing a
-    /// given separator between each.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # #![allow(deprecated)]
-    /// assert_eq!(["hello", "world"].connect(" "), "hello world");
-    /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_deprecated(since = "1.3.0", reason = "renamed to join")]
     fn connect(&self, sep: &T) -> Self::Output;
index be3f93992d9eff8eb3757eb73c306cc2904f9385..9ce1a111cf83a18f36f31dd8543450abb5237101 100644 (file)
@@ -302,10 +302,10 @@ impl str {
     /// # Safety
     ///
     /// Callers of this function are responsible that three preconditions are
-    /// satisifed:
+    /// satisfied:
     ///
     /// * `begin` must come before `end`.
-    /// * `begin` and `end` must be bye positions within the string slice.
+    /// * `begin` and `end` must be byte positions within the string slice.
     /// * `begin` and `end` must lie on UTF-8 sequence boundaries.
     ///
     /// # Examples
@@ -345,10 +345,10 @@ impl str {
     /// # Safety
     ///
     /// Callers of this function are responsible that three preconditions are
-    /// satisifed:
+    /// satisfied:
     ///
     /// * `begin` must come before `end`.
-    /// * `begin` and `end` must be bye positions within the string slice.
+    /// * `begin` and `end` must be byte positions within the string slice.
     /// * `begin` and `end` must lie on UTF-8 sequence boundaries.
     #[stable(feature = "str_slice_mut", since = "1.5.0")]
     #[inline]
@@ -640,7 +640,7 @@ impl str {
     /// Value, and may not match your idea of what a 'character' is. Iteration
     /// over grapheme clusters may be what you actually want.
     ///
-    /// [`char`]: ../primitive.char.html
+    /// [`char`]: primitive.char.html
     ///
     /// # Examples
     ///
@@ -857,9 +857,10 @@ impl str {
         Utf16Units { encoder: Utf16Encoder::new(self[..].chars()) }
     }
 
-    /// Returns `true` if the given `&str` is a sub-slice of this string slice.
+    /// Returns `true` if the given pattern matches a sub-slice of
+    /// this string slice.
     ///
-    /// Returns `false` if it's not.
+    /// Returns `false` if it does not.
     ///
     /// # Examples
     ///
@@ -876,9 +877,10 @@ impl str {
         core_str::StrExt::contains(self, pat)
     }
 
-    /// Returns `true` if the given `&str` is a prefix of this string slice.
+    /// Returns `true` if the given pattern matches a prefix of this
+    /// string slice.
     ///
-    /// Returns `false` if it's not.
+    /// Returns `false` if it does not.
     ///
     /// # Examples
     ///
@@ -895,9 +897,10 @@ impl str {
         core_str::StrExt::starts_with(self, pat)
     }
 
-    /// Returns `true` if the given `&str` is a suffix of this string slice.
+    /// Returns `true` if the given pattern matches a suffix of this
+    /// string slice.
     ///
-    /// Returns `false` if not.
+    /// Returns `false` if it does not.
     ///
     /// # Examples
     ///
@@ -1381,7 +1384,7 @@ impl str {
     ///
     /// For iterating from the front, the [`matches()`] method can be used.
     ///
-    /// [`matches`]: #method.matches
+    /// [`matches()`]: #method.matches
     ///
     /// # Examples
     ///
@@ -1551,7 +1554,7 @@ impl str {
     /// The pattern can be a `&str`, [`char`], or a closure that determines
     /// if a character matches.
     ///
-    /// [`char`]: primtive.char.html
+    /// [`char`]: primitive.char.html
     ///
     /// # Examples
     ///
@@ -1643,7 +1646,7 @@ impl str {
     ///
     /// `parse()` can parse any type that implements the [`FromStr`] trait.
     ///
-    /// [`FromStr`]: trait.FromStr.html
+    /// [`FromStr`]: str/trait.FromStr.html
     ///
     /// # Failure
     ///
@@ -1681,11 +1684,11 @@ impl str {
         core_str::StrExt::parse(self)
     }
 
-    /// Replaces all occurrences of one string with another.
+    /// Replaces all matches of a pattern with another string.
     ///
     /// `replace` creates a new [`String`], and copies the data from this string slice into it.
-    /// While doing so, it attempts to find a sub-`&str`. If it finds it, it replaces it with
-    /// the replacement string slice.
+    /// While doing so, it attempts to find matches of a pattern. If it finds any, it
+    /// replaces them with the replacement string slice.
     ///
     /// [`String`]: string/struct.String.html
     ///
@@ -1699,14 +1702,14 @@ impl str {
     /// assert_eq!("this is new", s.replace("old", "new"));
     /// ```
     ///
-    /// When a `&str` isn't found:
+    /// When the pattern doesn't match:
     ///
     /// ```
     /// let s = "this is old";
     /// assert_eq!(s, s.replace("cookie monster", "little lamb"));
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn replace(&self, from: &str, to: &str) -> String {
+    pub fn replace<'a, P: Pattern<'a>>(&'a self, from: P, to: &str) -> String {
         let mut result = String::new();
         let mut last_end = 0;
         for (start, part) in self.match_indices(from) {
index a3c6918293477b276532d3e75b4cfb84206dd934..ad9c770a5a5cafa037fbbe2e29a6a214b9bc342b 100644 (file)
 //!
 //! [`String`]: struct.String.html
 //! [`ToString`]: trait.ToString.html
+//!
+//! # Examples
+//!
+//! There are multiple ways to create a new `String` from a string literal:
+//!
+//! ```rust
+//! let s = "Hello".to_string();
+//!
+//! let s = String::from("world");
+//! let s: String = "also this".into();
+//! ```
+//!
+//! You can create a new `String` from an existing one by concatenating with
+//! `+`:
+//!
+//! ```rust
+//! let s = "Hello".to_string();
+//!
+//! let message = s + " world!";
+//! ```
+//!
+//! If you have a vector of valid UTF-8 bytes, you can make a `String` out of
+//! it. You can do the reverse too.
+//!
+//! ```rust
+//! let sparkle_heart = vec![240, 159, 146, 150];
+//!
+//! // We know these bytes are valid, so we'll use `unwrap()`.
+//! let sparkle_heart = String::from_utf8(sparkle_heart).unwrap();
+//!
+//! assert_eq!("💖", sparkle_heart);
+//!
+//! let bytes = sparkle_heart.into_bytes();
+//!
+//! assert_eq!(bytes, [240, 159, 146, 150]);
+//! ```
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
@@ -23,13 +59,14 @@ use core::fmt;
 use core::hash;
 use core::iter::FromIterator;
 use core::mem;
-use core::ops::{self, Deref, Add, Index};
+use core::ops::{self, Add};
 use core::ptr;
 use core::slice;
 use core::str::pattern::Pattern;
 use rustc_unicode::char::{decode_utf16, REPLACEMENT_CHARACTER};
 use rustc_unicode::str as unicode_str;
 
+#[allow(deprecated)]
 use borrow::{Cow, IntoCow};
 use range::RangeArgument;
 use str::{self, FromStr, Utf8Error, Chars};
@@ -62,6 +99,7 @@ use boxed::Box;
 /// hello.push_str("orld!");
 /// ```
 ///
+/// [`char`]: ../primitive.char.html
 /// [`push()`]: #method.push
 /// [`push_str()`]: #method.push_str
 ///
@@ -163,8 +201,8 @@ use boxed::Box;
 /// ```
 ///
 /// [`as_ptr()`]: #method.as_ptr
-/// [`len()`]: # method.len
-/// [`capacity()`]: # method.capacity
+/// [`len()`]: #method.len
+/// [`capacity()`]: #method.capacity
 ///
 /// If a `String` has enough capacity, adding elements to it will not
 /// re-allocate. For example, consider this program:
@@ -291,13 +329,23 @@ pub struct FromUtf8Error {
 pub struct FromUtf16Error(());
 
 impl String {
-    /// Creates a new string buffer initialized with the empty string.
+    /// Creates a new empty `String`.
+    ///
+    /// Given that the `String` is empty, this will not allocate any initial
+    /// buffer. While that means that this initial operation is very
+    /// inexpensive, but may cause excessive allocation later, when you add
+    /// data. If you have an idea of how much data the `String` will hold,
+    /// consider the [`with_capacity()`] method to prevent excessive
+    /// re-allocation.
+    ///
+    /// [`with_capacity()`]: #method.with_capacity
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
-    /// # #![allow(unused_mut)]
-    /// let mut s = String::new();
+    /// let s = String::new();
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -305,12 +353,26 @@ impl String {
         String { vec: Vec::new() }
     }
 
-    /// Creates a new string buffer with the given capacity.
-    /// The string will be able to hold exactly `capacity` bytes without
-    /// reallocating. If `capacity` is 0, the string will not allocate.
+    /// Creates a new empty `String` with a particular capacity.
+    ///
+    /// `String`s have an internal buffer to hold their data. The capacity is
+    /// the length of that buffer, and can be queried with the [`capacity()`]
+    /// method. This method creates an empty `String`, but one with an initial
+    /// buffer that can hold `capacity` bytes. This is useful when you may be
+    /// appending a bunch of data to the `String`, reducing the number of
+    /// reallocations it needs to do.
+    ///
+    /// [`capacity()`]: #method.capacity
+    ///
+    /// If the given capacity is `0`, no allocation will occur, and this method
+    /// is identical to the [`new()`] method.
+    ///
+    /// [`new()`]: #method.new
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let mut s = String::with_capacity(10);
     ///
@@ -346,26 +408,30 @@ impl String {
 
     /// Converts a vector of bytes to a `String`.
     ///
-    /// A string slice (`&str`) is made of bytes (`u8`), and a vector of bytes
-    /// (`Vec<u8>`) is made of bytes, so this function converts between the
+    /// A string slice ([`&str`]) is made of bytes ([`u8`]), and a vector of bytes
+    /// ([`Vec<u8>`]) is made of bytes, so this function converts between the
     /// two. Not all byte slices are valid `String`s, however: `String`
     /// requires that it is valid UTF-8. `from_utf8()` checks to ensure that
     /// the bytes are valid UTF-8, and then does the conversion.
     ///
+    /// [`&str`]: ../primitive.str.html
+    /// [`u8`]: ../primitive.u8.html
+    /// [`Vec<u8>`]: ../vec/struct.Vec.html
+    ///
     /// If you are sure that the byte slice is valid UTF-8, and you don't want
     /// to incur the overhead of the validity check, there is an unsafe version
-    /// of this function, [`from_utf8_unchecked()`][fromutf8], which has the
-    /// same behavior but skips the check.
+    /// of this function, [`from_utf8_unchecked()`], which has the same behavior
+    /// but skips the check.
     ///
-    /// [fromutf8]: struct.String.html#method.from_utf8_unchecked
+    /// [`from_utf8_unchecked()`]: struct.String.html#method.from_utf8_unchecked
     ///
     /// This method will take care to not copy the vector, for efficiency's
     /// sake.
     ///
     /// If you need a `&str` instead of a `String`, consider
-    /// [`str::from_utf8()`][str].
+    /// [`str::from_utf8()`].
     ///
-    /// [str]: ../str/fn.from_utf8.html
+    /// [`str::from_utf8()`]: ../str/fn.from_utf8.html
     ///
     /// # Failure
     ///
@@ -395,10 +461,10 @@ impl String {
     /// assert!(String::from_utf8(sparkle_heart).is_err());
     /// ```
     ///
-    /// See the docs for [`FromUtf8Error`][error] for more details on what you
-    /// can do with this error.
+    /// See the docs for [`FromUtf8Error`] for more details on what you can do
+    /// with this error.
     ///
-    /// [error]: struct.FromUtf8Error.html
+    /// [`FromUtf8Error`]: struct.FromUtf8Error.html
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn from_utf8(vec: Vec<u8>) -> Result<String, FromUtf8Error> {
@@ -415,24 +481,28 @@ impl String {
 
     /// Converts a slice of bytes to a `String`, including invalid characters.
     ///
-    /// A string slice (`&str`) is made of bytes (`u8`), and a slice of bytes
-    /// (`&[u8]`) is made of bytes, so this function converts between the two.
-    /// Not all byte slices are valid string slices, however: `&str` requires
-    /// that it is valid UTF-8. During this conversion, `from_utf8_lossy()`
-    /// will replace any invalid UTF-8 sequences with
+    /// A string slice ([`&str`]) is made of bytes ([`u8`]), and a slice of
+    /// bytes ([`&[u8]`][byteslice]) is made of bytes, so this function converts between
+    /// the two. Not all byte slices are valid string slices, however: [`&str`]
+    /// requires that it is valid UTF-8. During this conversion,
+    /// `from_utf8_lossy()` will replace any invalid UTF-8 sequences with
     /// `U+FFFD REPLACEMENT CHARACTER`, which looks like this: �
     ///
+    /// [`&str`]: ../primitive.str.html
+    /// [`u8`]: ../primitive.u8.html
+    /// [byteslice]: ../primitive.slice.html
+    ///
     /// If you are sure that the byte slice is valid UTF-8, and you don't want
     /// to incur the overhead of the conversion, there is an unsafe version
-    /// of this function, [`from_utf8_unchecked()`][fromutf8], which has the
-    /// same behavior but skips the checks.
+    /// of this function, [`from_utf8_unchecked()`], which has the same behavior
+    /// but skips the checks.
     ///
-    /// [fromutf8]: struct.String.html#method.from_utf8_unchecked
+    /// [`from_utf8_unchecked()`]: struct.String.html#method.from_utf8_unchecked
     ///
-    /// If you need a `&str` instead of a `String`, consider
-    /// [`str::from_utf8()`][str].
+    /// If you need a [`&str`] instead of a `String`, consider
+    /// [`str::from_utf8()`].
     ///
-    /// [str]: ../str/fn.from_utf8.html
+    /// [`str::from_utf8()`]: ../str/fn.from_utf8.html
     ///
     /// # Examples
     ///
@@ -576,12 +646,14 @@ impl String {
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// // 𝄞music
     /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075,
     ///           0x0073, 0x0069, 0x0063];
-    /// assert_eq!(String::from_utf16(v).unwrap(),
-    ///            "𝄞music".to_string());
+    /// assert_eq!(String::from("𝄞music"),
+    ///            String::from_utf16(v).unwrap());
     ///
     /// // 𝄞mu<invalid>ic
     /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075,
@@ -598,14 +670,16 @@ impl String {
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// // 𝄞mus<invalid>ic<invalid>
     /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075,
     ///           0x0073, 0xDD1E, 0x0069, 0x0063,
     ///           0xD834];
     ///
-    /// assert_eq!(String::from_utf16_lossy(v),
-    ///            "𝄞mus\u{FFFD}ic\u{FFFD}".to_string());
+    /// assert_eq!(String::from("𝄞mus\u{FFFD}ic\u{FFFD}"),
+    ///            String::from_utf16_lossy(v));
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -617,13 +691,37 @@ impl String {
     ///
     /// # Safety
     ///
-    /// This is _very_ unsafe because:
+    /// This is highly unsafe, due to the number of invariants that aren't
+    /// checked:
+    ///
+    /// * The memory at `ptr` needs to have been previously allocated by the
+    ///   same allocator the standard library uses.
+    /// * `length` needs to be less than or equal to `capacity`.
+    /// * `capacity` needs to be the correct value.
+    ///
+    /// Violating these may cause problems like corrupting the allocator's
+    /// internal datastructures.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// use std::mem;
+    ///
+    /// unsafe {
+    ///     let s = String::from("hello");
+    ///     let ptr = s.as_ptr();
+    ///     let len = s.len();
+    ///     let capacity = s.capacity();
+    ///
+    ///     mem::forget(s);
+    ///
+    ///     let s = String::from_raw_parts(ptr as *mut _, len, capacity);
     ///
-    /// * We call `Vec::from_raw_parts` to get a `Vec<u8>`. Therefore, this
-    ///   function inherits all of its unsafety, see [its
-    ///   documentation](../vec/struct.Vec.html#method.from_raw_parts)
-    ///   for the invariants it expects, they also apply to this function.
-    /// * We assume that the `Vec` contains valid UTF-8.
+    ///     assert_eq!(String::from("hello"), s);
+    /// }
+    /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String {
@@ -633,15 +731,16 @@ impl String {
     /// Converts a vector of bytes to a `String` without checking that the
     /// string contains valid UTF-8.
     ///
-    /// See the safe version, [`from_utf8()`][fromutf8], for more.
+    /// See the safe version, [`from_utf8()`], for more details.
     ///
-    /// [fromutf8]: struct.String.html#method.from_utf8
+    /// [`from_utf8()`]: struct.String.html#method.from_utf8
     ///
     /// # Safety
     ///
-    /// This function is unsafe because it does not check that the bytes passed to
-    /// it are valid UTF-8. If this constraint is violated, undefined behavior
-    /// results, as the rest of Rust assumes that `String`s are valid UTF-8.
+    /// This function is unsafe because it does not check that the bytes passed
+    /// to it are valid UTF-8. If this constraint is violated, it may cause
+    /// memory unsafety issues with future users of the `String`, as the rest of
+    /// the standard library assumes that `String`s are valid UTF-8.
     ///
     /// # Examples
     ///
@@ -663,14 +762,19 @@ impl String {
         String { vec: bytes }
     }
 
-    /// Returns the underlying byte buffer, encoded as UTF-8.
+    /// Converts a `String` into a byte vector.
+    ///
+    /// This consumes the `String`, so we do not need to copy its contents.
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let s = String::from("hello");
     /// let bytes = s.into_bytes();
-    /// assert_eq!(bytes, [104, 101, 108, 108, 111]);
+    ///
+    /// assert_eq!(&[104, 101, 108, 108, 111][..], &bytes[..]);
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -680,21 +784,30 @@ impl String {
 
     /// Extracts a string slice containing the entire string.
     #[inline]
-    #[unstable(feature = "convert",
-               reason = "waiting on RFC revision",
-               issue = "27729")]
+    #[stable(feature = "string_as_str", since = "1.7.0")]
     pub fn as_str(&self) -> &str {
         self
     }
 
-    /// Pushes the given string onto this string buffer.
+    /// Extracts a string slice containing the entire string.
+    #[inline]
+    #[stable(feature = "string_as_str", since = "1.7.0")]
+    pub fn as_mut_str(&mut self) -> &mut str {
+        self
+    }
+
+    /// Appends a given string slice onto the end of this `String`.
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let mut s = String::from("foo");
+    ///
     /// s.push_str("bar");
-    /// assert_eq!(s, "foobar");
+    ///
+    /// assert_eq!("foobar", s);
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -702,13 +815,15 @@ impl String {
         self.vec.extend_from_slice(string.as_bytes())
     }
 
-    /// Returns the number of bytes that this string buffer can hold without
-    /// reallocating.
+    /// Returns this `String`'s capacity, in bytes.
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let s = String::with_capacity(10);
+    ///
     /// assert!(s.capacity() >= 10);
     /// ```
     #[inline]
@@ -717,9 +832,16 @@ impl String {
         self.vec.capacity()
     }
 
-    /// Reserves capacity for at least `additional` more bytes to be inserted
-    /// in the given `String`. The collection may reserve more space to avoid
-    /// frequent reallocations.
+    /// Ensures that this `String`'s capacity is at least `additional` bytes
+    /// larger than its length.
+    ///
+    /// The capacity may be increased by more than `additional` bytes if it
+    /// chooses, to prevent frequent reallocations.
+    ///
+    /// If you do not want this "at least" behavior, see the [`reserve_exact()`]
+    /// method.
+    ///
+    /// [`reserve_exact()`]: #method.reserve_exact
     ///
     /// # Panics
     ///
@@ -727,24 +849,46 @@ impl String {
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let mut s = String::new();
+    ///
     /// s.reserve(10);
+    ///
     /// assert!(s.capacity() >= 10);
     /// ```
+    ///
+    /// This may not actually increase the capacity:
+    ///
+    /// ```
+    /// let mut s = String::with_capacity(10);
+    /// s.push('a');
+    /// s.push('b');
+    ///
+    /// // s now has a length of 2 and a capacity of 10
+    /// assert_eq!(2, s.len());
+    /// assert_eq!(10, s.capacity());
+    ///
+    /// // Since we already have an extra 8 capacity, calling this...
+    /// s.reserve(8);
+    ///
+    /// // ... doesn't actually increase.
+    /// assert_eq!(10, s.capacity());
+    /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn reserve(&mut self, additional: usize) {
         self.vec.reserve(additional)
     }
 
-    /// Reserves the minimum capacity for exactly `additional` more bytes to be
-    /// inserted in the given `String`. Does nothing if the capacity is already
-    /// sufficient.
+    /// Ensures that this `String`'s capacity is `additional` bytes
+    /// larger than its length.
     ///
-    /// Note that the allocator may give the collection more space than it
-    /// requests. Therefore capacity can not be relied upon to be precisely
-    /// minimal. Prefer `reserve` if future insertions are expected.
+    /// Consider using the [`reserve()`] method unless you absolutely know
+    /// better than the allocator.
+    ///
+    /// [`reserve()`]: #method.reserve
     ///
     /// # Panics
     ///
@@ -752,27 +896,53 @@ impl String {
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let mut s = String::new();
+    ///
     /// s.reserve_exact(10);
+    ///
     /// assert!(s.capacity() >= 10);
     /// ```
+    ///
+    /// This may not actually increase the capacity:
+    ///
+    /// ```
+    /// let mut s = String::with_capacity(10);
+    /// s.push('a');
+    /// s.push('b');
+    ///
+    /// // s now has a length of 2 and a capacity of 10
+    /// assert_eq!(2, s.len());
+    /// assert_eq!(10, s.capacity());
+    ///
+    /// // Since we already have an extra 8 capacity, calling this...
+    /// s.reserve_exact(8);
+    ///
+    /// // ... doesn't actually increase.
+    /// assert_eq!(10, s.capacity());
+    /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn reserve_exact(&mut self, additional: usize) {
         self.vec.reserve_exact(additional)
     }
 
-    /// Shrinks the capacity of this string buffer to match its length.
+    /// Shrinks the capacity of this `String` to match its length.
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let mut s = String::from("foo");
+    ///
     /// s.reserve(100);
     /// assert!(s.capacity() >= 100);
+    ///
     /// s.shrink_to_fit();
-    /// assert_eq!(s.capacity(), 3);
+    /// assert_eq!(3, s.capacity());
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -780,16 +950,20 @@ impl String {
         self.vec.shrink_to_fit()
     }
 
-    /// Adds the given character to the end of the string.
+    /// Appends the given `char` to the end of this `String`.
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let mut s = String::from("abc");
+    ///
     /// s.push('1');
     /// s.push('2');
     /// s.push('3');
-    /// assert_eq!(s, "abc123");
+    ///
+    /// assert_eq!("abc123", s);
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -815,13 +989,16 @@ impl String {
         }
     }
 
-    /// Works with the underlying buffer as a byte slice.
+    /// Returns a byte slice of this `String`'s contents.
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let s = String::from("hello");
-    /// assert_eq!(s.as_bytes(), [104, 101, 108, 108, 111]);
+    ///
+    /// assert_eq!(&[104, 101, 108, 108, 111], s.as_bytes());
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -829,19 +1006,25 @@ impl String {
         &self.vec
     }
 
-    /// Shortens a string to the specified length.
+    /// Shortens this `String` to the specified length.
     ///
     /// # Panics
     ///
-    /// Panics if `new_len` > current length,
-    /// or if `new_len` is not a character boundary.
+    /// Panics if `new_len` > current length, or if `new_len` does not lie on a
+    /// [`char`] boundary.
+    ///
+    /// [`char`]: ../primitive.char.html
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let mut s = String::from("hello");
+    ///
     /// s.truncate(2);
-    /// assert_eq!(s, "he");
+    ///
+    /// assert_eq!("he", s);
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -851,15 +1034,20 @@ impl String {
     }
 
     /// Removes the last character from the string buffer and returns it.
-    /// Returns `None` if this string buffer is empty.
+    ///
+    /// Returns `None` if this `String` is empty.
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let mut s = String::from("foo");
+    ///
     /// assert_eq!(s.pop(), Some('o'));
     /// assert_eq!(s.pop(), Some('o'));
     /// assert_eq!(s.pop(), Some('f'));
+    ///
     /// assert_eq!(s.pop(), None);
     /// ```
     #[inline]
@@ -877,23 +1065,25 @@ impl String {
         Some(ch)
     }
 
-    /// Removes the character from the string buffer at byte position `idx` and
-    /// returns it.
-    ///
-    /// # Warning
+    /// Removes a `char` from this `String` at a byte position and returns it.
     ///
-    /// This is an O(n) operation as it requires copying every element in the
+    /// This is an `O(n)` operation, as it requires copying every element in the
     /// buffer.
     ///
     /// # Panics
     ///
-    /// If `idx` does not lie on a character boundary, or if it is out of
-    /// bounds, then this function will panic.
+    /// Panics if `idx` is larger than or equal to the `String`'s length,
+    /// or if it does not lie on a [`char`] boundary.
+    ///
+    /// [`char`]: ../primitive.char.html
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let mut s = String::from("foo");
+    ///
     /// assert_eq!(s.remove(0), 'f');
     /// assert_eq!(s.remove(1), 'o');
     /// assert_eq!(s.remove(0), 'o');
@@ -902,7 +1092,7 @@ impl String {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn remove(&mut self, idx: usize) -> char {
         let len = self.len();
-        assert!(idx <= len);
+        assert!(idx < len);
 
         let ch = self.char_at(idx);
         let next = idx + ch.len_utf8();
@@ -915,17 +1105,31 @@ impl String {
         ch
     }
 
-    /// Inserts a character into the string buffer at byte position `idx`.
+    /// Inserts a character into this `String` at a byte position.
     ///
-    /// # Warning
-    ///
-    /// This is an O(n) operation as it requires copying every element in the
+    /// This is an `O(n)` operation as it requires copying every element in the
     /// buffer.
     ///
     /// # Panics
     ///
-    /// If `idx` does not lie on a character boundary or is out of bounds, then
-    /// this function will panic.
+    /// Panics if `idx` is larger than the `String`'s length, or if it does not
+    /// lie on a [`char`] boundary.
+    ///
+    /// [`char`]: ../primitive.char.html
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// let mut s = String::with_capacity(3);
+    ///
+    /// s.insert(0, 'f');
+    /// s.insert(1, 'o');
+    /// s.insert(2, 'o');
+    ///
+    /// assert_eq!("foo", s);
+    /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn insert(&mut self, idx: usize, ch: char) {
@@ -947,18 +1151,26 @@ impl String {
         }
     }
 
-    /// Views the string buffer as a mutable sequence of bytes.
+    /// Returns a mutable reference to the contents of this `String`.
     ///
-    /// This is unsafe because it does not check
-    /// to ensure that the resulting string will be valid UTF-8.
+    /// # Safety
+    ///
+    /// This function is unsafe because it does not check that the bytes passed
+    /// to it are valid UTF-8. If this constraint is violated, it may cause
+    /// memory unsafety issues with future users of the `String`, as the rest of
+    /// the standard library assumes that `String`s are valid UTF-8.
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let mut s = String::from("hello");
+    ///
     /// unsafe {
     ///     let vec = s.as_mut_vec();
-    ///     assert!(vec == &[104, 101, 108, 108, 111]);
+    ///     assert_eq!(&[104, 101, 108, 108, 111][..], &vec[..]);
+    ///
     ///     vec.reverse();
     /// }
     /// assert_eq!(s, "olleh");
@@ -969,12 +1181,15 @@ impl String {
         &mut self.vec
     }
 
-    /// Returns the number of bytes in this string.
+    /// Returns the length of this `String`, in bytes.
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
-    /// let a = "foo".to_string();
+    /// let a = String::from("foo");
+    ///
     /// assert_eq!(a.len(), 3);
     /// ```
     #[inline]
@@ -983,13 +1198,18 @@ impl String {
         self.vec.len()
     }
 
-    /// Returns true if the string contains no bytes
+    /// Returns `true` if this `String` has a length of zero.
+    ///
+    /// Returns `false` otherwise.
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let mut v = String::new();
     /// assert!(v.is_empty());
+    ///
     /// v.push('a');
     /// assert!(!v.is_empty());
     /// ```
@@ -999,14 +1219,23 @@ impl String {
         self.len() == 0
     }
 
-    /// Truncates the string, returning it to 0 length.
+    /// Truncates this `String`, removing all contents.
+    ///
+    /// While this means the `String` will have a length of zero, it does not
+    /// touch its capacity.
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
-    /// let mut s = "foo".to_string();
+    /// let mut s = String::from("foo");
+    ///
     /// s.clear();
+    ///
     /// assert!(s.is_empty());
+    /// assert_eq!(0, s.len());
+    /// assert_eq!(3, s.capacity());
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -1015,16 +1244,22 @@ impl String {
     }
 
     /// Create a draining iterator that removes the specified range in the string
-    /// and yields the removed chars from start to end. The element range is
-    /// removed even if the iterator is not consumed until the end.
+    /// and yields the removed chars.
+    ///
+    /// Note: The element range is removed even if the iterator is not
+    /// consumed until the end.
     ///
     /// # Panics
     ///
-    /// Panics if the starting point or end point are not on character boundaries,
-    /// or if they are out of bounds.
+    /// Panics if the starting point or end point do not lie on a [`char`]
+    /// boundary, or if they're out of bounds.
+    ///
+    /// [`char`]: ../primitive.char.html
     ///
     /// # Examples
     ///
+    /// Basic usage:
+    ///
     /// ```
     /// let mut s = String::from("α is alpha, β is beta");
     /// let beta_offset = s.find('β').unwrap_or(s.len());
@@ -1066,25 +1301,24 @@ impl String {
         }
     }
 
-    /// Converts the string into `Box<str>`.
+    /// Converts this `String` into a `Box<str>`.
+    ///
+    /// This will drop any excess capacity.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// let s = String::from("hello");
     ///
-    /// Note that this will drop any excess capacity.
+    /// let b = s.into_boxed_str();
+    /// ```
     #[stable(feature = "box_str", since = "1.4.0")]
     pub fn into_boxed_str(self) -> Box<str> {
         let slice = self.vec.into_boxed_slice();
         unsafe { mem::transmute::<Box<[u8]>, Box<str>>(slice) }
     }
-
-    /// Converts the string into `Box<str>`.
-    ///
-    /// Note that this will drop any excess capacity.
-    #[unstable(feature = "box_str2",
-               reason = "recently added, matches RFC",
-               issue = "27785")]
-    #[rustc_deprecated(since = "1.4.0", reason = "renamed to `into_boxed_str`")]
-    pub fn into_boxed_slice(self) -> Box<str> {
-        self.into_boxed_str()
-    }
 }
 
 impl FromUtf8Error {
@@ -1120,6 +1354,8 @@ impl FromUtf8Error {
     ///
     /// [`Utf8Error`]: ../str/struct.Utf8Error.html
     /// [`std::str`]: ../str/index.html
+    /// [`u8`]: ../primitive.u8.html
+    /// [`&str`]: ../primitive.str.html
     ///
     /// # Examples
     ///
@@ -1564,6 +1800,7 @@ impl Into<Vec<u8>> for String {
 
 #[unstable(feature = "into_cow", reason = "may be replaced by `convert::Into`",
            issue= "27735")]
+#[allow(deprecated)]
 impl IntoCow<'static, str> for String {
     #[inline]
     fn into_cow(self) -> Cow<'static, str> {
@@ -1573,6 +1810,7 @@ impl IntoCow<'static, str> for String {
 
 #[unstable(feature = "into_cow", reason = "may be replaced by `convert::Into`",
            issue = "27735")]
+#[allow(deprecated)]
 impl<'a> IntoCow<'a, str> for &'a str {
     #[inline]
     fn into_cow(self) -> Cow<'a, str> {
index ddad7533a081f8f89d2649f6c5399f53475c9309..a49b7304643ccaa2e9c5fba8301242dc9371bdad 100644 (file)
@@ -68,11 +68,12 @@ use core::hash::{self, Hash};
 use core::intrinsics::{arith_offset, assume, needs_drop};
 use core::iter::FromIterator;
 use core::mem;
-use core::ops::{Index, IndexMut, Deref};
+use core::ops::{Index, IndexMut};
 use core::ops;
 use core::ptr;
 use core::slice;
 
+#[allow(deprecated)]
 use borrow::{Cow, IntoCow};
 
 use super::range::RangeArgument;
@@ -464,9 +465,7 @@ impl<T> Vec<T> {
     ///
     /// Equivalent to `&s[..]`.
     #[inline]
-    #[unstable(feature = "convert",
-               reason = "waiting on RFC revision",
-               issue = "27729")]
+    #[stable(feature = "vec_as_slice", since = "1.7.0")]
     pub fn as_slice(&self) -> &[T] {
         self
     }
@@ -475,9 +474,7 @@ impl<T> Vec<T> {
     ///
     /// Equivalent to `&mut s[..]`.
     #[inline]
-    #[unstable(feature = "convert",
-               reason = "waiting on RFC revision",
-               issue = "27729")]
+    #[stable(feature = "vec_as_slice", since = "1.7.0")]
     pub fn as_mut_slice(&mut self) -> &mut [T] {
         &mut self[..]
     }
@@ -725,10 +722,12 @@ impl<T> Vec<T> {
     }
 
     /// Create a draining iterator that removes the specified range in the vector
-    /// and yields the removed items from start to end. The element range is
-    /// removed even if the iterator is not consumed until the end.
+    /// and yields the removed items.
     ///
-    /// Note: It is unspecified how many elements are removed from the vector,
+    /// Note 1: The element range is removed even if the iterator is not
+    /// consumed until the end.
+    ///
+    /// Note 2: It is unspecified how many elements are removed from the vector,
     /// if the `Drain` value is leaked.
     ///
     /// # Panics
@@ -739,11 +738,14 @@ impl<T> Vec<T> {
     /// # Examples
     ///
     /// ```
-    /// // Draining using `..` clears the whole vector.
     /// let mut v = vec![1, 2, 3];
-    /// let u: Vec<_> = v.drain(..).collect();
+    /// let u: Vec<_> = v.drain(1..).collect();
+    /// assert_eq!(v, &[1]);
+    /// assert_eq!(u, &[2, 3]);
+    ///
+    /// // A full range clears the vector
+    /// v.drain(..);
     /// assert_eq!(v, &[]);
-    /// assert_eq!(u, &[1, 2, 3]);
     /// ```
     #[stable(feature = "drain", since = "1.6.0")]
     pub fn drain<R>(&mut self, range: R) -> Drain<T>
@@ -1511,6 +1513,7 @@ impl<'a, T> FromIterator<T> for Cow<'a, [T]> where T: Clone {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
+#[allow(deprecated)]
 impl<'a, T: 'a> IntoCow<'a, [T]> for Vec<T> where T: Clone {
     fn into_cow(self) -> Cow<'a, [T]> {
         Cow::Owned(self)
@@ -1518,6 +1521,7 @@ impl<'a, T: 'a> IntoCow<'a, [T]> for Vec<T> where T: Clone {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
+#[allow(deprecated)]
 impl<'a, T> IntoCow<'a, [T]> for &'a [T] where T: Clone {
     fn into_cow(self) -> Cow<'a, [T]> {
         Cow::Borrowed(self)
index 53597f566b8ae4446fe30aab5d21fff3e5c63ee6..394f7a975989ae9b1f82a1930c7521148e49e5b7 100644 (file)
@@ -25,7 +25,6 @@ use core::mem;
 use core::ops::{Index, IndexMut};
 use core::ptr;
 use core::slice;
-use core::usize;
 
 use core::hash::{Hash, Hasher};
 use core::cmp;
@@ -36,7 +35,10 @@ use super::range::RangeArgument;
 
 const INITIAL_CAPACITY: usize = 7; // 2^3 - 1
 const MINIMUM_CAPACITY: usize = 1; // 2 - 1
-const MAXIMUM_ZST_CAPACITY: usize = 1 << (usize::BITS - 1); // Largest possible power of two
+#[cfg(target_pointer_width = "32")]
+const MAXIMUM_ZST_CAPACITY: usize = 1 << (32 - 1); // Largest possible power of two
+#[cfg(target_pointer_width = "64")]
+const MAXIMUM_ZST_CAPACITY: usize = 1 << (64 - 1); // Largest possible power of two
 
 /// `VecDeque` is a growable ring buffer, which can be used as a double-ended
 /// queue efficiently.
@@ -763,10 +765,12 @@ impl<T> VecDeque<T> {
     }
 
     /// Create a draining iterator that removes the specified range in the
-    /// `VecDeque` and yields the removed items from start to end. The element
-    /// range is removed even if the iterator is not consumed until the end.
+    /// `VecDeque` and yields the removed items.
     ///
-    /// Note: It is unspecified how many elements are removed from the deque,
+    /// Note 1: The element range is removed even if the iterator is not
+    /// consumed until the end.
+    ///
+    /// Note 2: It is unspecified how many elements are removed from the deque,
     /// if the `Drain` value is not dropped, but the borrow it holds expires
     /// (eg. due to mem::forget).
     ///
@@ -779,11 +783,13 @@ impl<T> VecDeque<T> {
     ///
     /// ```
     /// use std::collections::VecDeque;
+
+    /// let mut v: VecDeque<_> = vec![1, 2, 3].into_iter().collect();
+    /// assert_eq!(vec![3].into_iter().collect::<VecDeque<_>>(), v.drain(2..).collect());
+    /// assert_eq!(vec![1, 2].into_iter().collect::<VecDeque<_>>(), v);
     ///
-    /// // draining using `..` clears the whole deque.
-    /// let mut v = VecDeque::new();
-    /// v.push_back(1);
-    /// assert_eq!(v.drain(..).next(), Some(1));
+    /// // A full range clears all contents
+    /// v.drain(..);
     /// assert!(v.is_empty());
     /// ```
     #[inline]
@@ -1115,15 +1121,6 @@ impl<T> VecDeque<T> {
         self.pop_back()
     }
 
-    /// deprecated
-    #[unstable(feature = "deque_extras",
-               reason = "the naming of this function may be altered",
-               issue = "27788")]
-    #[rustc_deprecated(since = "1.5.0", reason = "renamed to swap_remove_back")]
-    pub fn swap_back_remove(&mut self, index: usize) -> Option<T> {
-        self.swap_remove_back(index)
-    }
-
     /// Removes an element from anywhere in the `VecDeque` and returns it,
     /// replacing it with the first element.
     ///
@@ -1158,15 +1155,6 @@ impl<T> VecDeque<T> {
         self.pop_front()
     }
 
-    /// deprecated
-    #[unstable(feature = "deque_extras",
-               reason = "the naming of this function may be altered",
-               issue = "27788")]
-    #[rustc_deprecated(since = "1.5.0", reason = "renamed to swap_remove_front")]
-    pub fn swap_front_remove(&mut self, index: usize) -> Option<T> {
-        self.swap_remove_front(index)
-    }
-
     /// Inserts an element at `index` within the `VecDeque`. Whichever
     /// end is closer to the insertion point will be moved to make room,
     /// and all the affected elements will be moved to new positions.
@@ -2178,7 +2166,7 @@ mod tests {
                             tester.push_front(i);
                         }
                         for i in 0..len {
-                            assert_eq!(tester.swap_back_remove(i), Some(len * 2 - 1 - i));
+                            assert_eq!(tester.swap_remove_back(i), Some(len * 2 - 1 - i));
                         }
                     } else {
                         for i in 0..len * 2 {
@@ -2186,7 +2174,7 @@ mod tests {
                         }
                         for i in 0..len {
                             let idx = tester.len() - 1 - i;
-                            assert_eq!(tester.swap_front_remove(idx), Some(len * 2 - 1 - i));
+                            assert_eq!(tester.swap_remove_front(idx), Some(len * 2 - 1 - i));
                         }
                     }
                     assert!(tester.tail < tester.cap());
@@ -2376,34 +2364,4 @@ mod tests {
             }
         }
     }
-
-    #[test]
-    fn test_zst_push() {
-        const N: usize = 8;
-
-        // Zero sized type
-        struct Zst;
-
-        // Test that for all possible sequences of push_front / push_back,
-        // we end up with a deque of the correct size
-
-        for len in 0..N {
-            let mut tester = VecDeque::with_capacity(len);
-            assert_eq!(tester.len(), 0);
-            assert!(tester.capacity() >= len);
-            for case in 0..(1 << len) {
-                assert_eq!(tester.len(), 0);
-                for bit in 0..len {
-                    if case & (1 << bit) != 0 {
-                        tester.push_front(Zst);
-                    } else {
-                        tester.push_back(Zst);
-                    }
-                }
-                assert_eq!(tester.len(), len);
-                assert_eq!(tester.iter().count(), len);
-                tester.clear();
-            }
-        }
-    }
 }
index 303a0ce811df6f825d60eed5d87d763ea0937e2b..cc4366e8ae4638785fb3f3a4300df1d818163773 100644 (file)
@@ -14,7 +14,7 @@ use std::collections::BinaryHeap;
 fn test_iterator() {
     let data = vec![5, 9, 3];
     let iterout = [9, 5, 3];
-    let heap = BinaryHeap::from_vec(data);
+    let heap = BinaryHeap::from(data);
     let mut i = 0;
     for el in &heap {
         assert_eq!(*el, iterout[i]);
@@ -26,7 +26,7 @@ fn test_iterator() {
 fn test_iterator_reverse() {
     let data = vec![5, 9, 3];
     let iterout = vec![3, 5, 9];
-    let pq = BinaryHeap::from_vec(data);
+    let pq = BinaryHeap::from(data);
 
     let v: Vec<_> = pq.iter().rev().cloned().collect();
     assert_eq!(v, iterout);
@@ -36,7 +36,7 @@ fn test_iterator_reverse() {
 fn test_move_iter() {
     let data = vec![5, 9, 3];
     let iterout = vec![9, 5, 3];
-    let pq = BinaryHeap::from_vec(data);
+    let pq = BinaryHeap::from(data);
 
     let v: Vec<_> = pq.into_iter().collect();
     assert_eq!(v, iterout);
@@ -45,7 +45,7 @@ fn test_move_iter() {
 #[test]
 fn test_move_iter_size_hint() {
     let data = vec![5, 9];
-    let pq = BinaryHeap::from_vec(data);
+    let pq = BinaryHeap::from(data);
 
     let mut it = pq.into_iter();
 
@@ -63,7 +63,7 @@ fn test_move_iter_size_hint() {
 fn test_move_iter_reverse() {
     let data = vec![5, 9, 3];
     let iterout = vec![3, 5, 9];
-    let pq = BinaryHeap::from_vec(data);
+    let pq = BinaryHeap::from(data);
 
     let v: Vec<_> = pq.into_iter().rev().collect();
     assert_eq!(v, iterout);
@@ -74,7 +74,7 @@ fn test_peek_and_pop() {
     let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
     let mut sorted = data.clone();
     sorted.sort();
-    let mut heap = BinaryHeap::from_vec(data);
+    let mut heap = BinaryHeap::from(data);
     while !heap.is_empty() {
         assert_eq!(heap.peek().unwrap(), sorted.last().unwrap());
         assert_eq!(heap.pop().unwrap(), sorted.pop().unwrap());
@@ -83,7 +83,7 @@ fn test_peek_and_pop() {
 
 #[test]
 fn test_push() {
-    let mut heap = BinaryHeap::from_vec(vec![2, 4, 9]);
+    let mut heap = BinaryHeap::from(vec![2, 4, 9]);
     assert_eq!(heap.len(), 3);
     assert!(*heap.peek().unwrap() == 9);
     heap.push(11);
@@ -105,7 +105,7 @@ fn test_push() {
 
 #[test]
 fn test_push_unique() {
-    let mut heap = BinaryHeap::<Box<_>>::from_vec(vec![box 2, box 4, box 9]);
+    let mut heap = BinaryHeap::<Box<_>>::from(vec![box 2, box 4, box 9]);
     assert_eq!(heap.len(), 3);
     assert!(*heap.peek().unwrap() == box 9);
     heap.push(box 11);
@@ -127,7 +127,7 @@ fn test_push_unique() {
 
 #[test]
 fn test_push_pop() {
-    let mut heap = BinaryHeap::from_vec(vec![5, 5, 2, 1, 3]);
+    let mut heap = BinaryHeap::from(vec![5, 5, 2, 1, 3]);
     assert_eq!(heap.len(), 5);
     assert_eq!(heap.push_pop(6), 6);
     assert_eq!(heap.len(), 5);
@@ -141,7 +141,7 @@ fn test_push_pop() {
 
 #[test]
 fn test_replace() {
-    let mut heap = BinaryHeap::from_vec(vec![5, 5, 2, 1, 3]);
+    let mut heap = BinaryHeap::from(vec![5, 5, 2, 1, 3]);
     assert_eq!(heap.len(), 5);
     assert_eq!(heap.replace(6).unwrap(), 5);
     assert_eq!(heap.len(), 5);
@@ -154,7 +154,7 @@ fn test_replace() {
 }
 
 fn check_to_vec(mut data: Vec<i32>) {
-    let heap = BinaryHeap::from_vec(data.clone());
+    let heap = BinaryHeap::from(data.clone());
     let mut v = heap.clone().into_vec();
     v.sort();
     data.sort();
index 846353cc4e7c29abe36aad462e14b1d5bc8095c3..05d4aff108aa9142942f3b485f9dcfb457d610d6 100644 (file)
@@ -11,7 +11,6 @@
 use std::collections::BTreeMap;
 use std::collections::Bound::{Excluded, Included, Unbounded, self};
 use std::collections::btree_map::Entry::{Occupied, Vacant};
-use std::iter::range_inclusive;
 use std::rc::Rc;
 
 #[test]
@@ -188,7 +187,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 = range_inclusive(i, j).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);
@@ -347,6 +346,54 @@ fn test_bad_zst() {
     }
 }
 
+#[test]
+fn test_clone() {
+    let mut map = BTreeMap::new();
+    let size = 100;
+    assert_eq!(map.len(), 0);
+
+    for i in 0..size {
+        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.len(), size);
+        assert_eq!(map, map.clone());
+    }
+
+    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);
+        assert_eq!(map, map.clone());
+    }
+}
+
+#[test]
+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 }
+}
+
 mod bench {
     use std::collections::BTreeMap;
     use std::__rand::{Rng, thread_rng};
index 8fcfe97f42afcb2fcc029c8f1a1e418139a7a05d..fee183433285e305a0ae541ceeb60d6986d20164 100644 (file)
@@ -254,3 +254,13 @@ fn test_recovery() {
 
     assert_eq!(s.iter().next(), None);
 }
+
+#[test]
+fn test_variance() {
+    use std::collections::btree_set::{IntoIter, Iter, Range};
+
+    fn set<'new>(v: BTreeSet<&'static str>) -> BTreeSet<&'new str> { v }
+    fn iter<'a, 'new>(v: Iter<'a, &'static str>) -> Iter<'a, &'new str> { v }
+    fn into_iter<'new>(v: IntoIter<&'static str>) -> IntoIter<&'new str> { v }
+    fn range<'a, 'new>(v: Range<'a, &'static str>) -> Range<'a, &'new str> { v }
+}
index e22ff7ca540610335c22e1b1705d9213c07d1851..0fde70aacdca9b5821c26a8048d867ebbc19289d 100644 (file)
@@ -269,6 +269,15 @@ fn test_replace_2d() {
     assert_eq!(data.replace(d, repl), data);
 }
 
+#[test]
+fn test_replace_pattern() {
+    let data = "abcdαβγδabcdαβγδ";
+    assert_eq!(data.replace("dαβ", "😺😺😺"), "abc😺😺😺γδabc😺😺😺γδ");
+    assert_eq!(data.replace('γ', "😺😺😺"), "abcdαβ😺😺😺δabcdαβ😺😺😺δ");
+    assert_eq!(data.replace(&['a', 'γ'] as &[_], "😺😺😺"), "😺😺😺bcdαβ😺😺😺δ😺😺😺bcdαβ😺😺😺δ");
+    assert_eq!(data.replace(|c| c == 'γ', "😺😺😺"), "abcdαβ😺😺😺δabcdαβ😺😺😺δ");
+}
+
 #[test]
 fn test_slice() {
     assert_eq!("ab", &"abc"[0..2]);
@@ -470,6 +479,18 @@ fn test_is_utf8() {
     assert!(from_utf8(&[0xF4, 0x8F, 0xBF, 0xBF]).is_ok());
 }
 
+#[test]
+fn from_utf8_mostly_ascii() {
+    // deny invalid bytes embedded in long stretches of ascii
+    for i in 32..64 {
+        let mut data = [0; 128];
+        data[i] = 0xC0;
+        assert!(from_utf8(&data).is_err());
+        data[i] = 0xC2;
+        assert!(from_utf8(&data).is_err());
+    }
+}
+
 #[test]
 fn test_is_utf16() {
     use rustc_unicode::str::is_utf16;
index 61c64ab8deebd709ba1d75522ba3154d81a538be..cb9bf935cdb58b9608cce14250419de9524fb58e 100644 (file)
 //! `downcast_ref` methods, to test if the contained value is of a given type,
 //! and to get a reference to the inner value as a type. As `&mut Any`, there
 //! is also the `downcast_mut` method, for getting a mutable reference to the
-//! inner value. `Box<Any>` adds the `move` method, which will unwrap a
-//! `Box<T>` from the object. See the extension traits (`*Ext`) for the full
-//! details.
+//! inner value. `Box<Any>` adds the `downcast` method, which attempts to
+//! convert to a `Box<T>`. See the [`Box`] documentation for the full details.
 //!
 //! Note that &Any is limited to testing whether a value is of a specified
 //! concrete type, and cannot be used to test whether a type implements a trait.
 //!
+//! [`Box`]: ../boxed/struct.Box.html
+//!
 //! # Examples
 //!
 //! Consider a situation where we want to log out a value passed to a function.
@@ -98,7 +99,7 @@ pub trait Any: Reflect + 'static {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<T: Reflect + 'static> Any for T {
+impl<T: Reflect + 'static + ?Sized > Any for T {
     fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
 }
 
index c02704217a8de10ba54b9fd06593a09c1dbf1a7c..0c3807d8ca0b5420389cd838edd6fe959c575bf1 100644 (file)
@@ -69,7 +69,7 @@ const MAX_THREE_B: u32 =  0x10000;
 /// Point], but only ones within a certain range. `MAX` is the highest valid
 /// code point that's a valid [Unicode Scalar Value].
 ///
-/// [`char`]: primitive.char.html
+/// [`char`]: ../primitive.char.html
 /// [Unicode Scalar Value]: http://www.unicode.org/glossary/#unicode_scalar_value
 /// [Code Point]: http://www.unicode.org/glossary/#code_point
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -91,9 +91,9 @@ pub const MAX: char = '\u{10ffff}';
 /// [`char`]s. `from_u32()` will return `None` if the input is not a valid value
 /// for a [`char`].
 ///
-/// [`char`]: primitive.char.html
-/// [`u32`]: primitive.u32.html
-/// [`as`]: ../book/casting-between-types.html#as
+/// [`char`]: ../primitive.char.html
+/// [`u32`]: ../primitive.u32.html
+/// [`as`]: ../../book/casting-between-types.html#as
 ///
 /// For an unsafe version of this function which ignores these checks, see
 /// [`from_u32_unchecked()`].
@@ -148,9 +148,9 @@ pub fn from_u32(i: u32) -> Option<char> {
 /// [`char`]s. `from_u32_unchecked()` will ignore this, and blindly cast to
 /// [`char`], possibly creating an invalid one.
 ///
-/// [`char`]: primitive.char.html
-/// [`u32`]: primitive.u32.html
-/// [`as`]: ../book/casting-between-types.html#as
+/// [`char`]: ../primitive.char.html
+/// [`u32`]: ../primitive.u32.html
+/// [`as`]: ../../book/casting-between-types.html#as
 ///
 /// # Safety
 ///
@@ -181,7 +181,7 @@ pub unsafe fn from_u32_unchecked(i: u32) -> char {
 ///
 /// A 'radix' here is sometimes also called a 'base'. A radix of two
 /// indicates a binary number, a radix of ten, decimal, and a radix of
-/// sixteen, hexicdecimal, to give some common values. Arbitrary
+/// sixteen, hexadecimal, to give some common values. Arbitrary
 /// radicum are supported.
 ///
 /// `from_digit()` will return `None` if the input is not a digit in
@@ -414,8 +414,8 @@ pub fn encode_utf16_raw(mut ch: u32, dst: &mut [u16]) -> Option<usize> {
 /// This `struct` is created by the [`escape_unicode()`] method on [`char`]. See
 /// its documentation for more.
 ///
-/// [`escape_unicode()`]: primitive.char.html#method.escape_unicode
-/// [`char`]: primitive.char.html
+/// [`escape_unicode()`]: ../primitive.char.html#method.escape_unicode
+/// [`char`]: ../primitive.char.html
 #[derive(Clone)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct EscapeUnicode {
@@ -494,8 +494,8 @@ impl Iterator for EscapeUnicode {
 /// This `struct` is created by the [`escape_default()`] method on [`char`]. See
 /// its documentation for more.
 ///
-/// [`escape_default()`]: primitive.char.html#method.escape_default
-/// [`char`]: primitive.char.html
+/// [`escape_default()`]: ../primitive.char.html#method.escape_default
+/// [`char`]: ../primitive.char.html
 #[derive(Clone)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct EscapeDefault {
@@ -537,4 +537,49 @@ impl Iterator for EscapeDefault {
             EscapeDefaultState::Done => (0, Some(0)),
         }
     }
+
+    fn count(self) -> usize {
+        match self.state {
+            EscapeDefaultState::Char(_) => 1,
+            EscapeDefaultState::Unicode(iter) => iter.count(),
+            EscapeDefaultState::Done => 0,
+            EscapeDefaultState::Backslash(_) => 2,
+        }
+    }
+
+    fn nth(&mut self, n: usize) -> Option<char> {
+        match self.state {
+            EscapeDefaultState::Backslash(c) if n == 0 => {
+                self.state = EscapeDefaultState::Char(c);
+                Some('\\')
+            },
+            EscapeDefaultState::Backslash(c) if n == 1 => {
+                self.state = EscapeDefaultState::Done;
+                Some(c)
+            },
+            EscapeDefaultState::Backslash(_) => {
+                self.state = EscapeDefaultState::Done;
+                None
+            },
+            EscapeDefaultState::Char(c) => {
+                self.state = EscapeDefaultState::Done;
+
+                if n == 0 {
+                    Some(c)
+                } else {
+                    None
+                }
+            },
+            EscapeDefaultState::Done => return None,
+            EscapeDefaultState::Unicode(ref mut i) => return i.nth(n),
+        }
+    }
+
+    fn last(self) -> Option<char> {
+        match self.state {
+            EscapeDefaultState::Unicode(iter) => iter.last(),
+            EscapeDefaultState::Done => None,
+            EscapeDefaultState::Backslash(c) | EscapeDefaultState::Char(c) => Some(c),
+        }
+    }
 }
index 0d4c0bb6480086c34a71e37ed13c45458cf26e1e..7c986131a52858dd357c48b0f6ec02208635878e 100644 (file)
@@ -181,6 +181,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
     /// Returns the wrapped `Formatter`.
     #[unstable(feature = "debug_builder_formatter", reason = "recently added",
                issue = "27782")]
+    #[rustc_deprecated(since = "1.7.0", reason = "will be removed")]
     pub fn formatter(&mut self) -> &mut fmt::Formatter<'b> {
         &mut self.fmt
     }
index 04676c0c9c8b47547d2cb9c3cb22f421518fc176..37f03d731dc72e0259050cbbd2aef75e8697be35 100644 (file)
@@ -25,10 +25,16 @@ use str;
 use self::rt::v1::Alignment;
 
 #[unstable(feature = "fmt_radix", issue = "27728")]
+#[rustc_deprecated(since = "1.7.0", reason = "not used enough to stabilize")]
+#[allow(deprecated)]
 pub use self::num::radix;
 #[unstable(feature = "fmt_radix", issue = "27728")]
+#[rustc_deprecated(since = "1.7.0", reason = "not used enough to stabilize")]
+#[allow(deprecated)]
 pub use self::num::Radix;
 #[unstable(feature = "fmt_radix", issue = "27728")]
+#[rustc_deprecated(since = "1.7.0", reason = "not used enough to stabilize")]
+#[allow(deprecated)]
 pub use self::num::RadixFmt;
 #[stable(feature = "debug_builders", since = "1.2.0")]
 pub use self::builders::{DebugStruct, DebugTuple, DebugSet, DebugList, DebugMap};
@@ -356,7 +362,7 @@ impl<'a> Display for Arguments<'a> {
 /// `Debug` implementations using either `derive` or the debug builder API
 /// on `Formatter` support pretty printing using the alternate flag: `{:#?}`.
 ///
-/// [debug_struct]: ../std/fmt/struct.Formatter.html#method.debug_struct
+/// [debug_struct]: ../../std/fmt/struct.Formatter.html#method.debug_struct
 ///
 /// Pretty printing with `#?`:
 ///
@@ -852,7 +858,7 @@ impl<'a> Formatter<'a> {
     ///
     /// # Arguments
     ///
-    /// * is_positive - whether the original integer was positive or not.
+    /// * is_nonnegative - whether the original integer was either positive or zero.
     /// * prefix - if the '#' character (Alternate) is provided, this
     ///   is the prefix to put in front of the number.
     /// * buf - the byte array that the number has been formatted into
@@ -861,7 +867,7 @@ impl<'a> Formatter<'a> {
     /// the minimum width. It will not take precision into account.
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn pad_integral(&mut self,
-                        is_positive: bool,
+                        is_nonnegative: bool,
                         prefix: &str,
                         buf: &str)
                         -> Result {
@@ -870,7 +876,7 @@ impl<'a> Formatter<'a> {
         let mut width = buf.len();
 
         let mut sign = None;
-        if !is_positive {
+        if !is_nonnegative {
             sign = Some('-'); width += 1;
         } else if self.sign_plus() {
             sign = Some('+'); width += 1;
@@ -1391,7 +1397,7 @@ impl<T> Pointer for *const T {
             f.flags |= 1 << (FlagV1::SignAwareZeroPad as u32);
 
             if let None = f.width {
-                f.width = Some((::usize::BITS/4) + 2);
+                f.width = Some(((mem::size_of::<usize>() * 8) / 4) + 2);
             }
         }
         f.flags |= 1 << (FlagV1::Alternate as u32);
@@ -1532,7 +1538,7 @@ macro_rules! tuple {
     ( $($name:ident,)+ ) => (
         #[stable(feature = "rust1", since = "1.0.0")]
         impl<$($name:Debug),*> Debug for ($($name,)*) {
-            #[allow(non_snake_case, unused_assignments)]
+            #[allow(non_snake_case, unused_assignments, deprecated)]
             fn fmt(&self, f: &mut Formatter) -> Result {
                 let mut builder = f.debug_tuple("");
                 let ($(ref $name,)*) = *self;
@@ -1569,7 +1575,7 @@ impl Debug for () {
     }
 }
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Debug for PhantomData<T> {
+impl<T: ?Sized> Debug for PhantomData<T> {
     fn fmt(&self, f: &mut Formatter) -> Result {
         f.pad("PhantomData")
     }
index bfae64f24cfe6fc56434a33100565ee07ab44b59..263e03dcc78393a87b428d59a0d029cfd32e40b1 100644 (file)
@@ -10,6 +10,8 @@
 
 //! Integer and floating-point number formatting
 
+#![allow(deprecated)]
+
 // FIXME: #6220 Implement floating point formatting
 
 use prelude::v1::*;
@@ -60,11 +62,11 @@ trait GenericRadix {
         // The radix can be as low as 2, so we need a buffer of at least 64
         // characters for a base 2 number.
         let zero = T::zero();
-        let is_positive = x >= zero;
+        let is_nonnegative = x >= zero;
         let mut buf = [0; 64];
         let mut curr = buf.len();
         let base = T::from_u8(self.base());
-        if is_positive {
+        if is_nonnegative {
             // Accumulate each digit of the number from the least significant
             // to the most significant figure.
             for byte in buf.iter_mut().rev() {
@@ -91,7 +93,7 @@ trait GenericRadix {
             }
         }
         let buf = unsafe { str::from_utf8_unchecked(&buf[curr..]) };
-        f.pad_integral(is_positive, self.prefix(), buf)
+        f.pad_integral(is_nonnegative, self.prefix(), buf)
     }
 }
 
@@ -143,6 +145,7 @@ radix! { UpperHex, 16, "0x", x @  0 ...  9 => b'0' + x,
 #[unstable(feature = "fmt_radix",
            reason = "may be renamed or move to a different module",
            issue = "27728")]
+#[rustc_deprecated(since = "1.7.0", reason = "not used enough to stabilize")]
 pub struct Radix {
     base: u8,
 }
@@ -173,6 +176,7 @@ impl GenericRadix for Radix {
 #[unstable(feature = "fmt_radix",
            reason = "may be renamed or move to a different module",
            issue = "27728")]
+#[rustc_deprecated(since = "1.7.0", reason = "not used enough to stabilize")]
 #[derive(Copy, Clone)]
 pub struct RadixFmt<T, R>(T, R);
 
@@ -189,6 +193,7 @@ pub struct RadixFmt<T, R>(T, R);
 #[unstable(feature = "fmt_radix",
            reason = "may be renamed or move to a different module",
            issue = "27728")]
+#[rustc_deprecated(since = "1.7.0", reason = "not used enough to stabilize")]
 pub fn radix<T>(x: T, base: u8) -> RadixFmt<T, Radix> {
     RadixFmt(x, Radix::new(base))
 }
@@ -268,8 +273,8 @@ macro_rules! impl_Display {
     impl fmt::Display for $t {
         #[allow(unused_comparisons)]
         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-            let is_positive = *self >= 0;
-            let mut n = if is_positive {
+            let is_nonnegative = *self >= 0;
+            let mut n = if is_nonnegative {
                 self.$conv_fn()
             } else {
                 // convert the negative num to positive by summing 1 to it's 2 complement
@@ -321,7 +326,7 @@ macro_rules! impl_Display {
                 str::from_utf8_unchecked(
                     slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize))
             };
-            f.pad_integral(is_positive, "", buf_slice)
+            f.pad_integral(is_nonnegative, "", buf_slice)
         }
     })*);
 }
index 3cb0691b4216046ddd6aff1962863b6d2d099f47..9ab55620e0aece5ebad170eb205792215975639e 100644 (file)
@@ -73,6 +73,7 @@
 
 use prelude::v1::*;
 
+use marker;
 use mem;
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -190,11 +191,83 @@ pub trait Hasher {
     }
 }
 
+/// A `BuildHasher` is typically used as a factory for instances of `Hasher`
+/// which a `HashMap` can then use to hash keys independently.
+///
+/// Note that for each instance of `BuildHasher` the create hashers should be
+/// identical. That is if the same stream of bytes is fed into each hasher the
+/// same output will also be generated.
+#[stable(since = "1.7.0", feature = "build_hasher")]
+pub trait BuildHasher {
+    /// Type of the hasher that will be created.
+    #[stable(since = "1.7.0", feature = "build_hasher")]
+    type Hasher: Hasher;
+
+    /// Creates a new hasher.
+    #[stable(since = "1.7.0", feature = "build_hasher")]
+    fn build_hasher(&self) -> Self::Hasher;
+}
+
+/// A structure which implements `BuildHasher` for all `Hasher` types which also
+/// implement `Default`.
+///
+/// This struct is 0-sized and does not need construction.
+#[stable(since = "1.7.0", feature = "build_hasher")]
+pub struct BuildHasherDefault<H>(marker::PhantomData<H>);
+
+#[stable(since = "1.7.0", feature = "build_hasher")]
+impl<H: Default + Hasher> BuildHasher for BuildHasherDefault<H> {
+    type Hasher = H;
+
+    fn build_hasher(&self) -> H {
+        H::default()
+    }
+}
+
+#[stable(since = "1.7.0", feature = "build_hasher")]
+impl<H> Clone for BuildHasherDefault<H> {
+    fn clone(&self) -> BuildHasherDefault<H> {
+        BuildHasherDefault(marker::PhantomData)
+    }
+}
+
+#[stable(since = "1.7.0", feature = "build_hasher")]
+impl<H> Default for BuildHasherDefault<H> {
+    fn default() -> BuildHasherDefault<H> {
+        BuildHasherDefault(marker::PhantomData)
+    }
+}
+
+// The HashState trait is super deprecated, but it's here to have the blanket
+// impl that goes from HashState -> BuildHasher
+
+/// Deprecated, renamed to `BuildHasher`
+#[unstable(feature = "hashmap_hasher", reason = "hasher stuff is unclear",
+           issue = "27713")]
+#[rustc_deprecated(since = "1.7.0", reason = "support moved to std::hash and \
+                                              renamed to BuildHasher")]
+pub trait HashState {
+    /// Type of the hasher that will be created.
+    type Hasher: Hasher;
+
+    /// Creates a new hasher based on the given state of this object.
+    fn hasher(&self) -> Self::Hasher;
+}
+
+#[unstable(feature = "hashmap_hasher", reason = "hasher stuff is unclear",
+           issue = "27713")]
+#[allow(deprecated)]
+impl<T: HashState> BuildHasher for T {
+    type Hasher = T::Hasher;
+    fn build_hasher(&self) -> T::Hasher { self.hasher() }
+}
+
 //////////////////////////////////////////////////////////////////////////////
 
 mod impls {
     use prelude::v1::*;
 
+    use mem;
     use slice;
     use super::*;
 
@@ -207,9 +280,7 @@ mod impls {
                 }
 
                 fn hash_slice<H: Hasher>(data: &[$ty], state: &mut H) {
-                    // FIXME(#23542) Replace with type ascription.
-                    #![allow(trivial_casts)]
-                    let newlen = data.len() * ::$ty::BYTES;
+                    let newlen = data.len() * mem::size_of::<$ty>();
                     let ptr = data.as_ptr() as *const u8;
                     state.write(unsafe { slice::from_raw_parts(ptr, newlen) })
                 }
index a094bcd0192d24664ae01e7b40e7fef6afcc045d..568c4e143e04b4397fec280bd7b0a77f15e3622c 100644 (file)
@@ -512,164 +512,32 @@ extern "rust-intrinsic" {
     /// Returns the nearest integer to an `f64`. Rounds half-way cases away from zero.
     pub fn roundf64(x: f64) -> f64;
 
-    /// Returns the number of bits set in a `u8`.
-    #[cfg(stage0)]
-    pub fn ctpop8(x: u8) -> u8;
-    /// Returns the number of bits set in a `u16`.
-    #[cfg(stage0)]
-    pub fn ctpop16(x: u16) -> u16;
-    /// Returns the number of bits set in a `u32`.
-    #[cfg(stage0)]
-    pub fn ctpop32(x: u32) -> u32;
-    /// Returns the number of bits set in a `u64`.
-    #[cfg(stage0)]
-    pub fn ctpop64(x: u64) -> u64;
     /// Returns the number of bits set in an integer type `T`
-    #[cfg(not(stage0))]
     pub fn ctpop<T>(x: T) -> T;
 
-    /// Returns the number of leading bits unset in a `u8`.
-    #[cfg(stage0)]
-    pub fn ctlz8(x: u8) -> u8;
-    /// Returns the number of leading bits unset in a `u16`.
-    #[cfg(stage0)]
-    pub fn ctlz16(x: u16) -> u16;
-    /// Returns the number of leading bits unset in a `u32`.
-    #[cfg(stage0)]
-    pub fn ctlz32(x: u32) -> u32;
-    /// Returns the number of leading bits unset in a `u64`.
-    #[cfg(stage0)]
-    pub fn ctlz64(x: u64) -> u64;
     /// Returns the number of leading bits unset in an integer type `T`
-    #[cfg(not(stage0))]
     pub fn ctlz<T>(x: T) -> T;
 
-    /// Returns the number of trailing bits unset in a `u8`.
-    #[cfg(stage0)]
-    pub fn cttz8(x: u8) -> u8;
-    /// Returns the number of trailing bits unset in a `u16`.
-    #[cfg(stage0)]
-    pub fn cttz16(x: u16) -> u16;
-    /// Returns the number of trailing bits unset in a `u32`.
-    #[cfg(stage0)]
-    pub fn cttz32(x: u32) -> u32;
-    /// Returns the number of trailing bits unset in a `u64`.
-    #[cfg(stage0)]
-    pub fn cttz64(x: u64) -> u64;
     /// Returns the number of trailing bits unset in an integer type `T`
-    #[cfg(not(stage0))]
     pub fn cttz<T>(x: T) -> T;
 
-    /// Reverses the bytes in a `u16`.
-    #[cfg(stage0)]
-    pub fn bswap16(x: u16) -> u16;
-    /// Reverses the bytes in a `u32`.
-    #[cfg(stage0)]
-    pub fn bswap32(x: u32) -> u32;
-    /// Reverses the bytes in a `u64`.
-    #[cfg(stage0)]
-    pub fn bswap64(x: u64) -> u64;
     /// Reverses the bytes in an integer type `T`.
-    #[cfg(not(stage0))]
     pub fn bswap<T>(x: T) -> T;
 
-    /// Performs checked `i8` addition.
-    #[cfg(stage0)]
-    pub fn i8_add_with_overflow(x: i8, y: i8) -> (i8, bool);
-    /// Performs checked `i16` addition.
-    #[cfg(stage0)]
-    pub fn i16_add_with_overflow(x: i16, y: i16) -> (i16, bool);
-    /// Performs checked `i32` addition.
-    #[cfg(stage0)]
-    pub fn i32_add_with_overflow(x: i32, y: i32) -> (i32, bool);
-    /// Performs checked `i64` addition.
-    #[cfg(stage0)]
-    pub fn i64_add_with_overflow(x: i64, y: i64) -> (i64, bool);
-
-    /// Performs checked `u8` addition.
-    #[cfg(stage0)]
-    pub fn u8_add_with_overflow(x: u8, y: u8) -> (u8, bool);
-    /// Performs checked `u16` addition.
-    #[cfg(stage0)]
-    pub fn u16_add_with_overflow(x: u16, y: u16) -> (u16, bool);
-    /// Performs checked `u32` addition.
-    #[cfg(stage0)]
-    pub fn u32_add_with_overflow(x: u32, y: u32) -> (u32, bool);
-    /// Performs checked `u64` addition.
-    #[cfg(stage0)]
-    pub fn u64_add_with_overflow(x: u64, y: u64) -> (u64, bool);
-
     /// Performs checked integer addition.
-    #[cfg(not(stage0))]
     pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
 
-    /// Performs checked `i8` subtraction.
-    #[cfg(stage0)]
-    pub fn i8_sub_with_overflow(x: i8, y: i8) -> (i8, bool);
-    /// Performs checked `i16` subtraction.
-    #[cfg(stage0)]
-    pub fn i16_sub_with_overflow(x: i16, y: i16) -> (i16, bool);
-    /// Performs checked `i32` subtraction.
-    #[cfg(stage0)]
-    pub fn i32_sub_with_overflow(x: i32, y: i32) -> (i32, bool);
-    /// Performs checked `i64` subtraction.
-    #[cfg(stage0)]
-    pub fn i64_sub_with_overflow(x: i64, y: i64) -> (i64, bool);
-
-    /// Performs checked `u8` subtraction.
-    #[cfg(stage0)]
-    pub fn u8_sub_with_overflow(x: u8, y: u8) -> (u8, bool);
-    /// Performs checked `u16` subtraction.
-    #[cfg(stage0)]
-    pub fn u16_sub_with_overflow(x: u16, y: u16) -> (u16, bool);
-    /// Performs checked `u32` subtraction.
-    #[cfg(stage0)]
-    pub fn u32_sub_with_overflow(x: u32, y: u32) -> (u32, bool);
-    /// Performs checked `u64` subtraction.
-    #[cfg(stage0)]
-    pub fn u64_sub_with_overflow(x: u64, y: u64) -> (u64, bool);
-
     /// Performs checked integer subtraction
-    #[cfg(not(stage0))]
     pub fn sub_with_overflow<T>(x: T, y: T) -> (T, bool);
 
-    /// Performs checked `i8` multiplication.
-    #[cfg(stage0)]
-    pub fn i8_mul_with_overflow(x: i8, y: i8) -> (i8, bool);
-    /// Performs checked `i16` multiplication.
-    #[cfg(stage0)]
-    pub fn i16_mul_with_overflow(x: i16, y: i16) -> (i16, bool);
-    /// Performs checked `i32` multiplication.
-    #[cfg(stage0)]
-    pub fn i32_mul_with_overflow(x: i32, y: i32) -> (i32, bool);
-    /// Performs checked `i64` multiplication.
-    #[cfg(stage0)]
-    pub fn i64_mul_with_overflow(x: i64, y: i64) -> (i64, bool);
-
-    /// Performs checked `u8` multiplication.
-    #[cfg(stage0)]
-    pub fn u8_mul_with_overflow(x: u8, y: u8) -> (u8, bool);
-    /// Performs checked `u16` multiplication.
-    #[cfg(stage0)]
-    pub fn u16_mul_with_overflow(x: u16, y: u16) -> (u16, bool);
-    /// Performs checked `u32` multiplication.
-    #[cfg(stage0)]
-    pub fn u32_mul_with_overflow(x: u32, y: u32) -> (u32, bool);
-    /// Performs checked `u64` multiplication.
-    #[cfg(stage0)]
-    pub fn u64_mul_with_overflow(x: u64, y: u64) -> (u64, bool);
-
     /// Performs checked integer multiplication
-    #[cfg(not(stage0))]
     pub fn mul_with_overflow<T>(x: T, y: T) -> (T, bool);
 
     /// Performs an unchecked division, resulting in undefined behavior
     /// where y = 0 or x = `T::min_value()` and y = -1
-    #[cfg(not(stage0))]
     pub fn unchecked_div<T>(x: T, y: T) -> T;
     /// Returns the remainder of an unchecked division, resulting in
     /// undefined behavior where y = 0 or x = `T::min_value()` and y = -1
-    #[cfg(not(stage0))]
     pub fn unchecked_rem<T>(x: T, y: T) -> T;
 
     /// Returns (a + b) mod 2^N, where N is the width of T in bits.
index 959b6a97c5ccbacbcfdae275fe5efe0cfba13ac0..e3e783329ec812e003d8d6cd915648eac8c86c86 100644 (file)
@@ -321,7 +321,6 @@ fn _assert_is_object_safe(_: &Iterator<Item=()>) {}
 ///
 /// [module-level documentation]: index.html
 /// [impl]: index.html#implementing-iterator
-#[lang = "iterator"]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_on_unimplemented = "`{Self}` is not an iterator; maybe try calling \
                             `.iter()` or a similar method"]
@@ -604,7 +603,7 @@ pub trait Iterator {
     /// iterators, returning a tuple where the first element comes from the
     /// first iterator, and the second element comes from the second iterator.
     ///
-    /// In other words, it zips two iterators together, into a single one. 🤐
+    /// In other words, it zips two iterators together, into a single one.
     ///
     /// When either iterator returns `None`, all further calls to `next()`
     /// will return `None`.
@@ -1113,16 +1112,22 @@ pub trait Iterator {
         Take{iter: self, n: n}
     }
 
-    /// An iterator similar to `fold()`, with internal state.
-    ///
-    /// `scan()` accumulates a final value, similar to [`fold()`], but instead
-    /// of passing along an accumulator, it maintains the accumulator internally.
+    /// An iterator adaptor similar to [`fold()`] that holds internal state and
+    /// produces a new iterator.
     ///
     /// [`fold()`]: #method.fold
     ///
-    /// On each iteraton of `scan()`, you can assign to the internal state, and
-    /// a mutable reference to the state is passed as the first argument to the
-    /// closure, allowing you to modify it on each iteration.
+    /// `scan()` takes two arguments: an initial value which seeds the internal
+    /// state, and a closure with two arguments, the first being a mutable
+    /// reference to the internal state and the second an iterator element.
+    /// The closure can assign to the internal state to share state between
+    /// iterations.
+    ///
+    /// On iteration, the closure will be applied to each element of the
+    /// iterator and the return value from the closure, an [`Option`], is
+    /// yielded by the iterator.
+    ///
+    /// [`Option`]: ../option/enum.Option.html
     ///
     /// # Examples
     ///
@@ -1353,7 +1358,7 @@ pub trait Iterator {
     /// One of the keys to `collect()`'s power is that many things you might
     /// not think of as 'collections' actually are. For example, a [`String`]
     /// is a collection of [`char`]s. And a collection of [`Result<T, E>`] can
-    /// be thought of as single [`Result<Collection<T>, E>`]. See the examples
+    /// be thought of as single `Result<Collection<T>, E>`. See the examples
     /// below for more.
     ///
     /// [`String`]: ../string/struct.String.html
@@ -2126,7 +2131,7 @@ pub trait Iterator {
     /// ```
     #[unstable(feature = "iter_arith", reason = "bounds recently changed",
                issue = "27739")]
-    fn sum<S=<Self as Iterator>::Item>(self) -> S where
+    fn sum<S>(self) -> S where
         S: Add<Self::Item, Output=S> + Zero,
         Self: Sized,
     {
@@ -2151,7 +2156,7 @@ pub trait Iterator {
     /// ```
     #[unstable(feature="iter_arith", reason = "bounds recently changed",
                issue = "27739")]
-    fn product<P=<Self as Iterator>::Item>(self) -> P where
+    fn product<P>(self) -> P where
         P: Mul<Self::Item, Output=P> + One,
         Self: Sized,
     {
@@ -4750,87 +4755,3 @@ impl<T> ExactSizeIterator for Once<T> {
 pub fn once<T>(value: T) -> Once<T> {
     Once { inner: Some(value).into_iter() }
 }
-
-/// Functions for lexicographical ordering of sequences.
-///
-/// Lexicographical ordering through `<`, `<=`, `>=`, `>` requires
-/// that the elements implement both `PartialEq` and `PartialOrd`.
-///
-/// If two sequences are equal up until the point where one ends,
-/// the shorter sequence compares less.
-#[rustc_deprecated(since = "1.4.0", reason = "use the equivalent methods on `Iterator` instead")]
-#[unstable(feature = "iter_order_deprecated", reason = "needs review and revision",
-           issue = "27737")]
-pub mod order {
-    use cmp;
-    use cmp::{Eq, Ord, PartialOrd, PartialEq};
-    use option::Option;
-    use super::Iterator;
-
-    /// Compare `a` and `b` for equality using `Eq`
-    pub fn equals<A, L, R>(a: L, b: R) -> bool where
-        A: Eq,
-        L: Iterator<Item=A>,
-        R: Iterator<Item=A>,
-    {
-        a.eq(b)
-    }
-
-    /// Order `a` and `b` lexicographically using `Ord`
-    pub fn cmp<A, L, R>(a: L, b: R) -> cmp::Ordering where
-        A: Ord,
-        L: Iterator<Item=A>,
-        R: Iterator<Item=A>,
-    {
-        a.cmp(b)
-    }
-
-    /// Order `a` and `b` lexicographically using `PartialOrd`
-    pub fn partial_cmp<L: Iterator, R: Iterator>(a: L, b: R) -> Option<cmp::Ordering> where
-        L::Item: PartialOrd<R::Item>
-    {
-        a.partial_cmp(b)
-    }
-
-    /// Compare `a` and `b` for equality (Using partial equality, `PartialEq`)
-    pub fn eq<L: Iterator, R: Iterator>(a: L, b: R) -> bool where
-        L::Item: PartialEq<R::Item>,
-    {
-        a.eq(b)
-    }
-
-    /// Compares `a` and `b` for nonequality (Using partial equality, `PartialEq`)
-    pub fn ne<L: Iterator, R: Iterator>(a: L, b: R) -> bool where
-        L::Item: PartialEq<R::Item>,
-    {
-        a.ne(b)
-    }
-
-    /// Returns `a` < `b` lexicographically (Using partial order, `PartialOrd`)
-    pub fn lt<L: Iterator, R: Iterator>(a: L, b: R) -> bool where
-        L::Item: PartialOrd<R::Item>,
-    {
-        a.lt(b)
-    }
-
-    /// Returns `a` <= `b` lexicographically (Using partial order, `PartialOrd`)
-    pub fn le<L: Iterator, R: Iterator>(a: L, b: R) -> bool where
-        L::Item: PartialOrd<R::Item>,
-    {
-        a.le(b)
-    }
-
-    /// Returns `a` > `b` lexicographically (Using partial order, `PartialOrd`)
-    pub fn gt<L: Iterator, R: Iterator>(a: L, b: R) -> bool where
-        L::Item: PartialOrd<R::Item>,
-    {
-        a.gt(b)
-    }
-
-    /// Returns `a` >= `b` lexicographically (Using partial order, `PartialOrd`)
-    pub fn ge<L: Iterator, R: Iterator>(a: L, b: R) -> bool where
-        L::Item: PartialOrd<R::Item>,
-    {
-        a.ge(b)
-    }
-}
index 86f2e3bcec396a6bbf433592dd6a8e52a5a1ae25..e8803976937d2d7ea43f49c71d55c6ddfe9fe04e 100644 (file)
 //! nor does it provide concurrency or I/O. These things require
 //! platform integration, and this library is platform-agnostic.
 //!
-//! *It is not recommended to use the core library*. The stable
-//! functionality of libcore is reexported from the
-//! [standard library](../std/index.html). The composition of this library is
-//! subject to change over time; only the interface exposed through libstd is
-//! intended to be stable.
-//!
 //! # How to use the core library
 //!
 // FIXME: Fill me in with more detail when the interface settles
 // Since libcore defines many fundamental lang items, all tests live in a
 // separate crate, libcoretest, to avoid bizarre issues.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "core"]
 #![stable(feature = "core", since = "1.6.0")]
-#![cfg_attr(stage0, staged_api)]
 #![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",
@@ -66,8 +57,6 @@
 #![no_core]
 #![deny(missing_docs)]
 
-#![cfg_attr(stage0, feature(rustc_attrs))]
-#![cfg_attr(stage0, allow(unused_attributes))]
 #![feature(allow_internal_unstable)]
 #![feature(associated_type_defaults)]
 #![feature(concat_idents)]
@@ -81,8 +70,7 @@
 #![feature(optin_builtin_traits)]
 #![feature(reflect)]
 #![feature(unwind_attributes)]
-#![cfg_attr(stage0, feature(simd))]
-#![cfg_attr(not(stage0), feature(repr_simd, platform_intrinsics))]
+#![feature(repr_simd, platform_intrinsics)]
 #![feature(staged_api)]
 #![feature(unboxed_closures)]
 
@@ -153,12 +141,6 @@ pub mod option;
 pub mod raw;
 pub mod result;
 
-#[cfg(stage0)]
-#[path = "simd_old.rs"]
-pub mod simd;
-#[cfg(not(stage0))]
-pub mod simd;
-
 pub mod slice;
 pub mod str;
 pub mod hash;
index 03d3cb11b3ed37fa93e9ff1c10d34c19fbeec377..154ca30c62dd148398744744912fed670a34ada3 100644 (file)
@@ -135,10 +135,10 @@ macro_rules! debug_assert {
     ($($arg:tt)*) => (if cfg!(debug_assertions) { assert!($($arg)*); })
 }
 
-/// Asserts that two expressions are equal to each other, testing equality in
-/// both directions.
+/// Asserts that two expressions are equal to each other.
 ///
-/// On panic, this macro will print the values of the expressions.
+/// On panic, this macro will print the values of the expressions with their
+/// debug representations.
 ///
 /// Unlike `assert_eq!`, `debug_assert_eq!` statements are only enabled in non
 /// optimized builds by default. An optimized build will omit all
index b584e59a825ffed0751e15cba1ff57ea49a5269b..1ed2a219fac3a5f03499d2a1532b90b064bc516a 100644 (file)
@@ -24,6 +24,8 @@ use hash::Hash;
 use hash::Hasher;
 
 /// Types that can be transferred across thread boundaries.
+///
+/// This trait is automatically derived when the compiler determines it's appropriate.
 #[stable(feature = "rust1", since = "1.0.0")]
 #[lang = "send"]
 #[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"]
@@ -219,6 +221,8 @@ pub trait Copy : Clone {
 /// wrapper around the value(s) which can be mutated when behind a `&`
 /// reference; not doing this is undefined behavior (for example,
 /// `transmute`-ing from `&T` to `&mut T` is invalid).
+///
+/// This trait is automatically derived when the compiler determines it's appropriate.
 #[stable(feature = "rust1", since = "1.0.0")]
 #[lang = "sync"]
 #[rustc_on_unimplemented = "`{Self}` cannot be shared between threads safely"]
@@ -291,6 +295,10 @@ macro_rules! impls{
 /// even though it does not. This allows you to inform the compiler about certain safety properties
 /// of your code.
 ///
+/// For a more in-depth explanation of how to use `PhantomData<T>`, please see [the Nomicon].
+///
+/// [the Nomicon]: ../../nomicon/phantom-data.html
+///
 /// # A ghastly note 👻👻👻
 ///
 /// Though they both have scary names, `PhantomData<T>` and 'phantom types' are related, but not
@@ -325,7 +333,7 @@ macro_rules! impls{
 /// use std::marker::PhantomData;
 ///
 /// # #[allow(dead_code)]
-/// struct Slice<'a, T:'a> {
+/// struct Slice<'a, T: 'a> {
 ///     start: *const T,
 ///     end: *const T,
 ///     phantom: PhantomData<&'a T>
@@ -420,18 +428,18 @@ mod impls {
 /// use std::any::Any;
 ///
 /// # #[allow(dead_code)]
-/// fn foo<T:Reflect+'static>(x: &T) {
+/// fn foo<T: Reflect + 'static>(x: &T) {
 ///     let any: &Any = x;
 ///     if any.is::<u32>() { println!("u32"); }
 /// }
 /// ```
 ///
-/// Without the declaration `T:Reflect`, `foo` would not type check
+/// Without the declaration `T: Reflect`, `foo` would not type check
 /// (note: as a matter of style, it would be preferable to write
-/// `T:Any`, because `T:Any` implies `T:Reflect` and `T:'static`, but
+/// `T: Any`, because `T: Any` implies `T: Reflect` and `T: 'static`, but
 /// we use `Reflect` here to show how it works). The `Reflect` bound
 /// thus serves to alert `foo`'s caller to the fact that `foo` may
-/// behave differently depending on whether `T=u32` or not. In
+/// behave differently depending on whether `T = u32` or not. In
 /// particular, thanks to the `Reflect` bound, callers know that a
 /// function declared like `fn bar<T>(...)` will always act in
 /// precisely the same way no matter what type `T` is supplied,
index ee6e708ea327defa7ad3c8e68db99469b1435ed1..fb6dac407983423fa721668cd4a07728f19f933d 100644 (file)
@@ -130,7 +130,7 @@ pub fn size_of<T>() -> usize {
     unsafe { intrinsics::size_of::<T>() }
 }
 
-/// Returns the size of the type that `val` points to in bytes.
+/// Returns the size of the given value in bytes.
 ///
 /// # Examples
 ///
index c4ca3fa384e968cfe7734df57b8aa52108418ea6..92bbc4efb7cc1d78ab45fe0d0828401d544608d3 100644 (file)
@@ -38,31 +38,13 @@ unsafe impl Zeroable for u64 {}
 #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
 pub struct NonZero<T: Zeroable>(T);
 
-#[cfg(stage0)]
-macro_rules! nonzero_new {
-    () => (
-        /// Creates an instance of NonZero with the provided value.
-        /// You must indeed ensure that the value is actually "non-zero".
-        #[inline(always)]
-        pub unsafe fn new(inner: T) -> NonZero<T> {
-            NonZero(inner)
-        }
-    )
-}
-#[cfg(not(stage0))]
-macro_rules! nonzero_new {
-    () => (
-        /// Creates an instance of NonZero with the provided value.
-        /// You must indeed ensure that the value is actually "non-zero".
-        #[inline(always)]
-        pub const unsafe fn new(inner: T) -> NonZero<T> {
-            NonZero(inner)
-        }
-    )
-}
-
 impl<T: Zeroable> NonZero<T> {
-    nonzero_new!{}
+    /// Creates an instance of NonZero with the provided value.
+    /// You must indeed ensure that the value is actually "non-zero".
+    #[inline(always)]
+    pub const unsafe fn new(inner: T) -> NonZero<T> {
+        NonZero(inner)
+    }
 }
 
 impl<T: Zeroable> Deref for NonZero<T> {
index 5d15ada4e750988ef30c7e2711bbcfd697823648..66c6deb361564305d964616cf078a29865549833 100644 (file)
@@ -55,15 +55,6 @@ macro_rules! impl_full_ops {
     ($($ty:ty: add($addfn:path), mul/div($bigty:ident);)*) => (
         $(
             impl FullOps for $ty {
-                #[cfg(stage0)]
-                fn full_add(self, other: $ty, carry: bool) -> (bool, $ty) {
-                    // this cannot overflow, the output is between 0 and 2*2^nbits - 1
-                    // FIXME will LLVM optimize this into ADC or similar???
-                    let (v, carry1) = unsafe { $addfn(self, other) };
-                    let (v, carry2) = unsafe { $addfn(v, if carry {1} else {0}) };
-                    (carry1 || carry2, v)
-                }
-                #[cfg(not(stage0))]
                 fn full_add(self, other: $ty, carry: bool) -> (bool, $ty) {
                     // this cannot overflow, the output is between 0 and 2*2^nbits - 1
                     // FIXME will LLVM optimize this into ADC or similar???
index 1f0f06d746197151f704f818a50de782b6d67b91..82d3389edc478d8fa2fcbed631f483620c2c4ccf 100644 (file)
@@ -60,17 +60,13 @@ pub fn fast_path<T: RawFloat>(integral: &[u8], fractional: &[u8], e: i64) -> Opt
     if f > T::max_sig() {
         return None;
     }
-    let e = e as i16; // Can't overflow because e.abs() <= LOG5_OF_EXP_N
     // The case e < 0 cannot be folded into the other branch. Negative powers result in
     // a repeating fractional part in binary, which are rounded, which causes real
     // (and occasioally quite significant!) errors in the final result.
-    // The case `e == 0`, however, is unnecessary for correctness. It's just measurably faster.
-    if e == 0 {
-        Some(T::from_int(f))
-    } else if e > 0 {
-        Some(T::from_int(f) * fp_to_float(power_of_ten(e)))
+    if e >= 0 {
+        Some(T::from_int(f) * T::short_fast_pow10(e as usize))
     } else {
-        Some(T::from_int(f) / fp_to_float(power_of_ten(-e)))
+        Some(T::from_int(f) / T::short_fast_pow10(e.abs() as usize))
     }
 }
 
index 55be4cd31910baedc5b547c1b9a1a0f3b728efd5..6acc621a6137630e4b11faa6cc74e538894b6822 100644 (file)
@@ -96,8 +96,7 @@ use prelude::v1::*;
 use fmt;
 use str::FromStr;
 
-use self::parse::{parse_decimal, Decimal, Sign};
-use self::parse::ParseResult::{self, Valid, ShortcutToInf, ShortcutToZero};
+use self::parse::{parse_decimal, Decimal, Sign, ParseResult};
 use self::num::digits_to_big;
 use self::rawfp::RawFloat;
 
@@ -109,7 +108,7 @@ pub mod rawfp;
 pub mod parse;
 
 macro_rules! from_str_float_impl {
-    ($t:ty, $func:ident) => {
+    ($t:ty) => {
         #[stable(feature = "rust1", since = "1.0.0")]
         impl FromStr for $t {
             type Err = ParseFloatError;
@@ -146,8 +145,8 @@ macro_rules! from_str_float_impl {
         }
     }
 }
-from_str_float_impl!(f32, to_f32);
-from_str_float_impl!(f64, to_f64);
+from_str_float_impl!(f32);
+from_str_float_impl!(f64);
 
 /// An error which can be returned when parsing a float.
 #[derive(Debug, Clone, PartialEq)]
@@ -183,11 +182,11 @@ impl fmt::Display for ParseFloatError {
     }
 }
 
-pub fn pfe_empty() -> ParseFloatError {
+fn pfe_empty() -> ParseFloatError {
     ParseFloatError { kind: FloatErrorKind::Empty }
 }
 
-pub fn pfe_invalid() -> ParseFloatError {
+fn pfe_invalid() -> ParseFloatError {
     ParseFloatError { kind: FloatErrorKind::Invalid }
 }
 
@@ -208,9 +207,9 @@ fn dec2flt<T: RawFloat>(s: &str) -> Result<T, ParseFloatError> {
     }
     let (sign, s) = extract_sign(s);
     let flt = match parse_decimal(s) {
-        Valid(decimal) => try!(convert(decimal)),
-        ShortcutToInf => T::infinity(),
-        ShortcutToZero => T::zero(),
+        ParseResult::Valid(decimal) => try!(convert(decimal)),
+        ParseResult::ShortcutToInf => T::infinity(),
+        ParseResult::ShortcutToZero => T::zero(),
         ParseResult::Invalid => match s {
             "inf" => T::infinity(),
             "NaN" => T::nan(),
index 414bcc874eac1ca320e7dac04aedc9a03516ce43..fce1c250a022e0f44075f52f940d09ffb5b893e6 100644 (file)
@@ -56,27 +56,28 @@ pub enum ParseResult<'a> {
 /// Check if the input string is a valid floating point number and if so, locate the integral
 /// part, the fractional part, and the exponent in it. Does not handle signs.
 pub fn parse_decimal(s: &str) -> ParseResult {
+    if s.is_empty() {
+        return Invalid;
+    }
+
     let s = s.as_bytes();
     let (integral, s) = eat_digits(s);
+
     match s.first() {
-        None => {
-            if integral.is_empty() {
-                return Invalid; // No digits at all
-            }
-            Valid(Decimal::new(integral, b"", 0))
-        }
+        None => Valid(Decimal::new(integral, b"", 0)),
         Some(&b'e') | Some(&b'E') => {
             if integral.is_empty() {
                 return Invalid; // No digits before 'e'
             }
+
             parse_exp(integral, b"", &s[1..])
         }
         Some(&b'.') => {
             let (fractional, s) = eat_digits(&s[1..]);
             if integral.is_empty() && fractional.is_empty() && s.is_empty() {
-                // For historic reasons "." is a valid input.
-                return Valid(Decimal::new(b"", b"", 0));
+                return Invalid;
             }
+
             match s.first() {
                 None => Valid(Decimal::new(integral, fractional, 0)),
                 Some(&b'e') | Some(&b'E') => parse_exp(integral, fractional, &s[1..]),
index be61653c37937f161e4cffad5fbf90b2abe96007..2099c6a7baa7649960065f6688d4fae745339368 100644 (file)
@@ -37,6 +37,7 @@ use num::diy_float::Fp;
 use num::FpCategory::{Infinite, Zero, Subnormal, Normal, Nan};
 use num::Float;
 use num::dec2flt::num::{self, Big};
+use num::dec2flt::table;
 
 #[derive(Copy, Clone, Debug)]
 pub struct Unpacked {
@@ -73,6 +74,9 @@ pub trait RawFloat : Float + Copy + Debug + LowerExp
     /// represented, the other code in this module makes sure to never let that happen.
     fn from_int(x: u64) -> Self;
 
+    /// Get the value 10^e from a pre-computed table. Panics for e >= ceil_log5_of_max_sig().
+    fn short_fast_pow10(e: usize) -> Self;
+
     // FIXME Everything that follows should be associated constants, but taking the value of an
     // associated constant from a type parameter does not work (yet?)
     // A possible workaround is having a `FloatInfo` struct for all the constants, but so far
@@ -175,6 +179,10 @@ impl RawFloat for f32 {
         x as f32
     }
 
+    fn short_fast_pow10(e: usize) -> Self {
+        table::F32_SHORT_POWERS[e]
+    }
+
     fn max_normal_digits() -> usize {
         35
     }
@@ -222,6 +230,10 @@ impl RawFloat for f64 {
         x as f64
     }
 
+    fn short_fast_pow10(e: usize) -> Self {
+        table::F64_SHORT_POWERS[e]
+    }
+
     fn max_normal_digits() -> usize {
         305
     }
@@ -288,7 +300,7 @@ pub fn encode_normal<T: RawFloat>(x: Unpacked) -> T {
 /// Construct the subnormal. A mantissa of 0 is allowed and constructs zero.
 pub fn encode_subnormal<T: RawFloat>(significand: u64) -> T {
     assert!(significand < T::min_sig(), "encode_subnormal: not actually subnormal");
-    // Êncoded exponent is 0, the sign bit is 0, so we just have to reinterpret the bits.
+    // Encoded exponent is 0, the sign bit is 0, so we just have to reinterpret the bits.
     T::from_bits(significand)
 }
 
index dd985fd155b850417cd3326d7ffcadac407f4b4f..cb8c94313d030cb526a427bdabbf19cd6d8794c0 100644 (file)
@@ -7,8 +7,10 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
-// Table of approximations of powers of ten.
-// DO NOT MODIFY: Generated by a src/etc/dec2flt_table.py
+
+//! Tables of approximations of powers of ten.
+//! DO NOT MODIFY: Generated by `src/etc/dec2flt_table.py`
+
 pub const MIN_E: i16 = -305;
 pub const MAX_E: i16 = 305;
 
@@ -1237,3 +1239,43 @@ pub const POWERS: ([u64; 611], [i16; 611]) = ([
     946,
     950,
 ]);
+
+pub const F32_SHORT_POWERS: [f32; 11] = [
+    1e0,
+    1e1,
+    1e2,
+    1e3,
+    1e4,
+    1e5,
+    1e6,
+    1e7,
+    1e8,
+    1e9,
+    1e10,
+];
+
+pub const F64_SHORT_POWERS: [f64; 23] = [
+    1e0,
+    1e1,
+    1e2,
+    1e3,
+    1e4,
+    1e5,
+    1e6,
+    1e7,
+    1e8,
+    1e9,
+    1e10,
+    1e11,
+    1e12,
+    1e13,
+    1e14,
+    1e15,
+    1e16,
+    1e17,
+    1e18,
+    1e19,
+    1e20,
+    1e21,
+    1e22,
+];
index 359d15640f978073c359d016a3d2da04aae072af..8af1022acdf24bdb7150653dffb213fe59501500 100644 (file)
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
-use prelude::v1::*;
-
 use intrinsics;
 use mem;
-use num::{Float, ParseFloatError};
+use num::Float;
 use num::FpCategory as Fp;
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -163,8 +161,6 @@ impl Float for f32 {
     #[inline]
     fn one() -> f32 { 1.0 }
 
-    from_str_radix_float_impl! { f32 }
-
     /// Returns `true` if the number is NaN.
     #[inline]
     fn is_nan(self) -> bool { self != self }
index 1a6acc5f4ab11a2b0c34b05fbaa85da5b514ea5e..9486e4337bf58f56dcbd38b6527c68c73174ec5f 100644 (file)
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
-use prelude::v1::*;
-
 use intrinsics;
 use mem;
 use num::FpCategory as Fp;
-use num::{Float, ParseFloatError};
+use num::Float;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 #[allow(missing_docs)]
@@ -163,8 +161,6 @@ impl Float for f64 {
     #[inline]
     fn one() -> f64 { 1.0 }
 
-    from_str_radix_float_impl! { f64 }
-
     /// Returns `true` if the number is NaN.
     #[inline]
     fn is_nan(self) -> bool { self != self }
index 88c3b756793a2b9bb9dd4664024f69c4f4a01ea7..b3adef53dabeebc8a7a03ed478fb691dd7ed28d6 100644 (file)
@@ -18,144 +18,3 @@ macro_rules! assert_approx_eq {
                 "{} is not approximately equal to {}", *a, *b);
     })
 }
-
-macro_rules! from_str_radix_float_impl {
-    ($T:ty) => {
-        fn from_str_radix(src: &str, radix: u32)
-                          -> Result<$T, ParseFloatError> {
-            use num::dec2flt::{pfe_empty, pfe_invalid};
-
-            // Special values
-            match src {
-                "inf"   => return Ok(Float::infinity()),
-                "-inf"  => return Ok(Float::neg_infinity()),
-                "NaN"   => return Ok(Float::nan()),
-                _       => {},
-            }
-
-            let (is_positive, src) =  match src.slice_shift_char() {
-                None             => return Err(pfe_empty()),
-                Some(('-', ""))  => return Err(pfe_empty()),
-                Some(('-', src)) => (false, src),
-                Some((_, _))     => (true,  src),
-            };
-
-            // The significand to accumulate
-            let mut sig = if is_positive { 0.0 } else { -0.0 };
-            // Necessary to detect overflow
-            let mut prev_sig = sig;
-            let mut cs = src.chars().enumerate();
-            // Exponent prefix and exponent index offset
-            let mut exp_info = None::<(char, usize)>;
-
-            // Parse the integer part of the significand
-            for (i, c) in cs.by_ref() {
-                match c.to_digit(radix) {
-                    Some(digit) => {
-                        // shift significand one digit left
-                        sig = sig * (radix as $T);
-
-                        // add/subtract current digit depending on sign
-                        if is_positive {
-                            sig = sig + ((digit as isize) as $T);
-                        } else {
-                            sig = sig - ((digit as isize) as $T);
-                        }
-
-                        // Detect overflow by comparing to last value, except
-                        // if we've not seen any non-zero digits.
-                        if prev_sig != 0.0 {
-                            if is_positive && sig <= prev_sig
-                                { return Ok(Float::infinity()); }
-                            if !is_positive && sig >= prev_sig
-                                { return Ok(Float::neg_infinity()); }
-
-                            // Detect overflow by reversing the shift-and-add process
-                            if is_positive && (prev_sig != (sig - digit as $T) / radix as $T)
-                                { return Ok(Float::infinity()); }
-                            if !is_positive && (prev_sig != (sig + digit as $T) / radix as $T)
-                                { return Ok(Float::neg_infinity()); }
-                        }
-                        prev_sig = sig;
-                    },
-                    None => match c {
-                        'e' | 'E' | 'p' | 'P' => {
-                            exp_info = Some((c, i + 1));
-                            break;  // start of exponent
-                        },
-                        '.' => {
-                            break;  // start of fractional part
-                        },
-                        _ => {
-                            return Err(pfe_invalid())
-                        },
-                    },
-                }
-            }
-
-            // If we are not yet at the exponent parse the fractional
-            // part of the significand
-            if exp_info.is_none() {
-                let mut power = 1.0;
-                for (i, c) in cs.by_ref() {
-                    match c.to_digit(radix) {
-                        Some(digit) => {
-                            // Decrease power one order of magnitude
-                            power = power / (radix as $T);
-                            // add/subtract current digit depending on sign
-                            sig = if is_positive {
-                                sig + (digit as $T) * power
-                            } else {
-                                sig - (digit as $T) * power
-                            };
-                            // Detect overflow by comparing to last value
-                            if is_positive && sig < prev_sig
-                                { return Ok(Float::infinity()); }
-                            if !is_positive && sig > prev_sig
-                                { return Ok(Float::neg_infinity()); }
-                            prev_sig = sig;
-                        },
-                        None => match c {
-                            'e' | 'E' | 'p' | 'P' => {
-                                exp_info = Some((c, i + 1));
-                                break; // start of exponent
-                            },
-                            _ => {
-                                return Err(pfe_invalid())
-                            },
-                        },
-                    }
-                }
-            }
-
-            // Parse and calculate the exponent
-            let exp = match exp_info {
-                Some((c, offset)) => {
-                    let base = match c {
-                        'E' | 'e' if radix == 10 => 10.0,
-                        'P' | 'p' if radix == 16 => 2.0,
-                        _ => return Err(pfe_invalid()),
-                    };
-
-                    // Parse the exponent as decimal integer
-                    let src = &src[offset..];
-                    let (is_positive, exp) = match src.slice_shift_char() {
-                        Some(('-', src)) => (false, src.parse::<usize>()),
-                        Some(('+', src)) => (true,  src.parse::<usize>()),
-                        Some((_, _))     => (true,  src.parse::<usize>()),
-                        None             => return Err(pfe_invalid()),
-                    };
-
-                    match (is_positive, exp) {
-                        (true,  Ok(exp)) => base.powi(exp as i32),
-                        (false, Ok(exp)) => 1.0 / base.powi(exp as i32),
-                        (_, Err(_))      => return Err(pfe_invalid()),
-                    }
-                },
-                None => 1.0, // no exponent
-            };
-
-            Ok(sig * exp)
-        }
-    }
-}
index 46f3c463ff06f10be25546c5a68cd4ad1a4f4fcb..9f7672a52a1832d6d572b69be20c7410dbb1e86a 100644 (file)
@@ -210,7 +210,7 @@ impl<'a> Part<'a> {
                     }
                 }
                 Part::Copy(buf) => {
-                    out.clone_from_slice(buf);
+                    out[..buf.len()].clone_from_slice(buf);
                 }
             }
             Some(len)
@@ -245,7 +245,7 @@ impl<'a> Formatted<'a> {
     /// (It may still leave partially written bytes in the buffer; do not rely on that.)
     pub fn write(&self, out: &mut [u8]) -> Option<usize> {
         if out.len() < self.sign.len() { return None; }
-        out.clone_from_slice(self.sign);
+        out[..self.sign.len()].clone_from_slice(self.sign);
 
         let mut written = self.sign.len();
         for part in self.parts {
index 61dcbdff0169e326bf3707f4bab3f07f9c60b3d7..77f662723c86dab6f3ca7868a12960aa09efbca3 100644 (file)
@@ -17,6 +17,8 @@ macro_rules! int_module { ($T:ty, $bits:expr) => (
 #[unstable(feature = "num_bits_bytes",
            reason = "may want to be an associated function",
            issue = "27753")]
+#[rustc_deprecated(since = "1.7.0",
+                   reason = "will be replaced via const fn or associated constants")]
 #[allow(missing_docs)]
 pub const BITS : usize = $bits;
 // FIXME(#11621): Should be deprecated once CTFE is implemented in favour of
@@ -24,6 +26,8 @@ pub const BITS : usize = $bits;
 #[unstable(feature = "num_bits_bytes",
            reason = "may want to be an associated function",
            issue = "27753")]
+#[rustc_deprecated(since = "1.7.0",
+                   reason = "will be replaced via const fn or associated constants")]
 #[allow(missing_docs)]
 pub const BYTES : usize = ($bits / 8);
 
@@ -31,7 +35,7 @@ pub const BYTES : usize = ($bits / 8);
 // 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);
+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.
index e1e5c01adb705eb572806c2c6bb0348270e24620..99a74cf09f571ac5987dab52b02ce18302fdaa6b 100644 (file)
@@ -13,8 +13,6 @@
 #![stable(feature = "rust1", since = "1.0.0")]
 #![allow(missing_docs)]
 
-use self::wrapping::OverflowingOps;
-
 use char::CharExt;
 use cmp::{Eq, PartialOrd};
 use convert::From;
@@ -115,11 +113,6 @@ macro_rules! zero_one_impl_float {
 }
 zero_one_impl_float! { f32 f64 }
 
-// Just for stage0; a byte swap on a byte is a no-op
-// Delete this once it becomes unused
-#[cfg(stage0)]
-unsafe fn bswap8(x: u8) -> u8 { x }
-
 macro_rules! checked_op {
     ($U:ty, $op:path, $x:expr, $y:expr) => {{
         let (result, overflowed) = unsafe { $op($x as $U, $y as $U) };
@@ -129,7 +122,7 @@ macro_rules! checked_op {
 
 // `Int` + `SignedInt` implemented for signed integers
 macro_rules! int_impl {
-    ($ActualT:ty, $UnsignedT:ty, $BITS:expr,
+    ($ActualT:ident, $UnsignedT:ty, $BITS:expr,
      $add_with_overflow:path,
      $sub_with_overflow:path,
      $mul_with_overflow:path) => {
@@ -398,7 +391,8 @@ macro_rules! int_impl {
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
         pub fn checked_add(self, other: Self) -> Option<Self> {
-            checked_op!($ActualT, $add_with_overflow, self, other)
+            let (a, b) = self.overflowing_add(other);
+            if b {None} else {Some(a)}
         }
 
         /// Checked integer subtraction. Computes `self - other`, returning
@@ -415,7 +409,8 @@ macro_rules! int_impl {
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
         pub fn checked_sub(self, other: Self) -> Option<Self> {
-            checked_op!($ActualT, $sub_with_overflow, self, other)
+            let (a, b) = self.overflowing_sub(other);
+            if b {None} else {Some(a)}
         }
 
         /// Checked integer multiplication. Computes `self * other`, returning
@@ -432,7 +427,8 @@ macro_rules! int_impl {
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
         pub fn checked_mul(self, other: Self) -> Option<Self> {
-            checked_op!($ActualT, $mul_with_overflow, self, other)
+            let (a, b) = self.overflowing_mul(other);
+            if b {None} else {Some(a)}
         }
 
         /// Checked integer division. Computes `self / other`, returning `None`
@@ -450,14 +446,95 @@ macro_rules! int_impl {
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
         pub fn checked_div(self, other: Self) -> Option<Self> {
-            match other {
-                0    => None,
-               -1 if self == Self::min_value()
-                     => None,
-               other => Some(self / other),
+            if other == 0 {
+                None
+            } else {
+                let (a, b) = self.overflowing_div(other);
+                if b {None} else {Some(a)}
+            }
+        }
+
+        /// Checked integer remainder. Computes `self % other`, returning `None`
+        /// if `other == 0` or the operation results in underflow or overflow.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// use std::i32;
+        ///
+        /// assert_eq!(5i32.checked_rem(2), Some(1));
+        /// assert_eq!(5i32.checked_rem(0), None);
+        /// assert_eq!(i32::MIN.checked_rem(-1), None);
+        /// ```
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        #[inline]
+        pub fn checked_rem(self, other: Self) -> Option<Self> {
+            if other == 0 {
+                None
+            } else {
+                let (a, b) = self.overflowing_rem(other);
+                if b {None} else {Some(a)}
             }
         }
 
+        /// Checked negation. Computes `!self`, returning `None` if `self ==
+        /// MIN`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// use std::i32;
+        ///
+        /// assert_eq!(5i32.checked_neg(), Some(-5));
+        /// assert_eq!(i32::MIN.checked_neg(), None);
+        /// ```
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        #[inline]
+        pub fn checked_neg(self) -> Option<Self> {
+            let (a, b) = self.overflowing_neg();
+            if b {None} else {Some(a)}
+        }
+
+        /// Checked shift left. Computes `self << rhs`, returning `None`
+        /// if `rhs` is larger than or equal to the number of bits in `self`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// assert_eq!(0x10i32.checked_shl(4), Some(0x100));
+        /// assert_eq!(0x10i32.checked_shl(33), None);
+        /// ```
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        #[inline]
+        pub fn checked_shl(self, rhs: u32) -> Option<Self> {
+            let (a, b) = self.overflowing_shl(rhs);
+            if b {None} else {Some(a)}
+        }
+
+        /// Checked shift right. Computes `self >> rhs`, returning `None`
+        /// if `rhs` is larger than or equal to the number of bits in `self`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// assert_eq!(0x10i32.checked_shr(4), Some(0x1));
+        /// assert_eq!(0x10i32.checked_shr(33), None);
+        /// ```
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        #[inline]
+        pub fn checked_shr(self, rhs: u32) -> Option<Self> {
+            let (a, b) = self.overflowing_shr(rhs);
+            if b {None} else {Some(a)}
+        }
+
         /// Saturating integer addition. Computes `self + other`, saturating at
         /// the numeric bounds instead of overflowing.
         ///
@@ -473,7 +550,7 @@ macro_rules! int_impl {
         #[inline]
         pub fn saturating_add(self, other: Self) -> Self {
             match self.checked_add(other) {
-                Some(x)                       => x,
+                Some(x) => x,
                 None if other >= Self::zero() => Self::max_value(),
                 None => Self::min_value(),
             }
@@ -494,12 +571,38 @@ macro_rules! int_impl {
         #[inline]
         pub fn saturating_sub(self, other: Self) -> Self {
             match self.checked_sub(other) {
-                Some(x)                      => x,
+                Some(x) => x,
                 None if other >= Self::zero() => Self::min_value(),
                 None => Self::max_value(),
             }
         }
 
+        /// Saturating integer multiplication. Computes `self * other`,
+        /// saturating at the numeric bounds instead of overflowing.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// use std::i32;
+        ///
+        /// assert_eq!(100i32.saturating_mul(127), 12700);
+        /// assert_eq!((1i32 << 23).saturating_mul(1 << 23), i32::MAX);
+        /// assert_eq!((-1i32 << 23).saturating_mul(1 << 23), i32::MIN);
+        /// ```
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        #[inline]
+        pub fn saturating_mul(self, other: Self) -> Self {
+            self.checked_mul(other).unwrap_or_else(|| {
+                if (self < 0 && other < 0) || (self > 0 && other > 0) {
+                    Self::max_value()
+                } else {
+                    Self::min_value()
+                }
+            })
+        }
+
         /// Wrapping (modular) addition. Computes `self + other`,
         /// wrapping around at the boundary of the type.
         ///
@@ -567,6 +670,10 @@ macro_rules! int_impl {
         /// in the type. In such a case, this function returns `MIN`
         /// itself.
         ///
+        /// # Panics
+        ///
+        /// This function will panic if `rhs` is 0.
+        ///
         /// # Examples
         ///
         /// Basic usage:
@@ -589,6 +696,10 @@ macro_rules! int_impl {
         /// -1` on a signed type (where `MIN` is the negative
         /// minimal value). In such a case, this function returns `0`.
         ///
+        /// # Panics
+        ///
+        /// This function will panic if `rhs` is 0.
+        ///
         /// # Examples
         ///
         /// Basic usage:
@@ -644,7 +755,7 @@ macro_rules! int_impl {
             self.overflowing_shl(rhs).0
         }
 
-        /// Panic-free bitwise shift-left; yields `self >> mask(rhs)`,
+        /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`,
         /// where `mask` removes any high-order bits of `rhs` that
         /// would cause the shift to exceed the bitwidth of the type.
         ///
@@ -662,6 +773,214 @@ macro_rules! int_impl {
             self.overflowing_shr(rhs).0
         }
 
+        /// Calculates `self` + `rhs`
+        ///
+        /// Returns a tuple of the addition along with a boolean indicating
+        /// whether an arithmetic overflow would occur. If an overflow would
+        /// have occurred then the wrapped value is returned.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// use std::i32;
+        ///
+        /// assert_eq!(5i32.overflowing_add(2), (7, false));
+        /// assert_eq!(i32::MAX.overflowing_add(1), (i32::MIN, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_add(self, rhs: Self) -> (Self, bool) {
+            unsafe {
+                let (a, b) = $add_with_overflow(self as $ActualT,
+                                                rhs as $ActualT);
+                (a as Self, b)
+            }
+        }
+
+        /// Calculates `self` - `rhs`
+        ///
+        /// Returns a tuple of the subtraction along with a boolean indicating
+        /// whether an arithmetic overflow would occur. If an overflow would
+        /// have occurred then the wrapped value is returned.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// use std::i32;
+        ///
+        /// assert_eq!(5i32.overflowing_sub(2), (3, false));
+        /// assert_eq!(i32::MIN.overflowing_sub(1), (i32::MAX, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
+            unsafe {
+                let (a, b) = $sub_with_overflow(self as $ActualT,
+                                                rhs as $ActualT);
+                (a as Self, b)
+            }
+        }
+
+        /// Calculates the multiplication of `self` and `rhs`.
+        ///
+        /// Returns a tuple of the multiplication along with a boolean
+        /// indicating whether an arithmetic overflow would occur. If an
+        /// overflow would have occurred then the wrapped value is returned.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// assert_eq!(5i32.overflowing_mul(2), (10, false));
+        /// assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
+            unsafe {
+                let (a, b) = $mul_with_overflow(self as $ActualT,
+                                                rhs as $ActualT);
+                (a as Self, b)
+            }
+        }
+
+        /// Calculates the divisor when `self` is divided by `rhs`.
+        ///
+        /// Returns a tuple of the divisor along with a boolean indicating
+        /// whether an arithmetic overflow would occur. If an overflow would
+        /// occur then self is returned.
+        ///
+        /// # Panics
+        ///
+        /// This function will panic if `rhs` is 0.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// use std::i32;
+        ///
+        /// assert_eq!(5i32.overflowing_div(2), (2, false));
+        /// assert_eq!(i32::MIN.overflowing_div(-1), (i32::MIN, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_div(self, rhs: Self) -> (Self, bool) {
+            if self == Self::min_value() && rhs == -1 {
+                (self, true)
+            } else {
+                (self / rhs, false)
+            }
+        }
+
+        /// Calculates the remainder when `self` is divided by `rhs`.
+        ///
+        /// Returns a tuple of the remainder after dividing along with a boolean
+        /// indicating whether an arithmetic overflow would occur. If an
+        /// overflow would occur then 0 is returned.
+        ///
+        /// # Panics
+        ///
+        /// This function will panic if `rhs` is 0.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// use std::i32;
+        ///
+        /// assert_eq!(5i32.overflowing_rem(2), (1, false));
+        /// assert_eq!(i32::MIN.overflowing_rem(-1), (0, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_rem(self, rhs: Self) -> (Self, bool) {
+            if self == Self::min_value() && rhs == -1 {
+                (0, true)
+            } else {
+                (self % rhs, false)
+            }
+        }
+
+        /// Negates self, overflowing if this is equal to the minimum value.
+        ///
+        /// Returns a tuple of the negated version of self along with a boolean
+        /// indicating whether an overflow happened. If `self` is the minimum
+        /// value (e.g. `i32::MIN` for values of type `i32`), then the minimum
+        /// value will be returned again and `true` will be returned for an
+        /// overflow happening.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// use std::i32;
+        ///
+        /// assert_eq!(2i32.overflowing_neg(), (-2, false));
+        /// assert_eq!(i32::MIN.overflowing_neg(), (i32::MIN, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_neg(self) -> (Self, bool) {
+            if self == Self::min_value() {
+                (Self::min_value(), true)
+            } else {
+                (-self, false)
+            }
+        }
+
+        /// Shifts self left by `rhs` bits.
+        ///
+        /// Returns a tuple of the shifted version of self along with a boolean
+        /// indicating whether the shift value was larger than or equal to the
+        /// number of bits. If the shift value is too large, then value is
+        /// masked (N-1) where N is the number of bits, and this value is then
+        /// used to perform the shift.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// assert_eq!(0x10i32.overflowing_shl(4), (0x100, false));
+        /// assert_eq!(0x10i32.overflowing_shl(36), (0x100, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_shl(self, rhs: u32) -> (Self, bool) {
+            (self << (rhs & ($BITS - 1)), (rhs > ($BITS - 1)))
+        }
+
+        /// Shifts self right by `rhs` bits.
+        ///
+        /// Returns a tuple of the shifted version of self along with a boolean
+        /// indicating whether the shift value was larger than or equal to the
+        /// number of bits. If the shift value is too large, then value is
+        /// masked (N-1) where N is the number of bits, and this value is then
+        /// used to perform the shift.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// assert_eq!(0x10i32.overflowing_shr(4), (0x1, false));
+        /// assert_eq!(0x10i32.overflowing_shr(36), (0x1, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_shr(self, rhs: u32) -> (Self, bool) {
+            (self >> (rhs & ($BITS - 1)), (rhs > ($BITS - 1)))
+        }
+
         /// Raises self to the power of `exp`, using exponentiation by squaring.
         ///
         /// # Examples
@@ -785,15 +1104,6 @@ macro_rules! int_impl {
 }
 
 #[lang = "i8"]
-#[cfg(stage0)]
-impl i8 {
-    int_impl! { i8, u8, 8,
-        intrinsics::i8_add_with_overflow,
-        intrinsics::i8_sub_with_overflow,
-        intrinsics::i8_mul_with_overflow }
-}
-#[lang = "i8"]
-#[cfg(not(stage0))]
 impl i8 {
     int_impl! { i8, u8, 8,
         intrinsics::add_with_overflow,
@@ -802,15 +1112,6 @@ impl i8 {
 }
 
 #[lang = "i16"]
-#[cfg(stage0)]
-impl i16 {
-    int_impl! { i16, u16, 16,
-        intrinsics::i16_add_with_overflow,
-        intrinsics::i16_sub_with_overflow,
-        intrinsics::i16_mul_with_overflow }
-}
-#[lang = "i16"]
-#[cfg(not(stage0))]
 impl i16 {
     int_impl! { i16, u16, 16,
         intrinsics::add_with_overflow,
@@ -819,15 +1120,6 @@ impl i16 {
 }
 
 #[lang = "i32"]
-#[cfg(stage0)]
-impl i32 {
-    int_impl! { i32, u32, 32,
-        intrinsics::i32_add_with_overflow,
-        intrinsics::i32_sub_with_overflow,
-        intrinsics::i32_mul_with_overflow }
-}
-#[lang = "i32"]
-#[cfg(not(stage0))]
 impl i32 {
     int_impl! { i32, u32, 32,
         intrinsics::add_with_overflow,
@@ -836,15 +1128,6 @@ impl i32 {
 }
 
 #[lang = "i64"]
-#[cfg(stage0)]
-impl i64 {
-    int_impl! { i64, u64, 64,
-        intrinsics::i64_add_with_overflow,
-        intrinsics::i64_sub_with_overflow,
-        intrinsics::i64_mul_with_overflow }
-}
-#[lang = "i64"]
-#[cfg(not(stage0))]
 impl i64 {
     int_impl! { i64, u64, 64,
         intrinsics::add_with_overflow,
@@ -854,16 +1137,6 @@ impl i64 {
 
 #[cfg(target_pointer_width = "32")]
 #[lang = "isize"]
-#[cfg(stage0)]
-impl isize {
-    int_impl! { i32, u32, 32,
-        intrinsics::i32_add_with_overflow,
-        intrinsics::i32_sub_with_overflow,
-        intrinsics::i32_mul_with_overflow }
-}
-#[cfg(target_pointer_width = "32")]
-#[lang = "isize"]
-#[cfg(not(stage0))]
 impl isize {
     int_impl! { i32, u32, 32,
         intrinsics::add_with_overflow,
@@ -873,16 +1146,6 @@ impl isize {
 
 #[cfg(target_pointer_width = "64")]
 #[lang = "isize"]
-#[cfg(stage0)]
-impl isize {
-    int_impl! { i64, u64, 64,
-        intrinsics::i64_add_with_overflow,
-        intrinsics::i64_sub_with_overflow,
-        intrinsics::i64_mul_with_overflow }
-}
-#[cfg(target_pointer_width = "64")]
-#[lang = "isize"]
-#[cfg(not(stage0))]
 impl isize {
     int_impl! { i64, u64, 64,
         intrinsics::add_with_overflow,
@@ -890,7 +1153,7 @@ impl isize {
         intrinsics::mul_with_overflow }
 }
 
-// `Int` + `UnsignedInt` implemented for signed integers
+// `Int` + `UnsignedInt` implemented for unsigned integers
 macro_rules! uint_impl {
     ($ActualT:ty, $BITS:expr,
      $ctpop:path,
@@ -980,25 +1243,6 @@ macro_rules! uint_impl {
             unsafe { $ctlz(self as $ActualT) as u32 }
         }
 
-        #[stable(feature = "rust1", since = "1.0.0")]
-        #[cfg(stage0)]
-        #[inline]
-        pub fn trailing_zeros(self) -> u32 {
-            // As of LLVM 3.6 the codegen for the zero-safe cttz8 intrinsic
-            // emits two conditional moves on x86_64. By promoting the value to
-            // u16 and setting bit 8, we get better code without any conditional
-            // operations.
-            // FIXME: There's a LLVM patch (http://reviews.llvm.org/D9284)
-            // pending, remove this workaround once LLVM generates better code
-            // for cttz8.
-            unsafe {
-                if $BITS == 8 {
-                    intrinsics::cttz16(self as u16 | 0x100) as u32
-                } else {
-                    $cttz(self as $ActualT) as u32
-                }
-            }
-        }
         /// Returns the number of trailing zeros in the binary representation
         /// of `self`.
         ///
@@ -1012,7 +1256,6 @@ macro_rules! uint_impl {
         /// assert_eq!(n.trailing_zeros(), 3);
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
-        #[cfg(not(stage0))]
         #[inline]
         pub fn trailing_zeros(self) -> u32 {
             // As of LLVM 3.6 the codegen for the zero-safe cttz8 intrinsic
@@ -1202,7 +1445,8 @@ macro_rules! uint_impl {
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
         pub fn checked_add(self, other: Self) -> Option<Self> {
-            checked_op!($ActualT, $add_with_overflow, self, other)
+            let (a, b) = self.overflowing_add(other);
+            if b {None} else {Some(a)}
         }
 
         /// Checked integer subtraction. Computes `self - other`, returning
@@ -1213,13 +1457,14 @@ macro_rules! uint_impl {
         /// Basic usage:
         ///
         /// ```
-        /// assert_eq!((-127i8).checked_sub(1), Some(-128));
-        /// assert_eq!((-128i8).checked_sub(1), None);
+        /// assert_eq!(1u8.checked_sub(1), Some(0));
+        /// assert_eq!(0u8.checked_sub(1), None);
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
         pub fn checked_sub(self, other: Self) -> Option<Self> {
-            checked_op!($ActualT, $sub_with_overflow, self, other)
+            let (a, b) = self.overflowing_sub(other);
+            if b {None} else {Some(a)}
         }
 
         /// Checked integer multiplication. Computes `self * other`, returning
@@ -1236,7 +1481,8 @@ macro_rules! uint_impl {
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
         pub fn checked_mul(self, other: Self) -> Option<Self> {
-            checked_op!($ActualT, $mul_with_overflow, self, other)
+            let (a, b) = self.overflowing_mul(other);
+            if b {None} else {Some(a)}
         }
 
         /// Checked integer division. Computes `self / other`, returning `None`
@@ -1247,9 +1493,8 @@ macro_rules! uint_impl {
         /// Basic usage:
         ///
         /// ```
-        /// assert_eq!((-127i8).checked_div(-1), Some(127));
-        /// assert_eq!((-128i8).checked_div(-1), None);
-        /// assert_eq!((1i8).checked_div(0), None);
+        /// assert_eq!(128u8.checked_div(2), Some(64));
+        /// assert_eq!(1u8.checked_div(0), None);
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
@@ -1260,6 +1505,83 @@ macro_rules! uint_impl {
             }
         }
 
+        /// Checked integer remainder. Computes `self % other`, returning `None`
+        /// if `other == 0` or the operation results in underflow or overflow.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// assert_eq!(5u32.checked_rem(2), Some(1));
+        /// assert_eq!(5u32.checked_rem(0), None);
+        /// ```
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        #[inline]
+        pub fn checked_rem(self, other: Self) -> Option<Self> {
+            if other == 0 {
+                None
+            } else {
+                Some(self % other)
+            }
+        }
+
+        /// Checked negation. Computes `-self`, returning `None` unless `self ==
+        /// 0`.
+        ///
+        /// Note that negating any positive integer will overflow.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// assert_eq!(0u32.checked_neg(), Some(0));
+        /// assert_eq!(1u32.checked_neg(), None);
+        /// ```
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        #[inline]
+        pub fn checked_neg(self) -> Option<Self> {
+            let (a, b) = self.overflowing_neg();
+            if b {None} else {Some(a)}
+        }
+
+        /// Checked shift left. Computes `self << rhs`, returning `None`
+        /// if `rhs` is larger than or equal to the number of bits in `self`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// assert_eq!(0x10u32.checked_shl(4), Some(0x100));
+        /// assert_eq!(0x10u32.checked_shl(33), None);
+        /// ```
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        #[inline]
+        pub fn checked_shl(self, rhs: u32) -> Option<Self> {
+            let (a, b) = self.overflowing_shl(rhs);
+            if b {None} else {Some(a)}
+        }
+
+        /// Checked shift right. Computes `self >> rhs`, returning `None`
+        /// if `rhs` is larger than or equal to the number of bits in `self`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// assert_eq!(0x10u32.checked_shr(4), Some(0x1));
+        /// assert_eq!(0x10u32.checked_shr(33), None);
+        /// ```
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        #[inline]
+        pub fn checked_shr(self, rhs: u32) -> Option<Self> {
+            let (a, b) = self.overflowing_shr(rhs);
+            if b {None} else {Some(a)}
+        }
+
         /// Saturating integer addition. Computes `self + other`, saturating at
         /// the numeric bounds instead of overflowing.
         ///
@@ -1268,16 +1590,15 @@ macro_rules! uint_impl {
         /// Basic usage:
         ///
         /// ```
-        /// assert_eq!(100i8.saturating_add(1), 101);
-        /// assert_eq!(100i8.saturating_add(127), 127);
+        /// assert_eq!(100u8.saturating_add(1), 101);
+        /// assert_eq!(200u8.saturating_add(127), 255);
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
         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 => Self::min_value(),
+                Some(x) => x,
+                None => Self::max_value(),
             }
         }
 
@@ -1289,19 +1610,37 @@ macro_rules! uint_impl {
         /// Basic usage:
         ///
         /// ```
-        /// assert_eq!(100i8.saturating_sub(127), -27);
-        /// assert_eq!((-100i8).saturating_sub(127), -128);
+        /// assert_eq!(100u8.saturating_sub(27), 73);
+        /// assert_eq!(13u8.saturating_sub(127), 0);
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
         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 => Self::max_value(),
+                Some(x) => x,
+                None => Self::min_value(),
             }
         }
 
+        /// Saturating integer multiplication. Computes `self * other`,
+        /// saturating at the numeric bounds instead of overflowing.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// use std::u32;
+        ///
+        /// assert_eq!(100u32.saturating_mul(127), 12700);
+        /// assert_eq!((1u32 << 23).saturating_mul(1 << 23), u32::MAX);
+        /// ```
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        #[inline]
+        pub fn saturating_mul(self, other: Self) -> Self {
+            self.checked_mul(other).unwrap_or(Self::max_value())
+        }
+
         /// Wrapping (modular) addition. Computes `self + other`,
         /// wrapping around at the boundary of the type.
         ///
@@ -1310,8 +1649,8 @@ macro_rules! uint_impl {
         /// Basic usage:
         ///
         /// ```
-        /// assert_eq!(100i8.wrapping_add(27), 127);
-        /// assert_eq!(100i8.wrapping_add(127), -29);
+        /// assert_eq!(200u8.wrapping_add(55), 255);
+        /// assert_eq!(200u8.wrapping_add(155), 99);
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
@@ -1329,8 +1668,8 @@ macro_rules! uint_impl {
         /// Basic usage:
         ///
         /// ```
-        /// assert_eq!(0i8.wrapping_sub(127), -127);
-        /// assert_eq!((-2i8).wrapping_sub(127), 127);
+        /// assert_eq!(100u8.wrapping_sub(100), 0);
+        /// assert_eq!(100u8.wrapping_sub(155), 201);
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
@@ -1348,8 +1687,8 @@ macro_rules! uint_impl {
         /// Basic usage:
         ///
         /// ```
-        /// assert_eq!(10i8.wrapping_mul(12), 120);
-        /// assert_eq!(11i8.wrapping_mul(12), -124);
+        /// assert_eq!(10u8.wrapping_mul(12), 120);
+        /// assert_eq!(25u8.wrapping_mul(12), 44);
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
@@ -1359,15 +1698,11 @@ macro_rules! uint_impl {
             }
         }
 
-        /// Wrapping (modular) division. Computes `self / other`,
-        /// wrapping around at the boundary of the type.
-        ///
-        /// The only case where such wrapping can occur is when one
-        /// divides `MIN / -1` on a signed type (where `MIN` is the
-        /// negative minimal value for the type); this is equivalent
-        /// to `-MIN`, a positive value that is too large to represent
-        /// in the type. In such a case, this function returns `MIN`
-        /// itself.
+        /// Wrapping (modular) division. Computes `self / other`.
+        /// Wrapped division on unsigned types is just normal division.
+        /// There's no way wrapping could ever happen.
+        /// This function exists, so that all operations
+        /// are accounted for in the wrapping operations.
         ///
         /// # Examples
         ///
@@ -1375,21 +1710,19 @@ macro_rules! uint_impl {
         ///
         /// ```
         /// assert_eq!(100u8.wrapping_div(10), 10);
-        /// assert_eq!((-128i8).wrapping_div(-1), -128);
         /// ```
         #[stable(feature = "num_wrapping", since = "1.2.0")]
         #[inline(always)]
         pub fn wrapping_div(self, rhs: Self) -> Self {
-            self.overflowing_div(rhs).0
+            self / rhs
         }
 
-        /// Wrapping (modular) remainder. Computes `self % other`,
-        /// wrapping around at the boundary of the type.
-        ///
-        /// Such wrap-around never actually occurs mathematically;
-        /// implementation artifacts make `x % y` invalid for `MIN /
-        /// -1` on a signed type (where `MIN` is the negative
-        /// minimal value). In such a case, this function returns `0`.
+        /// Wrapping (modular) remainder. Computes `self % other`.
+        /// Wrapped remainder calculation on unsigned types is
+        /// just the regular remainder calculation.
+        /// There's no way wrapping could ever happen.
+        /// This function exists, so that all operations
+        /// are accounted for in the wrapping operations.
         ///
         /// # Examples
         ///
@@ -1397,30 +1730,32 @@ macro_rules! uint_impl {
         ///
         /// ```
         /// assert_eq!(100i8.wrapping_rem(10), 0);
-        /// assert_eq!((-128i8).wrapping_rem(-1), 0);
         /// ```
         #[stable(feature = "num_wrapping", since = "1.2.0")]
         #[inline(always)]
         pub fn wrapping_rem(self, rhs: Self) -> Self {
-            self.overflowing_rem(rhs).0
+            self % rhs
         }
 
         /// Wrapping (modular) negation. Computes `-self`,
         /// wrapping around at the boundary of the type.
         ///
-        /// The only case where such wrapping can occur is when one
-        /// negates `MIN` on a signed type (where `MIN` is the
-        /// negative minimal value for the type); this is a positive
-        /// value that is too large to represent in the type. In such
-        /// a case, this function returns `MIN` itself.
+        /// Since unsigned types do not have negative equivalents
+        /// all applications of this function will wrap (except for `-0`).
+        /// For values smaller than the corresponding signed type's maximum
+        /// the result is the same as casting the corresponding signed value.
+        /// Any larger values are equivalent to `MAX + 1 - (val - MAX - 1)` where
+        /// `MAX` is the corresponding signed type's maximum.
         ///
         /// # Examples
         ///
         /// Basic usage:
         ///
         /// ```
-        /// assert_eq!(100i8.wrapping_neg(), -100);
-        /// assert_eq!((-128i8).wrapping_neg(), -128);
+        /// assert_eq!(100u8.wrapping_neg(), 156);
+        /// assert_eq!(0u8.wrapping_neg(), 0);
+        /// assert_eq!(180u8.wrapping_neg(), 76);
+        /// assert_eq!(180u8.wrapping_neg(), (127 + 1) - (180u8 - (127 + 1)));
         /// ```
         #[stable(feature = "num_wrapping", since = "1.2.0")]
         #[inline(always)]
@@ -1446,7 +1781,7 @@ macro_rules! uint_impl {
             self.overflowing_shl(rhs).0
         }
 
-        /// Panic-free bitwise shift-left; yields `self >> mask(rhs)`,
+        /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`,
         /// where `mask` removes any high-order bits of `rhs` that
         /// would cause the shift to exceed the bitwidth of the type.
         ///
@@ -1464,6 +1799,195 @@ macro_rules! uint_impl {
             self.overflowing_shr(rhs).0
         }
 
+        /// Calculates `self` + `rhs`
+        ///
+        /// Returns a tuple of the addition along with a boolean indicating
+        /// whether an arithmetic overflow would occur. If an overflow would
+        /// have occurred then the wrapped value is returned.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// use std::u32;
+        ///
+        /// assert_eq!(5u32.overflowing_add(2), (7, false));
+        /// assert_eq!(u32::MAX.overflowing_add(1), (0, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_add(self, rhs: Self) -> (Self, bool) {
+            unsafe {
+                let (a, b) = $add_with_overflow(self as $ActualT,
+                                                rhs as $ActualT);
+                (a as Self, b)
+            }
+        }
+
+        /// Calculates `self` - `rhs`
+        ///
+        /// Returns a tuple of the subtraction along with a boolean indicating
+        /// whether an arithmetic overflow would occur. If an overflow would
+        /// have occurred then the wrapped value is returned.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// use std::u32;
+        ///
+        /// assert_eq!(5u32.overflowing_sub(2), (3, false));
+        /// assert_eq!(0u32.overflowing_sub(1), (u32::MAX, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
+            unsafe {
+                let (a, b) = $sub_with_overflow(self as $ActualT,
+                                                rhs as $ActualT);
+                (a as Self, b)
+            }
+        }
+
+        /// Calculates the multiplication of `self` and `rhs`.
+        ///
+        /// Returns a tuple of the multiplication along with a boolean
+        /// indicating whether an arithmetic overflow would occur. If an
+        /// overflow would have occurred then the wrapped value is returned.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// assert_eq!(5u32.overflowing_mul(2), (10, false));
+        /// assert_eq!(1_000_000_000u32.overflowing_mul(10), (1410065408, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
+            unsafe {
+                let (a, b) = $mul_with_overflow(self as $ActualT,
+                                                rhs as $ActualT);
+                (a as Self, b)
+            }
+        }
+
+        /// Calculates the divisor when `self` is divided by `rhs`.
+        ///
+        /// Returns a tuple of the divisor along with a boolean indicating
+        /// whether an arithmetic overflow would occur. Note that for unsigned
+        /// integers overflow never occurs, so the second value is always
+        /// `false`.
+        ///
+        /// # Panics
+        ///
+        /// This function will panic if `rhs` is 0.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// assert_eq!(5u32.overflowing_div(2), (2, false));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_div(self, rhs: Self) -> (Self, bool) {
+            (self / rhs, false)
+        }
+
+        /// Calculates the remainder when `self` is divided by `rhs`.
+        ///
+        /// Returns a tuple of the remainder after dividing along with a boolean
+        /// indicating whether an arithmetic overflow would occur. Note that for
+        /// unsigned integers overflow never occurs, so the second value is
+        /// always `false`.
+        ///
+        /// # Panics
+        ///
+        /// This function will panic if `rhs` is 0.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// assert_eq!(5u32.overflowing_rem(2), (1, false));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_rem(self, rhs: Self) -> (Self, bool) {
+            (self % rhs, false)
+        }
+
+        /// Negates self in an overflowing fashion.
+        ///
+        /// Returns `!self + 1` using wrapping operations to return the value
+        /// that represents the negation of this unsigned value. Note that for
+        /// positive unsigned values overflow always occurs, but negating 0 does
+        /// not overflow.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// assert_eq!(0u32.overflowing_neg(), (0, false));
+        /// assert_eq!(2u32.overflowing_neg(), (-2i32 as u32, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_neg(self) -> (Self, bool) {
+            ((!self).wrapping_add(1), self != 0)
+        }
+
+        /// Shifts self left by `rhs` bits.
+        ///
+        /// Returns a tuple of the shifted version of self along with a boolean
+        /// indicating whether the shift value was larger than or equal to the
+        /// number of bits. If the shift value is too large, then value is
+        /// masked (N-1) where N is the number of bits, and this value is then
+        /// used to perform the shift.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// assert_eq!(0x10u32.overflowing_shl(4), (0x100, false));
+        /// assert_eq!(0x10u32.overflowing_shl(36), (0x100, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_shl(self, rhs: u32) -> (Self, bool) {
+            (self << (rhs & ($BITS - 1)), (rhs > ($BITS - 1)))
+        }
+
+        /// Shifts self right by `rhs` bits.
+        ///
+        /// Returns a tuple of the shifted version of self along with a boolean
+        /// indicating whether the shift value was larger than or equal to the
+        /// number of bits. If the shift value is too large, then value is
+        /// masked (N-1) where N is the number of bits, and this value is then
+        /// used to perform the shift.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage
+        ///
+        /// ```
+        /// assert_eq!(0x10u32.overflowing_shr(4), (0x1, false));
+        /// assert_eq!(0x10u32.overflowing_shr(36), (0x1, true));
+        /// ```
+        #[inline]
+        #[stable(feature = "wrapping", since = "1.7.0")]
+        pub fn overflowing_shr(self, rhs: u32) -> (Self, bool) {
+            (self >> (rhs & ($BITS - 1)), (rhs > ($BITS - 1)))
+        }
+
         /// Raises self to the power of `exp`, using exponentiation by squaring.
         ///
         /// # Examples
@@ -1471,7 +1995,7 @@ macro_rules! uint_impl {
         /// Basic usage:
         ///
         /// ```
-        /// assert_eq!(2i32.pow(4), 16);
+        /// assert_eq!(2u32.pow(4), 16);
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
@@ -1563,19 +2087,6 @@ macro_rules! uint_impl {
 }
 
 #[lang = "u8"]
-#[cfg(stage0)]
-impl u8 {
-    uint_impl! { u8, 8,
-        intrinsics::ctpop8,
-        intrinsics::ctlz8,
-        intrinsics::cttz8,
-        bswap8,
-        intrinsics::u8_add_with_overflow,
-        intrinsics::u8_sub_with_overflow,
-        intrinsics::u8_mul_with_overflow }
-}
-#[lang = "u8"]
-#[cfg(not(stage0))]
 impl u8 {
     uint_impl! { u8, 8,
         intrinsics::ctpop,
@@ -1588,19 +2099,6 @@ impl u8 {
 }
 
 #[lang = "u16"]
-#[cfg(stage0)]
-impl u16 {
-    uint_impl! { u16, 16,
-        intrinsics::ctpop16,
-        intrinsics::ctlz16,
-        intrinsics::cttz16,
-        intrinsics::bswap16,
-        intrinsics::u16_add_with_overflow,
-        intrinsics::u16_sub_with_overflow,
-        intrinsics::u16_mul_with_overflow }
-}
-#[lang = "u16"]
-#[cfg(not(stage0))]
 impl u16 {
     uint_impl! { u16, 16,
         intrinsics::ctpop,
@@ -1613,19 +2111,6 @@ impl u16 {
 }
 
 #[lang = "u32"]
-#[cfg(stage0)]
-impl u32 {
-    uint_impl! { u32, 32,
-        intrinsics::ctpop32,
-        intrinsics::ctlz32,
-        intrinsics::cttz32,
-        intrinsics::bswap32,
-        intrinsics::u32_add_with_overflow,
-        intrinsics::u32_sub_with_overflow,
-        intrinsics::u32_mul_with_overflow }
-}
-#[lang = "u32"]
-#[cfg(not(stage0))]
 impl u32 {
     uint_impl! { u32, 32,
         intrinsics::ctpop,
@@ -1638,19 +2123,6 @@ impl u32 {
 }
 
 #[lang = "u64"]
-#[cfg(stage0)]
-impl u64 {
-    uint_impl! { u64, 64,
-        intrinsics::ctpop64,
-        intrinsics::ctlz64,
-        intrinsics::cttz64,
-        intrinsics::bswap64,
-        intrinsics::u64_add_with_overflow,
-        intrinsics::u64_sub_with_overflow,
-        intrinsics::u64_mul_with_overflow }
-}
-#[lang = "u64"]
-#[cfg(not(stage0))]
 impl u64 {
     uint_impl! { u64, 64,
         intrinsics::ctpop,
@@ -1664,20 +2136,6 @@ impl u64 {
 
 #[cfg(target_pointer_width = "32")]
 #[lang = "usize"]
-#[cfg(stage0)]
-impl usize {
-    uint_impl! { u32, 32,
-        intrinsics::ctpop32,
-        intrinsics::ctlz32,
-        intrinsics::cttz32,
-        intrinsics::bswap32,
-        intrinsics::u32_add_with_overflow,
-        intrinsics::u32_sub_with_overflow,
-        intrinsics::u32_mul_with_overflow }
-}
-#[cfg(target_pointer_width = "32")]
-#[lang = "usize"]
-#[cfg(not(stage0))]
 impl usize {
     uint_impl! { u32, 32,
         intrinsics::ctpop,
@@ -1691,20 +2149,6 @@ impl usize {
 
 #[cfg(target_pointer_width = "64")]
 #[lang = "usize"]
-#[cfg(stage0)]
-impl usize {
-    uint_impl! { u64, 64,
-        intrinsics::ctpop64,
-        intrinsics::ctlz64,
-        intrinsics::cttz64,
-        intrinsics::bswap64,
-        intrinsics::u64_add_with_overflow,
-        intrinsics::u64_sub_with_overflow,
-        intrinsics::u64_mul_with_overflow }
-}
-#[cfg(target_pointer_width = "64")]
-#[lang = "usize"]
-#[cfg(not(stage0))]
 impl usize {
     uint_impl! { u64, 64,
         intrinsics::ctpop,
@@ -1771,12 +2215,6 @@ pub trait Float: Sized {
     #[unstable(feature = "float_extras", reason = "needs removal",
                issue = "27752")]
     fn one() -> Self;
-    /// Parses the string `s` with the radix `r` as a float.
-    #[unstable(feature = "float_from_str_radix", reason = "recently moved API",
-               issue = "27736")]
-    #[rustc_deprecated(since = "1.4.0",
-                 reason = "unclear how useful or correct this is")]
-    fn from_str_radix(s: &str, r: u32) -> Result<Self, ParseFloatError>;
 
     /// Returns true if this value is NaN and false otherwise.
     #[stable(feature = "core", since = "1.6.0")]
index 35e1e988f3ed4300296b84f2c352f1ef2a888bc8..16d84cf81e11db8a74608c2ebd9f3404476e65fa 100644 (file)
@@ -15,11 +15,15 @@ macro_rules! uint_module { ($T:ty, $T_SIGNED:ty, $bits:expr) => (
 #[unstable(feature = "num_bits_bytes",
            reason = "may want to be an associated function",
            issue = "27753")]
+#[rustc_deprecated(since = "1.7.0",
+                   reason = "will be replaced via const fn or associated constants")]
 #[allow(missing_docs)]
 pub const BITS : usize = $bits;
 #[unstable(feature = "num_bits_bytes",
            reason = "may want to be an associated function",
            issue = "27753")]
+#[rustc_deprecated(since = "1.7.0",
+                   reason = "will be replaced via const fn or associated constants")]
 #[allow(missing_docs)]
 pub const BYTES : usize = ($bits / 8);
 
index 70e790106e1c6706e4e96f8cb9ce4a858fae2201..a6a7be023ebf4e3851fff0372f51090f54b21e7a 100644 (file)
@@ -14,4 +14,7 @@
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
-uint_module! { usize, isize, ::isize::BITS }
+#[cfg(target_pointer_width = "32")]
+uint_module! { usize, isize, 32 }
+#[cfg(target_pointer_width = "64")]
+uint_module! { usize, isize, 64 }
index 88f71e63da68245adba2d2cc1c244cf950355fa6..a6b3dc744699a3b1238e19dc18c4f3b981ca4c36 100644 (file)
@@ -9,40 +9,20 @@
 // except according to those terms.
 
 #![allow(missing_docs)]
-#![unstable(feature = "wrapping", reason = "may be removed or relocated",
+#![unstable(feature = "old_wrapping", reason = "may be removed or relocated",
             issue = "27755")]
 
-#[cfg(stage0)]
-pub use intrinsics::{
-    u8_add_with_overflow, i8_add_with_overflow,
-    u16_add_with_overflow, i16_add_with_overflow,
-    u32_add_with_overflow, i32_add_with_overflow,
-    u64_add_with_overflow, i64_add_with_overflow,
-
-    u8_sub_with_overflow, i8_sub_with_overflow,
-    u16_sub_with_overflow, i16_sub_with_overflow,
-    u32_sub_with_overflow, i32_sub_with_overflow,
-    u64_sub_with_overflow, i64_sub_with_overflow,
-
-    u8_mul_with_overflow, i8_mul_with_overflow,
-    u16_mul_with_overflow, i16_mul_with_overflow,
-    u32_mul_with_overflow, i32_mul_with_overflow,
-    u64_mul_with_overflow, i64_mul_with_overflow,
-};
-
-#[cfg(not(stage0))]
-pub use intrinsics::{
-    add_with_overflow,
-    sub_with_overflow,
-    mul_with_overflow,
-};
+use intrinsics::{add_with_overflow, sub_with_overflow, mul_with_overflow};
 
 use super::Wrapping;
 
 use ops::*;
 
-use ::{i8,i16,i32,i64};
+use ::{i8, i16, i32, i64, isize};
 
+#[unstable(feature = "old_wrapping", reason = "may be removed or relocated",
+           issue = "27755")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to inherent methods")]
 pub trait OverflowingOps {
     fn overflowing_add(self, rhs: Self) -> (Self, bool);
     fn overflowing_sub(self, rhs: Self) -> (Self, bool);
@@ -56,15 +36,71 @@ pub trait OverflowingOps {
     fn overflowing_shr(self, rhs: u32) -> (Self, bool);
 }
 
-macro_rules! sh_impl {
-    ($t:ty, $f:ty) => (
+macro_rules! sh_impl_signed {
+    ($t:ident, $f:ident) => (
         #[stable(feature = "rust1", since = "1.0.0")]
         impl Shl<$f> for Wrapping<$t> {
             type Output = Wrapping<$t>;
 
             #[inline(always)]
             fn shl(self, other: $f) -> Wrapping<$t> {
-                Wrapping(self.0 << other)
+                if other < 0 {
+                    Wrapping(self.0.wrapping_shr((-other & self::shift_max::$t as $f) as u32))
+                } else {
+                    Wrapping(self.0.wrapping_shl((other & self::shift_max::$t as $f) as u32))
+                }
+            }
+        }
+
+        #[unstable(feature = "wrapping_impls", reason = "recently added", issue = "30524")]
+        impl ShlAssign<$f> for Wrapping<$t> {
+            #[inline(always)]
+            fn shl_assign(&mut self, other: $f) {
+                *self = *self << other;
+            }
+        }
+
+        #[stable(feature = "rust1", since = "1.0.0")]
+        impl Shr<$f> for Wrapping<$t> {
+            type Output = Wrapping<$t>;
+
+            #[inline(always)]
+            fn shr(self, other: $f) -> Wrapping<$t> {
+                if other < 0 {
+                    Wrapping(self.0.wrapping_shl((-other & self::shift_max::$t as $f) as u32))
+                } else {
+                    Wrapping(self.0.wrapping_shr((other & self::shift_max::$t as $f) as u32))
+                }
+            }
+        }
+
+        #[unstable(feature = "wrapping_impls", reason = "recently added", issue = "30524")]
+        impl ShrAssign<$f> for Wrapping<$t> {
+            #[inline(always)]
+            fn shr_assign(&mut self, other: $f) {
+                *self = *self >> other;
+            }
+        }
+    )
+}
+
+macro_rules! sh_impl_unsigned {
+    ($t:ident, $f:ident) => (
+        #[stable(feature = "rust1", since = "1.0.0")]
+        impl Shl<$f> for Wrapping<$t> {
+            type Output = Wrapping<$t>;
+
+            #[inline(always)]
+            fn shl(self, other: $f) -> Wrapping<$t> {
+                Wrapping(self.0.wrapping_shl((other & self::shift_max::$t as $f) as u32))
+            }
+        }
+
+        #[unstable(feature = "wrapping_impls", reason = "recently added", issue = "30524")]
+        impl ShlAssign<$f> for Wrapping<$t> {
+            #[inline(always)]
+            fn shl_assign(&mut self, other: $f) {
+                *self = *self << other;
             }
         }
 
@@ -74,7 +110,15 @@ macro_rules! sh_impl {
 
             #[inline(always)]
             fn shr(self, other: $f) -> Wrapping<$t> {
-                Wrapping(self.0 >> other)
+                Wrapping(self.0.wrapping_shr((other & self::shift_max::$t as $f) as u32))
+            }
+        }
+
+        #[unstable(feature = "wrapping_impls", reason = "recently added", issue = "30524")]
+        impl ShrAssign<$f> for Wrapping<$t> {
+            #[inline(always)]
+            fn shr_assign(&mut self, other: $f) {
+                *self = *self >> other;
             }
         }
     )
@@ -82,23 +126,24 @@ macro_rules! sh_impl {
 
 // FIXME (#23545): uncomment the remaining impls
 macro_rules! sh_impl_all {
-    ($($t:ty)*) => ($(
-        // sh_impl! { $t, u8 }
-        // sh_impl! { $t, u16 }
-        // sh_impl! { $t, u32 }
-        // sh_impl! { $t, u64 }
-        sh_impl! { $t, usize }
-
-        // sh_impl! { $t, i8 }
-        // sh_impl! { $t, i16 }
-        // sh_impl! { $t, i32 }
-        // sh_impl! { $t, i64 }
-        // sh_impl! { $t, isize }
+    ($($t:ident)*) => ($(
+        //sh_impl_unsigned! { $t, u8 }
+        //sh_impl_unsigned! { $t, u16 }
+        //sh_impl_unsigned! { $t, u32 }
+        //sh_impl_unsigned! { $t, u64 }
+        sh_impl_unsigned! { $t, usize }
+
+        //sh_impl_signed! { $t, i8 }
+        //sh_impl_signed! { $t, i16 }
+        //sh_impl_signed! { $t, i32 }
+        //sh_impl_signed! { $t, i64 }
+        //sh_impl_signed! { $t, isize }
     )*)
 }
 
 sh_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
 
+// FIXME(30524): impl Op<T> for Wrapping<T>, impl OpAssign<T> for Wrapping<T>
 macro_rules! wrapping_impl {
     ($($t:ty)*) => ($(
         #[stable(feature = "rust1", since = "1.0.0")]
@@ -111,6 +156,14 @@ macro_rules! wrapping_impl {
             }
         }
 
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl AddAssign for Wrapping<$t> {
+            #[inline(always)]
+            fn add_assign(&mut self, other: Wrapping<$t>) {
+                *self = *self + other;
+            }
+        }
+
         #[stable(feature = "rust1", since = "1.0.0")]
         impl Sub for Wrapping<$t> {
             type Output = Wrapping<$t>;
@@ -121,6 +174,14 @@ macro_rules! wrapping_impl {
             }
         }
 
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl SubAssign for Wrapping<$t> {
+            #[inline(always)]
+            fn sub_assign(&mut self, other: Wrapping<$t>) {
+                *self = *self - other;
+            }
+        }
+
         #[stable(feature = "rust1", since = "1.0.0")]
         impl Mul for Wrapping<$t> {
             type Output = Wrapping<$t>;
@@ -131,6 +192,14 @@ macro_rules! wrapping_impl {
             }
         }
 
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl MulAssign for Wrapping<$t> {
+            #[inline(always)]
+            fn mul_assign(&mut self, other: Wrapping<$t>) {
+                *self = *self * other;
+            }
+        }
+
         #[stable(feature = "wrapping_div", since = "1.3.0")]
         impl Div for Wrapping<$t> {
             type Output = Wrapping<$t>;
@@ -141,6 +210,32 @@ macro_rules! wrapping_impl {
             }
         }
 
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl DivAssign for Wrapping<$t> {
+            #[inline(always)]
+            fn div_assign(&mut self, other: Wrapping<$t>) {
+                *self = *self / other;
+            }
+        }
+
+        #[unstable(feature = "wrapping_impls", reason = "recently added", issue = "30524")]
+        impl Rem for Wrapping<$t> {
+            type Output = Wrapping<$t>;
+
+            #[inline(always)]
+            fn rem(self, other: Wrapping<$t>) -> Wrapping<$t> {
+                Wrapping(self.0.wrapping_rem(other.0))
+            }
+        }
+
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl RemAssign for Wrapping<$t> {
+            #[inline(always)]
+            fn rem_assign(&mut self, other: Wrapping<$t>) {
+                *self = *self % other;
+            }
+        }
+
         #[stable(feature = "rust1", since = "1.0.0")]
         impl Not for Wrapping<$t> {
             type Output = Wrapping<$t>;
@@ -161,6 +256,14 @@ macro_rules! wrapping_impl {
             }
         }
 
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl BitXorAssign for Wrapping<$t> {
+            #[inline(always)]
+            fn bitxor_assign(&mut self, other: Wrapping<$t>) {
+                *self = *self ^ other;
+            }
+        }
+
         #[stable(feature = "rust1", since = "1.0.0")]
         impl BitOr for Wrapping<$t> {
             type Output = Wrapping<$t>;
@@ -171,6 +274,14 @@ macro_rules! wrapping_impl {
             }
         }
 
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl BitOrAssign for Wrapping<$t> {
+            #[inline(always)]
+            fn bitor_assign(&mut self, other: Wrapping<$t>) {
+                *self = *self | other;
+            }
+        }
+
         #[stable(feature = "rust1", since = "1.0.0")]
         impl BitAnd for Wrapping<$t> {
             type Output = Wrapping<$t>;
@@ -180,6 +291,14 @@ macro_rules! wrapping_impl {
                 Wrapping(self.0 & other.0)
             }
         }
+
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl BitAndAssign for Wrapping<$t> {
+            #[inline(always)]
+            fn bitand_assign(&mut self, other: Wrapping<$t>) {
+                *self = *self & other;
+            }
+        }
     )*)
 }
 
@@ -188,57 +307,48 @@ wrapping_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 mod shift_max {
     #![allow(non_upper_case_globals)]
 
+    #[cfg(target_pointer_width = "32")]
+    mod platform {
+        pub const usize: u32 = super::u32;
+        pub const isize: u32 = super::i32;
+    }
+
+    #[cfg(target_pointer_width = "64")]
+    mod platform {
+        pub const usize: u32 = super::u64;
+        pub const isize: u32 = super::i64;
+    }
+
     pub const  i8: u32 = (1 << 3) - 1;
     pub const i16: u32 = (1 << 4) - 1;
     pub const i32: u32 = (1 << 5) - 1;
     pub const i64: u32 = (1 << 6) - 1;
+    pub use self::platform::isize;
 
     pub const  u8: u32 = i8;
     pub const u16: u32 = i16;
     pub const u32: u32 = i32;
     pub const u64: u32 = i64;
+    pub use self::platform::usize;
 }
 
 macro_rules! signed_overflowing_impl {
     ($($t:ident)*) => ($(
+        #[allow(deprecated)]
         impl OverflowingOps for $t {
             #[inline(always)]
-            #[cfg(stage0)]
-            fn overflowing_add(self, rhs: $t) -> ($t, bool) {
-                unsafe {
-                    concat_idents!($t, _add_with_overflow)(self, rhs)
-                }
-            }
-            #[inline(always)]
-            #[cfg(not(stage0))]
             fn overflowing_add(self, rhs: $t) -> ($t, bool) {
                 unsafe {
                     add_with_overflow(self, rhs)
                 }
             }
             #[inline(always)]
-            #[cfg(stage0)]
-            fn overflowing_sub(self, rhs: $t) -> ($t, bool) {
-                unsafe {
-                    concat_idents!($t, _sub_with_overflow)(self, rhs)
-                }
-            }
-            #[inline(always)]
-            #[cfg(not(stage0))]
             fn overflowing_sub(self, rhs: $t) -> ($t, bool) {
                 unsafe {
                     sub_with_overflow(self, rhs)
                 }
             }
             #[inline(always)]
-            #[cfg(stage0)]
-            fn overflowing_mul(self, rhs: $t) -> ($t, bool) {
-                unsafe {
-                    concat_idents!($t, _mul_with_overflow)(self, rhs)
-                }
-            }
-            #[inline(always)]
-            #[cfg(not(stage0))]
             fn overflowing_mul(self, rhs: $t) -> ($t, bool) {
                 unsafe {
                     mul_with_overflow(self, rhs)
@@ -287,44 +397,21 @@ macro_rules! signed_overflowing_impl {
 
 macro_rules! unsigned_overflowing_impl {
     ($($t:ident)*) => ($(
+        #[allow(deprecated)]
         impl OverflowingOps for $t {
             #[inline(always)]
-            #[cfg(stage0)]
-            fn overflowing_add(self, rhs: $t) -> ($t, bool) {
-                unsafe {
-                    concat_idents!($t, _add_with_overflow)(self, rhs)
-                }
-            }
-            #[inline(always)]
-            #[cfg(not(stage0))]
             fn overflowing_add(self, rhs: $t) -> ($t, bool) {
                 unsafe {
                     add_with_overflow(self, rhs)
                 }
             }
             #[inline(always)]
-            #[cfg(stage0)]
-            fn overflowing_sub(self, rhs: $t) -> ($t, bool) {
-                unsafe {
-                    concat_idents!($t, _sub_with_overflow)(self, rhs)
-                }
-            }
-            #[inline(always)]
-            #[cfg(not(stage0))]
             fn overflowing_sub(self, rhs: $t) -> ($t, bool) {
                 unsafe {
                     sub_with_overflow(self, rhs)
                 }
             }
             #[inline(always)]
-            #[cfg(stage0)]
-            fn overflowing_mul(self, rhs: $t) -> ($t, bool) {
-                unsafe {
-                    concat_idents!($t, _mul_with_overflow)(self, rhs)
-                }
-            }
-            #[inline(always)]
-            #[cfg(not(stage0))]
             fn overflowing_mul(self, rhs: $t) -> ($t, bool) {
                 unsafe {
                     mul_with_overflow(self, rhs)
@@ -359,301 +446,5 @@ macro_rules! unsigned_overflowing_impl {
     )*)
 }
 
-signed_overflowing_impl! { i8 i16 i32 i64 }
-unsigned_overflowing_impl! { u8 u16 u32 u64 }
-
-#[cfg(target_pointer_width = "64")]
-impl OverflowingOps for usize {
-    #[inline(always)]
-    #[cfg(stage0)]
-    fn overflowing_add(self, rhs: usize) -> (usize, bool) {
-        unsafe {
-            let res = u64_add_with_overflow(self as u64, rhs as u64);
-            (res.0 as usize, res.1)
-        }
-    }
-    #[inline(always)]
-    #[cfg(not(stage0))]
-    fn overflowing_add(self, rhs: usize) -> (usize, bool) {
-        unsafe {
-            add_with_overflow(self, rhs)
-        }
-    }
-    #[inline(always)]
-    #[cfg(stage0)]
-    fn overflowing_sub(self, rhs: usize) -> (usize, bool) {
-        unsafe {
-            let res = u64_sub_with_overflow(self as u64, rhs as u64);
-            (res.0 as usize, res.1)
-        }
-    }
-    #[inline(always)]
-    #[cfg(not(stage0))]
-    fn overflowing_sub(self, rhs: usize) -> (usize, bool) {
-        unsafe {
-            sub_with_overflow(self, rhs)
-        }
-    }
-    #[inline(always)]
-    #[cfg(stage0)]
-    fn overflowing_mul(self, rhs: usize) -> (usize, bool) {
-        unsafe {
-            let res = u64_mul_with_overflow(self as u64, rhs as u64);
-            (res.0 as usize, res.1)
-        }
-    }
-    #[inline(always)]
-    #[cfg(not(stage0))]
-    fn overflowing_mul(self, rhs: usize) -> (usize, bool) {
-        unsafe {
-            mul_with_overflow(self, rhs)
-        }
-    }
-    #[inline(always)]
-    fn overflowing_div(self, rhs: usize) -> (usize, bool) {
-        let (r, f) = (self as u64).overflowing_div(rhs as u64);
-        (r as usize, f)
-    }
-    #[inline(always)]
-    fn overflowing_rem(self, rhs: usize) -> (usize, bool) {
-        let (r, f) = (self as u64).overflowing_rem(rhs as u64);
-        (r as usize, f)
-    }
-    #[inline(always)]
-    fn overflowing_neg(self) -> (usize, bool) {
-        let (r, f) = (self as u64).overflowing_neg();
-        (r as usize, f)
-    }
-    #[inline(always)]
-    fn overflowing_shl(self, rhs: u32) -> (usize, bool) {
-        let (r, f) = (self as u64).overflowing_shl(rhs);
-        (r as usize, f)
-    }
-    #[inline(always)]
-    fn overflowing_shr(self, rhs: u32) -> (usize, bool) {
-        let (r, f) = (self as u64).overflowing_shr(rhs);
-        (r as usize, f)
-    }
-}
-
-#[cfg(target_pointer_width = "32")]
-impl OverflowingOps for usize {
-    #[inline(always)]
-    #[cfg(stage0)]
-    fn overflowing_add(self, rhs: usize) -> (usize, bool) {
-        unsafe {
-            let res = u32_add_with_overflow(self as u32, rhs as u32);
-            (res.0 as usize, res.1)
-        }
-    }
-    #[inline(always)]
-    #[cfg(not(stage0))]
-    fn overflowing_add(self, rhs: usize) -> (usize, bool) {
-        unsafe {
-            add_with_overflow(self, rhs)
-        }
-    }
-    #[inline(always)]
-    #[cfg(stage0)]
-    fn overflowing_sub(self, rhs: usize) -> (usize, bool) {
-        unsafe {
-            let res = u32_sub_with_overflow(self as u32, rhs as u32);
-            (res.0 as usize, res.1)
-        }
-    }
-    #[inline(always)]
-    #[cfg(not(stage0))]
-    fn overflowing_sub(self, rhs: usize) -> (usize, bool) {
-        unsafe {
-            sub_with_overflow(self, rhs)
-        }
-    }
-    #[inline(always)]
-    #[cfg(stage0)]
-    fn overflowing_mul(self, rhs: usize) -> (usize, bool) {
-        unsafe {
-            let res = u32_mul_with_overflow(self as u32, rhs as u32);
-            (res.0 as usize, res.1)
-        }
-    }
-    #[inline(always)]
-    #[cfg(not(stage0))]
-    fn overflowing_mul(self, rhs: usize) -> (usize, bool) {
-        unsafe {
-            mul_with_overflow(self, rhs)
-        }
-    }
-    #[inline(always)]
-    fn overflowing_div(self, rhs: usize) -> (usize, bool) {
-        let (r, f) = (self as u32).overflowing_div(rhs as u32);
-        (r as usize, f)
-    }
-    #[inline(always)]
-    fn overflowing_rem(self, rhs: usize) -> (usize, bool) {
-        let (r, f) = (self as u32).overflowing_rem(rhs as u32);
-        (r as usize, f)
-    }
-    #[inline(always)]
-    fn overflowing_neg(self) -> (usize, bool) {
-        let (r, f) = (self as u32).overflowing_neg();
-        (r as usize, f)
-    }
-    #[inline(always)]
-    fn overflowing_shl(self, rhs: u32) -> (usize, bool) {
-        let (r, f) = (self as u32).overflowing_shl(rhs);
-        (r as usize, f)
-    }
-    #[inline(always)]
-    fn overflowing_shr(self, rhs: u32) -> (usize, bool) {
-        let (r, f) = (self as u32).overflowing_shr(rhs);
-        (r as usize, f)
-    }
-}
-
-#[cfg(target_pointer_width = "64")]
-impl OverflowingOps for isize {
-    #[inline(always)]
-    #[cfg(stage0)]
-    fn overflowing_add(self, rhs: isize) -> (isize, bool) {
-        unsafe {
-            let res = i64_add_with_overflow(self as i64, rhs as i64);
-            (res.0 as isize, res.1)
-        }
-    }
-    #[inline(always)]
-    #[cfg(not(stage0))]
-    fn overflowing_add(self, rhs: isize) -> (isize, bool) {
-        unsafe {
-            add_with_overflow(self, rhs)
-        }
-    }
-    #[inline(always)]
-    #[cfg(stage0)]
-    fn overflowing_sub(self, rhs: isize) -> (isize, bool) {
-        unsafe {
-            let res = i64_sub_with_overflow(self as i64, rhs as i64);
-            (res.0 as isize, res.1)
-        }
-    }
-    #[inline(always)]
-    #[cfg(not(stage0))]
-    fn overflowing_sub(self, rhs: isize) -> (isize, bool) {
-        unsafe {
-            sub_with_overflow(self, rhs)
-        }
-    }
-    #[inline(always)]
-    #[cfg(stage0)]
-    fn overflowing_mul(self, rhs: isize) -> (isize, bool) {
-        unsafe {
-            let res = i64_mul_with_overflow(self as i64, rhs as i64);
-            (res.0 as isize, res.1)
-        }
-    }
-    #[inline(always)]
-    #[cfg(not(stage0))]
-    fn overflowing_mul(self, rhs: isize) -> (isize, bool) {
-        unsafe {
-            mul_with_overflow(self, rhs)
-        }
-    }
-    #[inline(always)]
-    fn overflowing_div(self, rhs: isize) -> (isize, bool) {
-        let (r, f) = (self as i64).overflowing_div(rhs as i64);
-        (r as isize, f)
-    }
-    #[inline(always)]
-    fn overflowing_rem(self, rhs: isize) -> (isize, bool) {
-        let (r, f) = (self as i64).overflowing_rem(rhs as i64);
-        (r as isize, f)
-    }
-    #[inline(always)]
-    fn overflowing_neg(self) -> (isize, bool) {
-        let (r, f) = (self as i64).overflowing_neg();
-        (r as isize, f)
-    }
-    #[inline(always)]
-    fn overflowing_shl(self, rhs: u32) -> (isize, bool) {
-        let (r, f) = (self as i64).overflowing_shl(rhs);
-        (r as isize, f)
-    }
-    #[inline(always)]
-    fn overflowing_shr(self, rhs: u32) -> (isize, bool) {
-        let (r, f) = (self as i64).overflowing_shr(rhs);
-        (r as isize, f)
-    }
-}
-
-#[cfg(target_pointer_width = "32")]
-impl OverflowingOps for isize {
-    #[inline(always)]
-    #[cfg(stage0)]
-    fn overflowing_add(self, rhs: isize) -> (isize, bool) {
-        unsafe {
-            let res = i32_add_with_overflow(self as i32, rhs as i32);
-            (res.0 as isize, res.1)
-        }
-    }
-    #[inline(always)]
-    #[cfg(not(stage0))]
-    fn overflowing_add(self, rhs: isize) -> (isize, bool) {
-        unsafe {
-            add_with_overflow(self, rhs)
-        }
-    }
-    #[inline(always)]
-    #[cfg(stage0)]
-    fn overflowing_sub(self, rhs: isize) -> (isize, bool) {
-        unsafe {
-            let res = i32_sub_with_overflow(self as i32, rhs as i32);
-            (res.0 as isize, res.1)
-        }
-    }
-    #[inline(always)]
-    #[cfg(not(stage0))]
-    fn overflowing_sub(self, rhs: isize) -> (isize, bool) {
-        unsafe {
-            sub_with_overflow(self, rhs)
-        }
-    }
-    #[inline(always)]
-    #[cfg(stage0)]
-    fn overflowing_mul(self, rhs: isize) -> (isize, bool) {
-        unsafe {
-            let res = i32_mul_with_overflow(self as i32, rhs as i32);
-            (res.0 as isize, res.1)
-        }
-    }
-    #[inline(always)]
-    #[cfg(not(stage0))]
-    fn overflowing_mul(self, rhs: isize) -> (isize, bool) {
-        unsafe {
-            mul_with_overflow(self, rhs)
-        }
-    }
-    #[inline(always)]
-    fn overflowing_div(self, rhs: isize) -> (isize, bool) {
-        let (r, f) = (self as i32).overflowing_div(rhs as i32);
-        (r as isize, f)
-    }
-    #[inline(always)]
-    fn overflowing_rem(self, rhs: isize) -> (isize, bool) {
-        let (r, f) = (self as i32).overflowing_rem(rhs as i32);
-        (r as isize, f)
-    }
-    #[inline(always)]
-    fn overflowing_neg(self) -> (isize, bool) {
-        let (r, f) = (self as i32).overflowing_neg();
-        (r as isize, f)
-    }
-    #[inline(always)]
-    fn overflowing_shl(self, rhs: u32) -> (isize, bool) {
-        let (r, f) = (self as i32).overflowing_shl(rhs);
-        (r as isize, f)
-    }
-    #[inline(always)]
-    fn overflowing_shr(self, rhs: u32) -> (isize, bool) {
-        let (r, f) = (self as i32).overflowing_shr(rhs);
-        (r as isize, f)
-    }
-}
+signed_overflowing_impl! { i8 i16 i32 i64 isize }
+unsigned_overflowing_impl! { u8 u16 u32 u64 usize }
index 0abbd70762d687255ff9fc31e73c31cb765b2144..d1c5b175bb03464b94aad403057f21a70046cc28 100644 (file)
@@ -38,7 +38,7 @@
 //! #[derive(Debug)]
 //! struct Point {
 //!     x: i32,
-//!     y: i32
+//!     y: i32,
 //! }
 //!
 //! impl Add for Point {
@@ -95,6 +95,16 @@ use fmt;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Drop {
     /// A method called when the value goes out of scope.
+    ///
+    /// When this method has been called, `self` has not yet been deallocated.
+    /// If it were, `self` would be a dangling reference.
+    ///
+    /// After this function is over, the memory of `self` will be deallocated.
+    ///
+    /// # Panics
+    ///
+    /// Given that a `panic!` will call `drop()` as it unwinds, any `panic!` in
+    /// a `drop()` implementation will likely abort.
     #[stable(feature = "rust1", since = "1.0.0")]
     fn drop(&mut self);
 }
@@ -161,7 +171,6 @@ macro_rules! forward_ref_binop {
 /// ```
 /// use std::ops::Add;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl Add for Foo {
@@ -215,7 +224,6 @@ add_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 /// ```
 /// use std::ops::Sub;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl Sub for Foo {
@@ -269,7 +277,6 @@ sub_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 /// ```
 /// use std::ops::Mul;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl Mul for Foo {
@@ -323,7 +330,6 @@ mul_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 /// ```
 /// use std::ops::Div;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl Div for Foo {
@@ -395,7 +401,6 @@ div_impl_float! { f32 f64 }
 /// ```
 /// use std::ops::Rem;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl Rem for Foo {
@@ -441,7 +446,6 @@ macro_rules! rem_impl_integer {
 
 rem_impl_integer! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 
-#[cfg(not(stage0))]
 macro_rules! rem_impl_float {
     ($($t:ty)*) => ($(
         #[stable(feature = "rust1", since = "1.0.0")]
@@ -456,48 +460,8 @@ macro_rules! rem_impl_float {
     )*)
 }
 
-#[cfg(not(stage0))]
 rem_impl_float! { f32 f64 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-#[cfg(stage0)]
-impl Rem for f32 {
-    type Output = f32;
-
-    // The builtin f32 rem operator is broken when targeting
-    // MSVC; see comment in std::f32::floor.
-    // FIXME: See also #27859.
-    #[inline]
-    #[cfg(target_env = "msvc")]
-    fn rem(self, other: f32) -> f32 {
-        (self as f64).rem(other as f64) as f32
-    }
-
-    #[inline]
-    #[cfg(not(target_env = "msvc"))]
-    fn rem(self, other: f32) -> f32 {
-        extern { fn fmodf(a: f32, b: f32) -> f32; }
-        unsafe { fmodf(self, other) }
-    }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-#[cfg(stage0)]
-impl Rem for f64 {
-    type Output = f64;
-
-    #[inline]
-    fn rem(self, other: f64) -> f64 {
-        extern { fn fmod(a: f64, b: f64) -> f64; }
-        unsafe { fmod(self, other) }
-    }
-}
-
-#[cfg(stage0)]
-forward_ref_binop! { impl Rem, rem for f64, f64 }
-#[cfg(stage0)]
-forward_ref_binop! { impl Rem, rem for f32, f32 }
-
 /// The `Neg` trait is used to specify the functionality of unary `-`.
 ///
 /// # Examples
@@ -508,7 +472,6 @@ forward_ref_binop! { impl Rem, rem for f32, f32 }
 /// ```
 /// use std::ops::Neg;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl Neg for Foo {
@@ -576,7 +539,6 @@ neg_impl_numeric! { isize i8 i16 i32 i64 f32 f64 }
 /// ```
 /// use std::ops::Not;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl Not for Foo {
@@ -630,7 +592,6 @@ not_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 /// ```
 /// use std::ops::BitAnd;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl BitAnd for Foo {
@@ -684,7 +645,6 @@ bitand_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 /// ```
 /// use std::ops::BitOr;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl BitOr for Foo {
@@ -738,7 +698,6 @@ bitor_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 /// ```
 /// use std::ops::BitXor;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl BitXor for Foo {
@@ -792,7 +751,6 @@ bitxor_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 /// ```
 /// use std::ops::Shl;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl Shl<Foo> for Foo {
@@ -864,7 +822,6 @@ shl_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
 /// ```
 /// use std::ops::Shr;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl Shr<Foo> for Foo {
@@ -939,7 +896,6 @@ shr_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
 ///
 /// use std::ops::AddAssign;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl AddAssign for Foo {
@@ -954,7 +910,6 @@ shr_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
 ///     foo += Foo;
 /// }
 /// ```
-#[cfg(not(stage0))]
 #[lang = "add_assign"]
 #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
 pub trait AddAssign<Rhs=Self> {
@@ -962,7 +917,6 @@ pub trait AddAssign<Rhs=Self> {
     fn add_assign(&mut self, Rhs);
 }
 
-#[cfg(not(stage0))]
 macro_rules! add_assign_impl {
     ($($t:ty)+) => ($(
         #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
@@ -973,7 +927,6 @@ macro_rules! add_assign_impl {
     )+)
 }
 
-#[cfg(not(stage0))]
 add_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 
 /// The `SubAssign` trait is used to specify the functionality of `-=`.
@@ -989,7 +942,6 @@ add_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 ///
 /// use std::ops::SubAssign;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl SubAssign for Foo {
@@ -1004,7 +956,6 @@ add_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 ///     foo -= Foo;
 /// }
 /// ```
-#[cfg(not(stage0))]
 #[lang = "sub_assign"]
 #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
 pub trait SubAssign<Rhs=Self> {
@@ -1012,7 +963,6 @@ pub trait SubAssign<Rhs=Self> {
     fn sub_assign(&mut self, Rhs);
 }
 
-#[cfg(not(stage0))]
 macro_rules! sub_assign_impl {
     ($($t:ty)+) => ($(
         #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
@@ -1023,7 +973,6 @@ macro_rules! sub_assign_impl {
     )+)
 }
 
-#[cfg(not(stage0))]
 sub_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 
 /// The `MulAssign` trait is used to specify the functionality of `*=`.
@@ -1039,7 +988,6 @@ sub_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 ///
 /// use std::ops::MulAssign;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl MulAssign for Foo {
@@ -1054,7 +1002,6 @@ sub_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 ///     foo *= Foo;
 /// }
 /// ```
-#[cfg(not(stage0))]
 #[lang = "mul_assign"]
 #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
 pub trait MulAssign<Rhs=Self> {
@@ -1062,7 +1009,6 @@ pub trait MulAssign<Rhs=Self> {
     fn mul_assign(&mut self, Rhs);
 }
 
-#[cfg(not(stage0))]
 macro_rules! mul_assign_impl {
     ($($t:ty)+) => ($(
         #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
@@ -1073,7 +1019,6 @@ macro_rules! mul_assign_impl {
     )+)
 }
 
-#[cfg(not(stage0))]
 mul_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 
 /// The `DivAssign` trait is used to specify the functionality of `/=`.
@@ -1089,7 +1034,6 @@ mul_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 ///
 /// use std::ops::DivAssign;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl DivAssign for Foo {
@@ -1104,7 +1048,6 @@ mul_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 ///     foo /= Foo;
 /// }
 /// ```
-#[cfg(not(stage0))]
 #[lang = "div_assign"]
 #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
 pub trait DivAssign<Rhs=Self> {
@@ -1112,7 +1055,6 @@ pub trait DivAssign<Rhs=Self> {
     fn div_assign(&mut self, Rhs);
 }
 
-#[cfg(not(stage0))]
 macro_rules! div_assign_impl {
     ($($t:ty)+) => ($(
         #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
@@ -1123,7 +1065,6 @@ macro_rules! div_assign_impl {
     )+)
 }
 
-#[cfg(not(stage0))]
 div_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 
 /// The `RemAssign` trait is used to specify the functionality of `%=`.
@@ -1139,7 +1080,6 @@ div_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 ///
 /// use std::ops::RemAssign;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl RemAssign for Foo {
@@ -1154,7 +1094,6 @@ div_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 ///     foo %= Foo;
 /// }
 /// ```
-#[cfg(not(stage0))]
 #[lang = "rem_assign"]
 #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
 pub trait RemAssign<Rhs=Self> {
@@ -1162,7 +1101,6 @@ pub trait RemAssign<Rhs=Self> {
     fn rem_assign(&mut self, Rhs);
 }
 
-#[cfg(not(stage0))]
 macro_rules! rem_assign_impl {
     ($($t:ty)+) => ($(
         #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
@@ -1173,7 +1111,6 @@ macro_rules! rem_assign_impl {
     )+)
 }
 
-#[cfg(not(stage0))]
 rem_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 
 /// The `BitAndAssign` trait is used to specify the functionality of `&=`.
@@ -1189,7 +1126,6 @@ rem_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 ///
 /// use std::ops::BitAndAssign;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl BitAndAssign for Foo {
@@ -1204,7 +1140,6 @@ rem_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
 ///     foo &= Foo;
 /// }
 /// ```
-#[cfg(not(stage0))]
 #[lang = "bitand_assign"]
 #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
 pub trait BitAndAssign<Rhs=Self> {
@@ -1212,7 +1147,6 @@ pub trait BitAndAssign<Rhs=Self> {
     fn bitand_assign(&mut self, Rhs);
 }
 
-#[cfg(not(stage0))]
 macro_rules! bitand_assign_impl {
     ($($t:ty)+) => ($(
         #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
@@ -1223,7 +1157,6 @@ macro_rules! bitand_assign_impl {
     )+)
 }
 
-#[cfg(not(stage0))]
 bitand_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 
 /// The `BitOrAssign` trait is used to specify the functionality of `|=`.
@@ -1239,7 +1172,6 @@ bitand_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 ///
 /// use std::ops::BitOrAssign;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl BitOrAssign for Foo {
@@ -1254,7 +1186,6 @@ bitand_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 ///     foo |= Foo;
 /// }
 /// ```
-#[cfg(not(stage0))]
 #[lang = "bitor_assign"]
 #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
 pub trait BitOrAssign<Rhs=Self> {
@@ -1262,7 +1193,6 @@ pub trait BitOrAssign<Rhs=Self> {
     fn bitor_assign(&mut self, Rhs);
 }
 
-#[cfg(not(stage0))]
 macro_rules! bitor_assign_impl {
     ($($t:ty)+) => ($(
         #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
@@ -1273,7 +1203,6 @@ macro_rules! bitor_assign_impl {
     )+)
 }
 
-#[cfg(not(stage0))]
 bitor_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 
 /// The `BitXorAssign` trait is used to specify the functionality of `^=`.
@@ -1289,7 +1218,6 @@ bitor_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 ///
 /// use std::ops::BitXorAssign;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl BitXorAssign for Foo {
@@ -1304,7 +1232,6 @@ bitor_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 ///     foo ^= Foo;
 /// }
 /// ```
-#[cfg(not(stage0))]
 #[lang = "bitxor_assign"]
 #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
 pub trait BitXorAssign<Rhs=Self> {
@@ -1312,7 +1239,6 @@ pub trait BitXorAssign<Rhs=Self> {
     fn bitxor_assign(&mut self, Rhs);
 }
 
-#[cfg(not(stage0))]
 macro_rules! bitxor_assign_impl {
     ($($t:ty)+) => ($(
         #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
@@ -1323,7 +1249,6 @@ macro_rules! bitxor_assign_impl {
     )+)
 }
 
-#[cfg(not(stage0))]
 bitxor_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 
 /// The `ShlAssign` trait is used to specify the functionality of `<<=`.
@@ -1339,7 +1264,6 @@ bitxor_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 ///
 /// use std::ops::ShlAssign;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl ShlAssign<Foo> for Foo {
@@ -1354,7 +1278,6 @@ bitxor_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 ///     foo <<= Foo;
 /// }
 /// ```
-#[cfg(not(stage0))]
 #[lang = "shl_assign"]
 #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
 pub trait ShlAssign<Rhs> {
@@ -1362,7 +1285,6 @@ pub trait ShlAssign<Rhs> {
     fn shl_assign(&mut self, Rhs);
 }
 
-#[cfg(not(stage0))]
 macro_rules! shl_assign_impl {
     ($t:ty, $f:ty) => (
         #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
@@ -1375,7 +1297,6 @@ macro_rules! shl_assign_impl {
     )
 }
 
-#[cfg(not(stage0))]
 macro_rules! shl_assign_impl_all {
     ($($t:ty)*) => ($(
         shl_assign_impl! { $t, u8 }
@@ -1392,7 +1313,6 @@ macro_rules! shl_assign_impl_all {
     )*)
 }
 
-#[cfg(not(stage0))]
 shl_assign_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
 
 /// The `ShrAssign` trait is used to specify the functionality of `>>=`.
@@ -1408,7 +1328,6 @@ shl_assign_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
 ///
 /// use std::ops::ShrAssign;
 ///
-/// #[derive(Copy, Clone)]
 /// struct Foo;
 ///
 /// impl ShrAssign<Foo> for Foo {
@@ -1423,7 +1342,6 @@ shl_assign_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
 ///     foo >>= Foo;
 /// }
 /// ```
-#[cfg(not(stage0))]
 #[lang = "shr_assign"]
 #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
 pub trait ShrAssign<Rhs=Self> {
@@ -1431,7 +1349,6 @@ pub trait ShrAssign<Rhs=Self> {
     fn shr_assign(&mut self, Rhs);
 }
 
-#[cfg(not(stage0))]
 macro_rules! shr_assign_impl {
     ($t:ty, $f:ty) => (
         #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
@@ -1444,7 +1361,6 @@ macro_rules! shr_assign_impl {
     )
 }
 
-#[cfg(not(stage0))]
 macro_rules! shr_assign_impl_all {
     ($($t:ty)*) => ($(
         shr_assign_impl! { $t, u8 }
@@ -1461,7 +1377,6 @@ macro_rules! shr_assign_impl_all {
     )*)
 }
 
-#[cfg(not(stage0))]
 shr_assign_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
 
 /// The `Index` trait is used to specify the functionality of indexing operations
@@ -1616,7 +1531,7 @@ impl<Idx: fmt::Debug> fmt::Debug for RangeTo<Idx> {
 }
 
 /// The `Deref` trait is used to specify the functionality of dereferencing
-/// operations like `*v`.
+/// operations, like `*v`.
 ///
 /// `Deref` also enables ['`Deref` coercions'][coercions].
 ///
index 209cebeaf1bc322d8d6769b7825b0052c7014521..8d40faf3bc6ff24b6a8a4f5b2e9eb463938656e9 100644 (file)
@@ -154,7 +154,6 @@ use mem;
 use ops::FnOnce;
 use result::Result::{Ok, Err};
 use result::Result;
-use slice;
 
 // Note that this is not a lang item per se, but it has a hidden dependency on
 // `Iterator`, which is one. The compiler assumes that the `next` method of
@@ -170,7 +169,7 @@ pub enum Option<T> {
     None,
     /// Some value `T`
     #[stable(feature = "rust1", since = "1.0.0")]
-    Some(T)
+    Some(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] T)
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -269,47 +268,11 @@ impl<T> Option<T> {
         }
     }
 
-    /// Converts from `Option<T>` to `&mut [T]` (without copying)
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(as_slice)]
-    /// # #![allow(deprecated)]
-    ///
-    /// let mut x = Some("Diamonds");
-    /// {
-    ///     let v = x.as_mut_slice();
-    ///     assert!(v == ["Diamonds"]);
-    ///     v[0] = "Dirt";
-    ///     assert!(v == ["Dirt"]);
-    /// }
-    /// assert_eq!(x, Some("Dirt"));
-    /// ```
-    #[inline]
-    #[unstable(feature = "as_slice",
-               reason = "waiting for mut conventions",
-               issue = "27776")]
-    #[rustc_deprecated(since = "1.4.0", reason = "niche API, unclear of usefulness")]
-    #[allow(deprecated)]
-    pub fn as_mut_slice(&mut self) -> &mut [T] {
-        match *self {
-            Some(ref mut x) => {
-                let result: &mut [T] = slice::mut_ref_slice(x);
-                result
-            }
-            None => {
-                let result: &mut [T] = &mut [];
-                result
-            }
-        }
-    }
-
     /////////////////////////////////////////////////////////////////////////
     // Getting to contained values
     /////////////////////////////////////////////////////////////////////////
 
-    /// Unwraps an option, yielding the content of a `Some`
+    /// Unwraps an option, yielding the content of a `Some`.
     ///
     /// # Panics
     ///
@@ -690,22 +653,6 @@ impl<T> Option<T> {
     pub fn take(&mut self) -> Option<T> {
         mem::replace(self, None)
     }
-
-    /// Converts from `Option<T>` to `&[T]` (without copying)
-    #[inline]
-    #[unstable(feature = "as_slice", reason = "unsure of the utility here",
-               issue = "27776")]
-    #[rustc_deprecated(since = "1.4.0", reason = "niche API, unclear of usefulness")]
-    #[allow(deprecated)]
-    pub fn as_slice(&self) -> &[T] {
-        match *self {
-            Some(ref x) => slice::ref_slice(x),
-            None => {
-                let result: &[_] = &[];
-                result
-            }
-        }
-    }
 }
 
 impl<'a, T: Clone> Option<&'a T> {
index 53ef9cd09026b2764dea77af2859e570b33e6260..e1e7869d548db03514b8c0cb2522e6e60f0171a1 100644 (file)
@@ -499,28 +499,12 @@ unsafe impl<T: Send + ?Sized> Send for Unique<T> { }
 #[unstable(feature = "unique", issue = "27730")]
 unsafe impl<T: Sync + ?Sized> Sync for Unique<T> { }
 
-#[cfg(stage0)]
-macro_rules! unique_new {
-    () => (
-        /// Creates a new `Unique`.
-        pub unsafe fn new(ptr: *mut T) -> Unique<T> {
-            Unique { pointer: NonZero::new(ptr), _marker: PhantomData }
-        }
-    )
-}
-#[cfg(not(stage0))]
-macro_rules! unique_new {
-    () => (
-        /// Creates a new `Unique`.
-        pub const unsafe fn new(ptr: *mut T) -> Unique<T> {
-            Unique { pointer: NonZero::new(ptr), _marker: PhantomData }
-        }
-    )
-}
-
 #[unstable(feature = "unique", issue = "27730")]
 impl<T: ?Sized> Unique<T> {
-    unique_new!{}
+    /// Creates a new `Unique`.
+    pub const unsafe fn new(ptr: *mut T) -> Unique<T> {
+        Unique { pointer: NonZero::new(ptr), _marker: PhantomData }
+    }
 
     /// Dereferences the content.
     pub unsafe fn get(&self) -> &T {
@@ -533,7 +517,6 @@ impl<T: ?Sized> Unique<T> {
     }
 }
 
-#[cfg(not(stage0))] // remove cfg after new snapshot
 #[unstable(feature = "unique", issue = "27730")]
 impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> { }
 
@@ -598,7 +581,6 @@ impl<T: ?Sized> Clone for Shared<T> {
 #[unstable(feature = "shared", issue = "27730")]
 impl<T: ?Sized> Copy for Shared<T> { }
 
-#[cfg(not(stage0))] // remove cfg after new snapshot
 #[unstable(feature = "shared", issue = "27730")]
 impl<T: ?Sized, U: ?Sized> CoerceUnsized<Shared<U>> for Shared<T> where T: Unsize<U> { }
 
index 37c40f96b0f7136e4b8bf913c9e8762b1ede5ee5..6ec76c821b30d0172c23b6b1489b7b2dd4707a4c 100644 (file)
@@ -240,7 +240,6 @@ use fmt;
 use iter::{Iterator, DoubleEndedIterator, FromIterator, ExactSizeIterator, IntoIterator};
 use ops::FnOnce;
 use option::Option::{self, None, Some};
-use slice;
 
 /// `Result` is a type that represents either success (`Ok`) or failure (`Err`).
 ///
@@ -251,11 +250,11 @@ use slice;
 pub enum Result<T, E> {
     /// Contains the success value
     #[stable(feature = "rust1", since = "1.0.0")]
-    Ok(T),
+    Ok(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] T),
 
     /// Contains the error value
     #[stable(feature = "rust1", since = "1.0.0")]
-    Err(E)
+    Err(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] E)
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -406,58 +405,6 @@ impl<T, E> Result<T, E> {
         }
     }
 
-    /// Converts from `Result<T, E>` to `&[T]` (without copying)
-    #[inline]
-    #[unstable(feature = "as_slice", reason = "unsure of the utility here",
-               issue = "27776")]
-    #[rustc_deprecated(since = "1.4.0", reason = "niche API, unclear of usefulness")]
-    #[allow(deprecated)]
-    pub fn as_slice(&self) -> &[T] {
-        match *self {
-            Ok(ref x) => slice::ref_slice(x),
-            Err(_) => {
-                // work around lack of implicit coercion from fixed-size array to slice
-                let emp: &[_] = &[];
-                emp
-            }
-        }
-    }
-
-    /// Converts from `Result<T, E>` to `&mut [T]` (without copying)
-    ///
-    /// ```
-    /// #![feature(as_slice)]
-    /// # #![allow(deprecated)]
-    ///
-    /// let mut x: Result<&str, u32> = Ok("Gold");
-    /// {
-    ///     let v = x.as_mut_slice();
-    ///     assert!(v == ["Gold"]);
-    ///     v[0] = "Silver";
-    ///     assert!(v == ["Silver"]);
-    /// }
-    /// assert_eq!(x, Ok("Silver"));
-    ///
-    /// let mut x: Result<&str, u32> = Err(45);
-    /// assert!(x.as_mut_slice().is_empty());
-    /// ```
-    #[inline]
-    #[unstable(feature = "as_slice",
-               reason = "waiting for mut conventions",
-               issue = "27776")]
-    #[rustc_deprecated(since = "1.4.0", reason = "niche API, unclear of usefulness")]
-    #[allow(deprecated)]
-    pub fn as_mut_slice(&mut self) -> &mut [T] {
-        match *self {
-            Ok(ref mut x) => slice::mut_ref_slice(x),
-            Err(_) => {
-                // work around lack of implicit coercion from fixed-size array to slice
-                let emp: &mut [_] = &mut [];
-                emp
-            }
-        }
-    }
-
     /////////////////////////////////////////////////////////////////////////
     // Transforming contained values
     /////////////////////////////////////////////////////////////////////////
@@ -744,6 +691,8 @@ impl<T, E: fmt::Debug> Result<T, E> {
 
     /// Unwraps a result, yielding the content of an `Ok`.
     ///
+    /// # Panics
+    ///
     /// Panics if the value is an `Err`, with a panic message including the
     /// passed message, and the content of the `Err`.
     ///
diff --git a/src/libcore/simd.rs b/src/libcore/simd.rs
deleted file mode 100644 (file)
index 697f96d..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-// 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.
-
-//! SIMD vectors.
-//!
-//! These types can be used for accessing basic SIMD operations. Currently
-//! comparison operators are not implemented. To use SSE3+, you must enable
-//! the features, like `-C target-feature=sse3,sse4.1,sse4.2`, or a more
-//! specific `target-cpu`. No other SIMD intrinsics or high-level wrappers are
-//! provided beyond this module.
-//!
-//! # Stability Note
-//!
-//! These are all experimental. The interface may change entirely, without
-//! warning.
-
-#![unstable(feature = "core_simd",
-            reason = "needs an RFC to flesh out the design",
-            issue = "27731")]
-#![rustc_deprecated(since = "1.3.0",
-              reason = "use the external `simd` crate instead")]
-
-#![allow(non_camel_case_types)]
-#![allow(missing_docs)]
-#![allow(deprecated)]
-
-use ops::{Add, Sub, Mul, Div, Shl, Shr, BitAnd, BitOr, BitXor};
-
-// FIXME(stage0): the contents of macro can be inlined.
-// ABIs are verified as valid as soon as they are parsed, i.e. before
-// `cfg` stripping. The `platform-intrinsic` ABI is new, so stage0
-// doesn't know about it, but it still errors out when it hits it
-// (despite this being in a `cfg(not(stage0))` module).
-macro_rules! argh {
-    () => {
-        extern "platform-intrinsic" {
-            fn simd_add<T>(x: T, y: T) -> T;
-            fn simd_sub<T>(x: T, y: T) -> T;
-            fn simd_mul<T>(x: T, y: T) -> T;
-            fn simd_div<T>(x: T, y: T) -> T;
-            fn simd_shl<T>(x: T, y: T) -> T;
-            fn simd_shr<T>(x: T, y: T) -> T;
-            fn simd_and<T>(x: T, y: T) -> T;
-            fn simd_or<T>(x: T, y: T) -> T;
-            fn simd_xor<T>(x: T, y: T) -> T;
-        }
-    }
-}
-argh!();
-
-#[repr(simd)]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct i8x16(pub i8, pub i8, pub i8, pub i8,
-                 pub i8, pub i8, pub i8, pub i8,
-                 pub i8, pub i8, pub i8, pub i8,
-                 pub i8, pub i8, pub i8, pub i8);
-
-#[repr(simd)]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct i16x8(pub i16, pub i16, pub i16, pub i16,
-                 pub i16, pub i16, pub i16, pub i16);
-
-#[repr(simd)]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct i32x4(pub i32, pub i32, pub i32, pub i32);
-
-#[repr(simd)]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct i64x2(pub i64, pub i64);
-
-#[repr(simd)]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct u8x16(pub u8, pub u8, pub u8, pub u8,
-                 pub u8, pub u8, pub u8, pub u8,
-                 pub u8, pub u8, pub u8, pub u8,
-                 pub u8, pub u8, pub u8, pub u8);
-
-#[repr(simd)]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct u16x8(pub u16, pub u16, pub u16, pub u16,
-                 pub u16, pub u16, pub u16, pub u16);
-
-#[repr(simd)]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct u32x4(pub u32, pub u32, pub u32, pub u32);
-
-#[repr(simd)]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct u64x2(pub u64, pub u64);
-
-#[repr(simd)]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct f32x4(pub f32, pub f32, pub f32, pub f32);
-
-#[repr(simd)]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct f64x2(pub f64, pub f64);
-
-macro_rules! impl_traits {
-    ($($trayt: ident, $method: ident, $func: ident: $($ty: ty),*;)*) => {
-        $($(
-            impl $trayt<$ty> for $ty {
-                type Output = Self;
-                fn $method(self, other: Self) -> Self {
-                    unsafe {
-                        $func(self, other)
-                    }
-                }
-            }
-            )*)*
-    }
-}
-
-impl_traits! {
-    Add, add, simd_add: u8x16, u16x8, u32x4, u64x2, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2;
-    Sub, sub, simd_sub: u8x16, u16x8, u32x4, u64x2, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2;
-    Mul, mul, simd_mul: u8x16, u16x8, u32x4, u64x2, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2;
-
-    Div, div, simd_div: f32x4, f64x2;
-
-    Shl, shl, simd_shl: u8x16, u16x8, u32x4, u64x2, i8x16, i16x8, i32x4, i64x2;
-    Shr, shr, simd_shr: u8x16, u16x8, u32x4, u64x2, i8x16, i16x8, i32x4, i64x2;
-    BitAnd, bitand, simd_and: u8x16, u16x8, u32x4, u64x2, i8x16, i16x8, i32x4, i64x2;
-    BitOr, bitor, simd_or: u8x16, u16x8, u32x4, u64x2, i8x16, i16x8, i32x4, i64x2;
-    BitXor, bitxor, simd_xor: u8x16, u16x8, u32x4, u64x2, i8x16, i16x8, i32x4, i64x2;
-}
diff --git a/src/libcore/simd_old.rs b/src/libcore/simd_old.rs
deleted file mode 100644 (file)
index 7ecd08b..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-// 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.
-
-//! SIMD vectors.
-//!
-//! These types can be used for accessing basic SIMD operations. Each of them
-//! implements the standard arithmetic operator traits (Add, Sub, Mul, Div,
-//! Rem, Shl, Shr) through compiler magic, rather than explicitly. Currently
-//! comparison operators are not implemented. To use SSE3+, you must enable
-//! the features, like `-C target-feature=sse3,sse4.1,sse4.2`, or a more
-//! specific `target-cpu`. No other SIMD intrinsics or high-level wrappers are
-//! provided beyond this module.
-//!
-//! ```rust
-//! # #![feature(core_simd)]
-//! fn main() {
-//!     use std::simd::f32x4;
-//!     let a = f32x4(40.0, 41.0, 42.0, 43.0);
-//!     let b = f32x4(1.0, 1.1, 3.4, 9.8);
-//!     println!("{:?}", a + b);
-//! }
-//! ```
-//!
-//! # Stability Note
-//!
-//! These are all experimental. The interface may change entirely, without
-//! warning.
-
-#![unstable(feature = "core_simd",
-            reason = "needs an RFC to flesh out the design")]
-
-#![allow(non_camel_case_types)]
-#![allow(missing_docs)]
-
-#[simd]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct i8x16(pub i8, pub i8, pub i8, pub i8,
-                 pub i8, pub i8, pub i8, pub i8,
-                 pub i8, pub i8, pub i8, pub i8,
-                 pub i8, pub i8, pub i8, pub i8);
-
-#[simd]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct i16x8(pub i16, pub i16, pub i16, pub i16,
-                 pub i16, pub i16, pub i16, pub i16);
-
-#[simd]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct i32x4(pub i32, pub i32, pub i32, pub i32);
-
-#[simd]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct i64x2(pub i64, pub i64);
-
-#[simd]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct u8x16(pub u8, pub u8, pub u8, pub u8,
-                 pub u8, pub u8, pub u8, pub u8,
-                 pub u8, pub u8, pub u8, pub u8,
-                 pub u8, pub u8, pub u8, pub u8);
-
-#[simd]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct u16x8(pub u16, pub u16, pub u16, pub u16,
-                 pub u16, pub u16, pub u16, pub u16);
-
-#[simd]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct u32x4(pub u32, pub u32, pub u32, pub u32);
-
-#[simd]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct u64x2(pub u64, pub u64);
-
-#[simd]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct f32x4(pub f32, pub f32, pub f32, pub f32);
-
-#[simd]
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct f64x2(pub f64, pub f64);
index 70175086147b73053ffaa3131289cbdaff90a4af..6107564b4aeff7344cdda938226e8e9904ff8d5b 100644 (file)
@@ -48,9 +48,7 @@ use result::Result;
 use result::Result::{Ok, Err};
 use ptr;
 use mem;
-use mem::size_of;
 use marker::{Send, Sync, self};
-use num::wrapping::OverflowingOps;
 use raw::Repr;
 // Avoid conflicts with *both* the Slice trait (buggy) and the `slice::raw` module.
 use raw::Slice as RawSlice;
@@ -152,8 +150,8 @@ pub trait SliceExt {
     #[stable(feature = "core", since = "1.6.0")]
     fn ends_with(&self, needle: &[Self::Item]) -> bool where Self::Item: PartialEq;
 
-    #[unstable(feature = "clone_from_slice", issue= "27750")]
-    fn clone_from_slice(&mut self, &[Self::Item]) -> usize where Self::Item: Clone;
+    #[stable(feature = "clone_from_slice", since = "1.7.0")]
+    fn clone_from_slice(&mut self, &[Self::Item]) where Self::Item: Clone;
 }
 
 // Use macros to be generic over const/mut
@@ -477,14 +475,17 @@ impl<T> SliceExt for [T] {
     }
 
     #[inline]
-    fn clone_from_slice(&mut self, src: &[T]) -> usize where T: Clone {
-        let min = cmp::min(self.len(), src.len());
-        let dst = &mut self[.. min];
-        let src = &src[.. min];
-        for i in 0..min {
-            dst[i].clone_from(&src[i]);
+    fn clone_from_slice(&mut self, src: &[T]) where T: Clone {
+        assert!(self.len() == src.len(),
+                "destination and source slices have different lengths");
+        // NOTE: We need to explicitly slice them to the same length
+        // for bounds checking to be elided, and the optimizer will
+        // generate memcpy for simple cases (for example T = u8).
+        let len = self.len();
+        let src = &src[..len];
+        for i in 0..len {
+            self[i].clone_from(&src[i]);
         }
-        min
     }
 }
 
@@ -1380,24 +1381,6 @@ impl<'a, T> ExactSizeIterator for ChunksMut<'a, T> {}
 // Free functions
 //
 
-/// Converts a reference to A into a slice of length 1 (without copying).
-#[unstable(feature = "ref_slice", issue = "27774")]
-#[rustc_deprecated(since = "1.5.0", reason = "unclear whether belongs in libstd")]
-pub fn ref_slice<A>(s: &A) -> &[A] {
-    unsafe {
-        from_raw_parts(s, 1)
-    }
-}
-
-/// Converts a reference to A into a slice of length 1 (without copying).
-#[unstable(feature = "ref_slice", issue = "27774")]
-#[rustc_deprecated(since = "1.5.0", reason = "unclear whether belongs in libstd")]
-pub fn mut_ref_slice<A>(s: &mut A) -> &mut [A] {
-    unsafe {
-        from_raw_parts_mut(s, 1)
-    }
-}
-
 /// Forms a slice from a pointer and a length.
 ///
 /// The `len` argument is the number of **elements**, not the number of bytes.
index 7aacdbeb768791eac256a50cfa07d3a6b64ac4c6..dd111981f0e57814064d77754150b802347d74db 100644 (file)
@@ -240,7 +240,7 @@ impl Utf8Error {
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> {
-    try!(run_utf8_validation_iterator(&mut v.iter()));
+    try!(run_utf8_validation(v));
     Ok(unsafe { from_utf8_unchecked(v) })
 }
 
@@ -292,7 +292,9 @@ Section: Iterators
 
 /// Iterator for the char (representing *Unicode Scalar Values*) of a string
 ///
-/// Created with the method `.chars()`.
+/// Created with the method [`chars()`].
+///
+/// [`chars()`]: ../primitive.str.html#method.chars
 #[derive(Clone)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Chars<'a> {
@@ -499,7 +501,9 @@ impl<'a> CharIndices<'a> {
 /// External iterator for a string's bytes.
 /// Use with the `std::iter` module.
 ///
-/// Created with the method `.bytes()`.
+/// Created with the method [`bytes()`].
+///
+/// [`bytes()`]: ../primitive.str.html#method.bytes
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Clone)]
 pub struct Bytes<'a>(Cloned<slice::Iter<'a, u8>>);
@@ -782,10 +786,14 @@ impl<'a, P: Pattern<'a>> SplitInternal<'a, P> {
 
 generate_pattern_iterators! {
     forward:
-        /// Created with the method `.split()`.
+        /// Created with the method [`split()`].
+        ///
+        /// [`split()`]: ../primitive.str.html#method.split
         struct Split;
     reverse:
-        /// Created with the method `.rsplit()`.
+        /// Created with the method [`rsplit()`].
+        ///
+        /// [`rsplit()`]: ../primitive.str.html#method.rsplit
         struct RSplit;
     stability:
         #[stable(feature = "rust1", since = "1.0.0")]
@@ -796,10 +804,14 @@ generate_pattern_iterators! {
 
 generate_pattern_iterators! {
     forward:
-        /// Created with the method `.split_terminator()`.
+        /// Created with the method [`split_terminator()`].
+        ///
+        /// [`split_terminator()`]: ../primitive.str.html#method.split_terminator
         struct SplitTerminator;
     reverse:
-        /// Created with the method `.rsplit_terminator()`.
+        /// Created with the method [`rsplit_terminator()`].
+        ///
+        /// [`rsplit_terminator()`]: ../primitive.str.html#method.rsplit_terminator
         struct RSplitTerminator;
     stability:
         #[stable(feature = "rust1", since = "1.0.0")]
@@ -842,10 +854,14 @@ impl<'a, P: Pattern<'a>> SplitNInternal<'a, P> {
 
 generate_pattern_iterators! {
     forward:
-        /// Created with the method `.splitn()`.
+        /// Created with the method [`splitn()`].
+        ///
+        /// [`splitn()`]: ../primitive.str.html#method.splitn
         struct SplitN;
     reverse:
-        /// Created with the method `.rsplitn()`.
+        /// Created with the method [`rsplitn()`].
+        ///
+        /// [`rsplitn()`]: ../primitive.str.html#method.rsplitn
         struct RSplitN;
     stability:
         #[stable(feature = "rust1", since = "1.0.0")]
@@ -880,10 +896,14 @@ impl<'a, P: Pattern<'a>> MatchIndicesInternal<'a, P> {
 
 generate_pattern_iterators! {
     forward:
-        /// Created with the method `.match_indices()`.
+        /// Created with the method [`match_indices()`].
+        ///
+        /// [`match_indices()`]: ../primitive.str.html#method.match_indices
         struct MatchIndices;
     reverse:
-        /// Created with the method `.rmatch_indices()`.
+        /// Created with the method [`rmatch_indices()`].
+        ///
+        /// [`rmatch_indices()`]: ../primitive.str.html#method.rmatch_indices
         struct RMatchIndices;
     stability:
         #[stable(feature = "str_match_indices", since = "1.5.0")]
@@ -920,10 +940,14 @@ impl<'a, P: Pattern<'a>> MatchesInternal<'a, P> {
 
 generate_pattern_iterators! {
     forward:
-        /// Created with the method `.matches()`.
+        /// Created with the method [`matches()`].
+        ///
+        /// [`matches()`]: ../primitive.str.html#method.matches
         struct Matches;
     reverse:
-        /// Created with the method `.rmatches()`.
+        /// Created with the method [`rmatches()`].
+        ///
+        /// [`rmatches()`]: ../primitive.str.html#method.rmatches
         struct RMatches;
     stability:
         #[stable(feature = "str_matches", since = "1.2.0")]
@@ -932,7 +956,9 @@ generate_pattern_iterators! {
     delegate double ended;
 }
 
-/// Created with the method `.lines()`.
+/// Created with the method [`lines()`].
+///
+/// [`lines()`]: ../primitive.str.html#method.lines
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Clone)]
 pub struct Lines<'a>(Map<SplitTerminator<'a, char>, LinesAnyMap>);
@@ -960,7 +986,9 @@ impl<'a> DoubleEndedIterator for Lines<'a> {
     }
 }
 
-/// Created with the method `.lines_any()`.
+/// Created with the method [`lines_any()`].
+///
+/// [`lines_any()`]: ../primitive.str.html#method.lines_any
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_deprecated(since = "1.4.0", reason = "use lines()/Lines instead now")]
 #[derive(Clone)]
@@ -1046,46 +1074,44 @@ unsafe fn cmp_slice(a: &str, b: &str, len: usize) -> i32 {
 }
 
 /*
-Section: Misc
+Section: UTF-8 validation
 */
 
+// use truncation to fit u64 into usize
+const NONASCII_MASK: usize = 0x80808080_80808080u64 as usize;
+
+/// Return `true` if any byte in the word `x` is nonascii (>= 128).
+#[inline]
+fn contains_nonascii(x: usize) -> bool {
+    (x & NONASCII_MASK) != 0
+}
+
 /// Walk through `iter` checking that it's a valid UTF-8 sequence,
 /// returning `true` in that case, or, if it is invalid, `false` with
 /// `iter` reset such that it is pointing at the first byte in the
 /// invalid sequence.
 #[inline(always)]
-fn run_utf8_validation_iterator(iter: &mut slice::Iter<u8>)
-                                -> Result<(), Utf8Error> {
-    let whole = iter.as_slice();
-    loop {
-        // save the current thing we're pointing at.
-        let old = iter.clone();
-
-        // restore the iterator we had at the start of this codepoint.
+fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> {
+    let mut offset = 0;
+    let len = v.len();
+    while offset < len {
+        let old_offset = offset;
         macro_rules! err { () => {{
-            *iter = old.clone();
             return Err(Utf8Error {
-                valid_up_to: whole.len() - iter.as_slice().len()
+                valid_up_to: old_offset
             })
         }}}
 
-        macro_rules! next { () => {
-            match iter.next() {
-                Some(a) => *a,
-                // we needed data, but there was none: error!
-                None => err!(),
+        macro_rules! next { () => {{
+            offset += 1;
+            // we needed data, but there was none: error!
+            if offset >= len {
+                err!()
             }
-        }}
-
-        let first = match iter.next() {
-            Some(&b) => b,
-            // we're at the end of the iterator and a codepoint
-            // boundary at the same time, so this string is valid.
-            None => return Ok(())
-        };
+            v[offset]
+        }}}
 
-        // ASCII characters are always valid, so only large
-        // bytes need more examination.
+        let first = v[offset];
         if first >= 128 {
             let w = UTF8_CHAR_WIDTH[first as usize];
             let second = next!();
@@ -1128,8 +1154,43 @@ fn run_utf8_validation_iterator(iter: &mut slice::Iter<u8>)
                 }
                 _ => err!()
             }
+            offset += 1;
+        } else {
+            // Ascii case, try to skip forward quickly.
+            // When the pointer is aligned, read 2 words of data per iteration
+            // until we find a word containing a non-ascii byte.
+            let usize_bytes = mem::size_of::<usize>();
+            let bytes_per_iteration = 2 * usize_bytes;
+            let ptr = v.as_ptr();
+            let align = (ptr as usize + offset) & (usize_bytes - 1);
+            if align == 0 {
+                if len >= bytes_per_iteration {
+                    while offset <= len - bytes_per_iteration {
+                        unsafe {
+                            let u = *(ptr.offset(offset as isize) as *const usize);
+                            let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize);
+
+                            // break if there is a nonascii byte
+                            let zu = contains_nonascii(u);
+                            let zv = contains_nonascii(v);
+                            if zu || zv {
+                                break;
+                            }
+                        }
+                        offset += bytes_per_iteration;
+                    }
+                }
+                // step from the point where the wordwise loop stopped
+                while offset < len && v[offset] < 128 {
+                    offset += 1;
+                }
+            } else {
+                offset += 1;
+            }
         }
     }
+
+    Ok(())
 }
 
 // https://tools.ietf.org/html/rfc3629
index eeaaa3e217e8f1c80bbb0aca874f3f688c3c42ce..a9fc8913182b37ddf18611f492a6fab730ac9d3e 100644 (file)
@@ -119,6 +119,11 @@ fn any_fixed_vec() {
     assert!(!test.is::<[usize; 10]>());
 }
 
+#[test]
+fn any_unsized() {
+    fn is_any<T: Any + ?Sized>() {}
+    is_any::<[i32]>();
+}
 
 #[bench]
 fn bench_downcast_ref(b: &mut Bencher) {
index d23442379bcd59095eb10767a480e5567539535f..c1f3ea42ef4f768d9b21e968e7fec2181f9eefd5 100644 (file)
@@ -216,3 +216,43 @@ fn test_decode_utf16() {
     check(&[0xD800, 0x41, 0x42], &[Err(0xD800), Ok('A'), Ok('B')]);
     check(&[0xD800, 0], &[Err(0xD800), Ok('\0')]);
 }
+
+#[test]
+fn ed_iterator_specializations() {
+    // Check counting
+    assert_eq!('\n'.escape_default().count(), 2);
+    assert_eq!('c'.escape_default().count(), 1);
+    assert_eq!(' '.escape_default().count(), 1);
+    assert_eq!('\\'.escape_default().count(), 2);
+    assert_eq!('\''.escape_default().count(), 2);
+
+    // Check nth
+
+    // Check that OoB is handled correctly
+    assert_eq!('\n'.escape_default().nth(2), None);
+    assert_eq!('c'.escape_default().nth(1), None);
+    assert_eq!(' '.escape_default().nth(1), None);
+    assert_eq!('\\'.escape_default().nth(2), None);
+    assert_eq!('\''.escape_default().nth(2), None);
+
+    // Check the first char
+    assert_eq!('\n'.escape_default().nth(0), Some('\\'));
+    assert_eq!('c'.escape_default().nth(0), Some('c'));
+    assert_eq!(' '.escape_default().nth(0), Some(' '));
+    assert_eq!('\\'.escape_default().nth(0), Some('\\'));
+    assert_eq!('\''.escape_default().nth(0), Some('\\'));
+
+    // Check the second char
+    assert_eq!('\n'.escape_default().nth(1), Some('n'));
+    assert_eq!('\\'.escape_default().nth(1), Some('\\'));
+    assert_eq!('\''.escape_default().nth(1), Some('\''));
+
+    // Check the last char
+    assert_eq!('\n'.escape_default().last(), Some('n'));
+    assert_eq!('c'.escape_default().last(), Some('c'));
+    assert_eq!(' '.escape_default().last(), Some(' '));
+    assert_eq!('\\'.escape_default().last(), Some('\\'));
+    assert_eq!('\''.escape_default().last(), Some('\''));
+}
+
+
index 9def44191db0599fec4e861ccc245c981a74a214..ba308314e9e8feb6b097afa988c0d41089cdf2de 100644 (file)
@@ -829,18 +829,6 @@ fn test_range() {
                (isize::MAX as usize + 2, Some(isize::MAX as usize + 2)));
 }
 
-#[test]
-fn test_range_inclusive() {
-    assert!(range_inclusive(0, 5).collect::<Vec<isize>>() ==
-            vec![0, 1, 2, 3, 4, 5]);
-    assert!(range_inclusive(0, 5).rev().collect::<Vec<isize>>() ==
-            vec![5, 4, 3, 2, 1, 0]);
-    assert_eq!(range_inclusive(200, -5).count(), 0);
-    assert_eq!(range_inclusive(200, -5).rev().count(), 0);
-    assert_eq!(range_inclusive(200, 200).collect::<Vec<isize>>(), [200]);
-    assert_eq!(range_inclusive(200, 200).rev().collect::<Vec<isize>>(), [200]);
-}
-
 #[test]
 fn test_range_step() {
     assert_eq!((0..20).step_by(5).collect::<Vec<isize>>(), [0, 5, 10, 15]);
index 20da4a86bf5458b526f6140202124083f298ceb9..88f1835d2cce4b42499b1f868fbf3d6697895346 100644 (file)
@@ -43,6 +43,7 @@
 #![feature(unboxed_closures)]
 #![feature(unicode)]
 #![feature(unique)]
+#![feature(clone_from_slice)]
 
 extern crate core;
 extern crate test;
index 0c92b2fe2a7dc425de69110c586b98daf62c14d3..7b25333e21ed214a48c2e08179b9067738587816 100644 (file)
@@ -98,7 +98,8 @@ fn fast_path_correct() {
 
 #[test]
 fn lonely_dot() {
-    assert_eq!(".".parse(), Ok(0.0));
+    assert!(".".parse::<f32>().is_err());
+    assert!(".".parse::<f64>().is_err());
 }
 
 #[test]
index 309bf6d8192188870433f8296f5646cb98dc9da9..48a5501acb79114c92e91bb4aef2e865ec2057d1 100644 (file)
@@ -10,7 +10,6 @@
 
 use std::prelude::v1::*;
 use std::{str, mem, i16, f32, f64, fmt};
-use std::slice::bytes;
 use std::__rand as rand;
 use rand::{Rand, XorShiftRng};
 use rand::distributions::{IndependentSample, Range};
@@ -101,7 +100,7 @@ fn check_exact<F, T>(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16
 
     // check significant digits
     for i in 1..cut.unwrap_or(expected.len() - 1) {
-        bytes::copy_memory(&expected[..i], &mut expected_);
+        expected_[..i].clone_from_slice(&expected[..i]);
         let mut expectedk_ = expectedk;
         if expected[i] >= b'5' {
             // check if this is a rounding-to-even case.
@@ -148,7 +147,7 @@ fn check_exact<F, T>(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16
     // check infinite zero digits
     if let Some(cut) = cut {
         for i in cut..expected.len()-1 {
-            bytes::copy_memory(&expected[..cut], &mut expected_);
+            expected_[..cut].clone_from_slice(&expected[..cut]);
             for c in &mut expected_[cut..i] { *c = b'0'; }
 
             try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk;
index 738761f3911b67da48a9eb0268728bbb30f67242..fba56db32bb4c65376be1a666cdfe03a2f56bd72 100644 (file)
@@ -60,27 +60,6 @@ mod tests {
         assert_eq!(u, None);
         let s : Option<i16> = i16::from_str_radix("80000", 10).ok();
         assert_eq!(s, None);
-        let s = "10000000000000000000000000000000000000000";
-        let f : Option<f32> = f32::from_str_radix(s, 10).ok();
-        assert_eq!(f, Some(Float::infinity()));
-        let fe : Option<f32> = f32::from_str_radix("1e40", 10).ok();
-        assert_eq!(fe, Some(Float::infinity()));
-    }
-
-    #[test]
-    fn test_from_str_radix_float() {
-        let x1 : Option<f64> = f64::from_str_radix("-123.456", 10).ok();
-        assert_eq!(x1, Some(-123.456));
-        let x2 : Option<f32> = f32::from_str_radix("123.456", 10).ok();
-        assert_eq!(x2, Some(123.456));
-        let x3 : Option<f32> = f32::from_str_radix("-0.0", 10).ok();
-        assert_eq!(x3, Some(-0.0));
-        let x4 : Option<f32> = f32::from_str_radix("0.0", 10).ok();
-        assert_eq!(x4, Some(0.0));
-        let x4 : Option<f32> = f32::from_str_radix("1.0", 10).ok();
-        assert_eq!(x4, Some(1.0));
-        let x5 : Option<f32> = f32::from_str_radix("-1.0", 10).ok();
-        assert_eq!(x5, Some(-1.0));
     }
 
     #[test]
index 02810bf668a0a5df31d95b483ce34fdf803e35a6..a60a1c67e175d2165d27aaeaf474c018bec53cc0 100644 (file)
 //! [def]: https://en.wikipedia.org/wiki/DEFLATE
 //! [mz]: https://code.google.com/p/miniz/
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "flate"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -30,7 +27,6 @@
 #![feature(staged_api)]
 #![feature(unique)]
 #![cfg_attr(test, feature(rustc_private, rand, vec_push_all))]
-#![cfg_attr(stage0, allow(improper_ctypes))]
 
 #[cfg(test)]
 #[macro_use]
index 6e185c674a698d30a046a074bd04d1dff25848de..7a229ad522227a7229e34958f013f91f82097466 100644 (file)
 //! Parsing does not happen at runtime: structures of `std::fmt::rt` are
 //! generated instead.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "fmt_macros"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
index 228ceb92da661e3d228ee8447e4b864a8b30ff76..57ce53e73b02577a95972139abb01612b9ab345e 100644 (file)
 //! }
 //! ```
 
-
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "getopts"]
 #![unstable(feature = "rustc_private",
             reason = "use the crates.io `getopts` library instead",
             issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
index c1bd188d3a286ef9b4ca1a84ecf103595f12ed18..38b45ec0feaedacbcd9294e710b36d20b66d4e0c 100644 (file)
@@ -47,9 +47,9 @@
 //! which is cyclic.
 //!
 //! ```rust
-//! #![feature(rustc_private, into_cow)]
+//! #![feature(rustc_private)]
 //!
-//! use std::borrow::IntoCow;
+//! use graphviz::IntoCow;
 //! use std::io::Write;
 //! use graphviz as dot;
 //!
 //!
 //! * [DOT language](http://www.graphviz.org/doc/info/lang.html)
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "graphviz"]
 #![unstable(feature = "rustc_private", issue = "27812")]
 #![feature(staged_api)]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
        html_root_url = "https://doc.rust-lang.org/nightly/",
        test(attr(allow(unused_variables), deny(warnings))))]
 
-#![feature(into_cow)]
 #![feature(str_escape)]
 
 use self::LabelText::*;
 
-use std::borrow::{IntoCow, Cow};
+use std::borrow::{Cow, ToOwned};
 use std::io::prelude::*;
 use std::io;
 
@@ -722,6 +718,34 @@ pub fn render_opts<'a,
     writeln(w, &["}"])
 }
 
+pub trait IntoCow<'a, B: ?Sized> where B: ToOwned {
+    fn into_cow(self) -> Cow<'a, B>;
+}
+
+impl<'a> IntoCow<'a, str> for String {
+    fn into_cow(self) -> Cow<'a, str> {
+        Cow::Owned(self)
+    }
+}
+
+impl<'a> IntoCow<'a, str> for &'a str {
+    fn into_cow(self) -> Cow<'a, str> {
+        Cow::Borrowed(self)
+    }
+}
+
+impl<'a, T: Clone> IntoCow<'a, [T]> for Vec<T> {
+    fn into_cow(self) -> Cow<'a, [T]> {
+        Cow::Owned(self)
+    }
+}
+
+impl<'a, T: Clone> IntoCow<'a, [T]> for &'a [T] {
+    fn into_cow(self) -> Cow<'a, [T]> {
+        Cow::Borrowed(self)
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use self::NodeLabels::*;
@@ -729,7 +753,7 @@ mod tests {
     use super::LabelText::{self, LabelStr, EscStr, HtmlStr};
     use std::io;
     use std::io::prelude::*;
-    use std::borrow::IntoCow;
+    use IntoCow;
 
     /// each node is an index in a vector in the graph.
     type Node = usize;
index fab85e7794f529e3733674567b380777fbaa96dc..333fb22562264d172223fb44db3d51a48a48b6f4 100644 (file)
@@ -25,7 +25,7 @@ env:
 matrix:
   include:
     - os: linux
-      env: TARGET=arm-linux-androideabi
+      env: TARGET=arm-linux-androideabi DOCKER=alexcrichton/rust-slave-android:2015-11-22
       rust: nightly
     - os: linux
       env: TARGET=x86_64-unknown-linux-musl
@@ -34,7 +34,7 @@ matrix:
       env: TARGET=arm-unknown-linux-gnueabihf
       rust: nightly
     - os: linux
-      env: TARGET=mips-unknown-linux-gnu
+      env: TARGET=mips-unknown-linux-gnu DOCKER=alexcrichton/rust-libc-mips:2016-01-10
       rust: nightly
     - os: linux
       env: TARGET=aarch64-unknown-linux-gnu
@@ -45,6 +45,9 @@ matrix:
     - os: osx
       env: TARGET=x86_64-apple-ios
       rust: nightly-2015-09-08
+    - os: linux
+      env: TARGET=x86_64-rumprun-netbsd DOCKER=alexcrichton/rust-libc-rumprun:2015-11-27
+      rust: nightly-2015-09-27
 notifications:
   email:
     on_success: never
index 2a788dadbc966c523d7dc777dfa2ad9ed4ee5573..b4298148746e655ed53a0199f905868cfc1d915a 100644 (file)
@@ -1,7 +1,7 @@
 [package]
 
 name = "libc"
-version = "0.2.2"
+version = "0.2.4"
 authors = ["The Rust Project Developers"]
 license = "MIT/Apache-2.0"
 readme = "README.md"
index e738f18902dc0ee6d076ae40f6820223ccd760a8..41e232e5460620e382928055ea8c86df510da320 100644 (file)
@@ -7,7 +7,7 @@ linker = "arm-linux-androideabi-gcc"
 linker = "arm-linux-gnueabihf-gcc-4.7"
 
 [target.mips-unknown-linux-gnu]
-linker = "mips-linux-gnu-gcc"
+linker = "mips-linux-gnu-gcc-5"
 
 [target.aarch64-unknown-linux-gnu]
 linker = "aarch64-linux-gnu-gcc"
diff --git a/src/liblibc/ci/mips/Dockerfile b/src/liblibc/ci/mips/Dockerfile
new file mode 100644 (file)
index 0000000..6da6f68
--- /dev/null
@@ -0,0 +1,12 @@
+FROM ubuntu:15.10
+
+RUN apt-get update
+RUN apt-get install -y --force-yes --no-install-recommends \
+        software-properties-common
+RUN add-apt-repository ppa:angelsl/mips-cross
+RUN apt-get update
+RUN apt-get install -y --force-yes --no-install-recommends \
+        gcc-5-mips-linux-gnu libc6-dev-mips-cross \
+        gcc-5-mipsel-linux-gnu libc6-dev-mipsel-cross
+RUN apt-get install -y --force-yes --no-install-recommends \
+                build-essential qemu-user
diff --git a/src/liblibc/ci/rumprun/Dockerfile b/src/liblibc/ci/rumprun/Dockerfile
new file mode 100644 (file)
index 0000000..16c7d37
--- /dev/null
@@ -0,0 +1,12 @@
+FROM ubuntu:15.04
+
+RUN apt-get update
+RUN apt-get install -y binutils git g++ make qemu
+
+WORKDIR /build
+RUN git clone --recursive http://repo.rumpkernel.org/rumprun
+WORKDIR /build/rumprun
+RUN CC=cc ./build-rr.sh hw
+
+ENV PATH=$PATH:/build/rumprun/rumprun/bin
+WORKDIR /root
index 1e4b144cbed79159c711c1ae8b981384a066df97..75ce8ba1d8f478d0fa8ce06f91a8ce2876d8169d 100644 (file)
@@ -16,55 +16,72 @@ if [ "$TARGET" = "" ]; then
 fi
 
 MAIN_TARGETS=https://static.rust-lang.org/dist
-EXTRA_TARGETS=https://people.mozilla.org/~acrichton/libc-test/2015-09-08
+DATE=$(echo $TRAVIS_RUST_VERSION | sed s/nightly-//)
+EXTRA_TARGETS=https://people.mozilla.org/~acrichton/libc-test/$DATE
 
 install() {
-  sudo apt-get update
-  sudo apt-get install -y $@
+  if [ "$TRAVIS" = "true" ]; then
+    sudo apt-get update
+    sudo apt-get install -y $@
+  fi
 }
 
-case "$TARGET" in
-  *-apple-ios)
-    curl -s $EXTRA_TARGETS/$TARGET.tar.gz | tar xzf - -C $HOME/rust/lib/rustlib
-    ;;
+mkdir -p .cargo
+cp ci/cargo-config .cargo/config
 
-  *)
-    # Download the rustlib folder from the relevant portion of main distribution's
-    # tarballs.
-    dir=rust-std-$TARGET
-    pkg=rust-std
-    if [ "$TRAVIS_RUST_VERSION" = "1.0.0" ]; then
-      pkg=rust
-      dir=rustc
-    fi
-    curl -s $MAIN_TARGETS/$pkg-$TRAVIS_RUST_VERSION-$TARGET.tar.gz | \
-      tar xzf - -C $HOME/rust/lib/rustlib --strip-components=4 \
-        $pkg-$TRAVIS_RUST_VERSION-$TARGET/$dir/lib/rustlib/$TARGET
-    ;;
+if [ "$TRAVIS" = "true" ]; then
+  case "$TARGET" in
+    *-apple-ios | *-rumprun-*)
+      curl -s $EXTRA_TARGETS/$TARGET.tar.gz | \
+       tar xzf - -C `rustc --print sysroot`/lib/rustlib
+      ;;
+
+    *)
+      # Download the rustlib folder from the relevant portion of main
+      # distribution's tarballs.
+      dir=rust-std-$TARGET
+      pkg=rust-std
+      if [ "$TRAVIS_RUST_VERSION" = "1.0.0" ]; then
+        pkg=rust
+        dir=rustc
+      fi
+      curl -s $MAIN_TARGETS/$pkg-$TRAVIS_RUST_VERSION-$TARGET.tar.gz | \
+        tar xzf - -C $HOME/rust/lib/rustlib --strip-components=4 \
+          $pkg-$TRAVIS_RUST_VERSION-$TARGET/$dir/lib/rustlib/$TARGET
+      ;;
+
+  esac
+fi
 
-esac
+# Pull a pre-built docker image for testing android, then run tests entirely
+# within that image. Note that this is using the same rustc installation that
+# travis has (sharing it via `-v`) and otherwise the tests run entirely within
+# the container.
+if [ "$DOCKER" != "" ]; then
+  args=""
+
+  case "$TARGET" in
+    mips-unknown-linux-gnu)
+      args="$args -e CC=mips-linux-gnu-gcc-5"
+      ;;
+
+    *)
+      ;;
+  esac
+
+  exec docker run \
+    --entrypoint bash \
+    -v `rustc --print sysroot`:/usr/local:ro \
+    -v `pwd`:/checkout \
+    -e LD_LIBRARY_PATH=/usr/local/lib \
+    -e CARGO_TARGET_DIR=/tmp \
+    $args \
+    -w /checkout \
+    -it $DOCKER \
+    ci/run.sh $TARGET
+fi
 
 case "$TARGET" in
-  # Pull a pre-built docker image for testing android, then run tests entirely
-  # within that image. Note that this is using the same rustc installation that
-  # travis has (sharing it via `-v`) and otherwise the tests run entirely within
-  # the container.
-  arm-linux-androideabi)
-    script="
-cp -r /checkout/* .
-mkdir .cargo
-cp ci/cargo-config .cargo/config
-exec sh ci/run.sh $TARGET
-"
-    exec docker run \
-      --entrypoint bash \
-      -v $HOME/rust:/usr/local:ro \
-      -v `pwd`:/checkout:ro \
-      -e LD_LIBRARY_PATH=/usr/local/lib \
-      -it alexcrichton/rust-slave-android:2015-10-21 \
-      -c "$script"
-    ;;
-
   x86_64-unknown-linux-musl)
     install musl-tools
     export CC=musl-gcc
@@ -83,19 +100,6 @@ exec sh ci/run.sh $TARGET
   *-apple-ios)
     ;;
 
-  mips-unknown-linux-gnu)
-    # Download pre-built and custom MIPS libs and then also instsall the MIPS
-    # compiler according to this post:
-    # http://sathisharada.blogspot.com/2014_10_01_archive.html
-    echo 'deb http://ftp.de.debian.org/debian squeeze main' | \
-      sudo tee -a /etc/apt/sources.list
-    echo 'deb http://www.emdebian.org/debian/ squeeze main' | \
-      sudo tee -a /etc/apt/sources.list
-    install emdebian-archive-keyring
-    install qemu-user gcc-4.4-mips-linux-gnu -y --force-yes
-    export CC=mips-linux-gnu-gcc
-    ;;
-
   *)
     # clang has better error messages and implements alignof more broadly
     export CC=clang
@@ -107,8 +111,6 @@ exec sh ci/run.sh $TARGET
 
 esac
 
-mkdir .cargo
-cp ci/cargo-config .cargo/config
 sh ci/run.sh $TARGET
 
 if [ "$TARGET" = "x86_64-unknown-linux-gnu" ] && \
index 635422cd753dbfc593df0c7a11939944e1bc3ba3..14985be912f0f3bcbf3b8b8ce0318c789849042b 100644 (file)
@@ -19,11 +19,11 @@ esac
 
 case "$TARGET" in
   arm-linux-androideabi)
-    emulator @arm-18 -no-window &
+    emulator @arm-21 -no-window &
     adb wait-for-device
-    adb push libc-test/target/$TARGET/debug/libc-test /data/libc-test
-    adb shell /data/libc-test 2>&1 | tee out
-    grep "^PASSED .* tests" out
+    adb push /tmp/$TARGET/debug/libc-test /data/libc-test
+    adb shell /data/libc-test 2>&1 | tee /tmp/out
+    grep "^PASSED .* tests" /tmp/out
     ;;
 
   arm-unknown-linux-gnueabihf)
@@ -31,7 +31,7 @@ case "$TARGET" in
     ;;
 
   mips-unknown-linux-gnu)
-    qemu-mips -L /usr/mips-linux-gnu libc-test/target/$TARGET/debug/libc-test
+    qemu-mips -L /usr/mips-linux-gnu /tmp/$TARGET/debug/libc-test
     ;;
 
   aarch64-unknown-linux-gnu)
@@ -39,6 +39,14 @@ case "$TARGET" in
       libc-test/target/$TARGET/debug/libc-test
     ;;
 
+  *-rumprun-netbsd)
+    rumprun-bake hw_virtio /tmp/libc-test.img /tmp/$TARGET/debug/libc-test
+    qemu-system-x86_64 -nographic -vga none -m 64 \
+        -kernel /tmp/libc-test.img 2>&1 | tee /tmp/out &
+    sleep 5
+    grep "^PASSED .* tests" /tmp/out
+    ;;
+
   *-apple-ios)
     libc-test/target/$TARGET/debug/libc-test
     ;;
diff --git a/src/liblibc/libc-test/Cargo.lock b/src/liblibc/libc-test/Cargo.lock
new file mode 100644 (file)
index 0000000..f4678de
--- /dev/null
@@ -0,0 +1,113 @@
+[root]
+name = "libc-test"
+version = "0.1.0"
+dependencies = [
+ "ctest 0.1.0 (git+https://github.com/alexcrichton/ctest)",
+ "libc 0.2.4",
+]
+
+[[package]]
+name = "advapi32-sys"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "bitflags"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "ctest"
+version = "0.1.0"
+source = "git+https://github.com/alexcrichton/ctest#4e3a8027b540b79769fcd3945a6de9f1e5edf8e0"
+dependencies = [
+ "gcc 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_syntax 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gcc"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "kernel32-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libc"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.4"
+
+[[package]]
+name = "log"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rustc-serialize"
+version = "0.3.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "syntex_syntax"
+version = "0.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "term"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-xid"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
index ccb902d9e24e157fc13beb7c4589b053f09896cb..1a071e68aee9515bd7101195e4759e5a413572b5 100644 (file)
@@ -6,6 +6,7 @@ use std::env;
 
 fn main() {
     let target = env::var("TARGET").unwrap();
+    let x86_64 = target.contains("x86_64");
     let windows = target.contains("windows");
     let mingw = target.contains("windows-gnu");
     let linux = target.contains("unknown-linux");
@@ -13,11 +14,15 @@ fn main() {
     let apple = target.contains("apple");
     let musl = target.contains("musl");
     let freebsd = target.contains("freebsd");
-    let bsdlike = freebsd || apple;
+    let mips = target.contains("mips");
+    let netbsd = target.contains("netbsd");
+    let openbsd = target.contains("openbsd");
+    let rumprun = target.contains("rumprun");
+    let bsdlike = freebsd || apple || netbsd || openbsd;
     let mut cfg = ctest::TestGenerator::new();
 
     // Pull in extra goodies on linux/mingw
-    if target.contains("unknown-linux") {
+    if linux || android {
         cfg.define("_GNU_SOURCE", None);
     } else if windows {
         cfg.define("_WIN32_WINNT", Some("0x8000"));
@@ -57,6 +62,9 @@ fn main() {
     } else {
         cfg.header("ctype.h");
         cfg.header("dirent.h");
+        if openbsd {
+            cfg.header("sys/socket.h");
+        }
         cfg.header("net/if.h");
         cfg.header("netdb.h");
         cfg.header("netinet/in.h");
@@ -78,6 +86,13 @@ fn main() {
         cfg.header("utime.h");
         cfg.header("pwd.h");
         cfg.header("grp.h");
+        cfg.header("sys/utsname.h");
+        cfg.header("sys/ptrace.h");
+        cfg.header("sys/mount.h");
+        cfg.header("sys/uio.h");
+        cfg.header("sched.h");
+        cfg.header("termios.h");
+        cfg.header("poll.h");
     }
 
     if android {
@@ -86,11 +101,17 @@ fn main() {
     } else if !windows {
         cfg.header("glob.h");
         cfg.header("ifaddrs.h");
+        if !openbsd {
+            cfg.header("sys/quota.h");
+        }
         cfg.header("sys/statvfs.h");
 
         if !musl {
-            cfg.header("execinfo.h");
             cfg.header("sys/sysctl.h");
+
+            if !netbsd && !openbsd {
+                cfg.header("execinfo.h");
+            }
         }
     }
 
@@ -103,19 +124,54 @@ fn main() {
         }
     }
 
+    if bsdlike {
+        cfg.header("sys/event.h");
+    }
+
+    if linux {
+        cfg.header("mqueue.h");
+        cfg.header("sys/signalfd.h");
+        cfg.header("sys/xattr.h");
+        cfg.header("sys/ipc.h");
+        cfg.header("sys/shm.h");
+    }
+
     if linux || android {
-        cfg.header("netpacket/packet.h");
-        cfg.header("net/ethernet.h");
         cfg.header("malloc.h");
+        cfg.header("net/ethernet.h");
+        cfg.header("netpacket/packet.h");
+        cfg.header("sched.h");
+        cfg.header("sys/epoll.h");
+        cfg.header("sys/eventfd.h");
         cfg.header("sys/prctl.h");
-        /* linux kernel header */
+        cfg.header("sys/vfs.h");
+        cfg.header("sys/syscall.h");
         if !musl {
             cfg.header("linux/netlink.h");
+            cfg.header("linux/magic.h");
+
+            if !mips {
+                cfg.header("linux/quota.h");
+            }
         }
     }
 
     if freebsd {
         cfg.header("pthread_np.h");
+        cfg.header("sched.h");
+    }
+
+    if netbsd {
+        cfg.header("ufs/ufs/quota.h");
+        cfg.header("ufs/ufs/quota1.h");
+        cfg.header("sys/ioctl_compat.h");
+    }
+
+    if openbsd {
+        cfg.header("ufs/ufs/quota.h");
+        cfg.header("rpcsvc/rex.h");
+        cfg.header("pthread_np.h");
+        cfg.header("sys/syscall.h");
     }
 
     cfg.type_name(move |ty, is_struct| {
@@ -157,6 +213,8 @@ fn main() {
     let target2 = target.clone();
     cfg.field_name(move |struct_, field| {
         match field {
+            "st_birthtime"      if openbsd && struct_ == "stat" => "__st_birthtime".to_string(),
+            "st_birthtime_nsec" if openbsd && struct_ == "stat" => "__st_birthtimensec".to_string(),
             // Our stat *_nsec fields normally don't actually exist but are part
             // of a timeval struct
             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
@@ -168,6 +226,7 @@ fn main() {
                     s.replace("e_nsec", ".tv_nsec")
                 }
             }
+            "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
             s => s.to_string(),
         }
     });
@@ -184,6 +243,10 @@ fn main() {
     cfg.skip_struct(move |ty| {
         match ty {
             "sockaddr_nl" => musl,
+
+            // The alignment of this is 4 on 64-bit OSX...
+            "kevent" if apple && x86_64 => true,
+
             _ => false
         }
     });
@@ -222,13 +285,28 @@ fn main() {
             "RLIMIT_NLIMITS" |
             "TCP_COOKIE_TRANSACTIONS" |
             "RLIMIT_RTTIME" if musl => true,
+            // work around super old mips toolchain
+            "SCHED_IDLE" | "SHM_NORESERVE" => mips,
+
+            // weird signed extension or something like that?
+            "MS_NOUSER" => true,
+
+            // These OSX constants are flagged as deprecated
+            "NOTE_EXIT_REPARENTED" |
+            "NOTE_REAP" if apple => true,
+
+            // The linux/quota.h header file which defines these can't be
+            // included with sys/quota.h currently on MIPS, so we don't include
+            // it and just ignore these constants
+            "QFMT_VFS_OLD" |
+            "QFMT_VFS_V0" if mips && linux => true,
 
             _ => false,
         }
     });
 
     cfg.skip_fn(move |name| {
-        // skip those that are manually verifiedmanually verified
+        // skip those that are manually verified
         match name {
             "execv" |       // crazy stuff with const/mut
             "execve" |
@@ -240,7 +318,10 @@ fn main() {
             "strerror_r" if linux => true,   // actually xpg-something-or-other
 
             // typed 2nd arg on linux and android
-            "gettimeofday" if linux || android || freebsd => true,
+            "gettimeofday" if linux || android || freebsd || openbsd => true,
+
+            // not declared in newer android toolchains
+            "getdtablesize" if android => true,
 
             "dlerror" if android => true, // const-ness is added
             "dladdr" if musl => true, // const-ness only added recently
@@ -249,12 +330,58 @@ fn main() {
             // Rust, but is close enough to *mut
             "timegm" if apple => true,
 
+            // OSX's daemon is deprecated in 10.5 so we'll get a warning (which
+            // we turn into an error) so just ignore it.
+            "daemon" if apple => true,
+
+            // These functions presumably exist on netbsd but don't look like
+            // they're implemented on rumprun yet, just let them slide for now.
+            // Some of them look like they have headers but then don't have
+            // corresponding actual definitions either...
+            "backtrace" |
+            "pthread_main_np" |
+            "pthread_set_name_np" |
+            "pthread_stackseg_np" |
+            "shm_open" |
+            "shm_unlink" |
+            "syscall" |
+            "ptrace" |
+            "sigaltstack" if rumprun => true,
+
+            // There seems to be a small error in EGLIBC's eventfd.h header. The
+            // [underlying system call][1] always takes its first `count`
+            // argument as an `unsigned int`, but [EGLIBC's <sys/eventfd.h>
+            // header][2] declares it to take an `int`. [GLIBC's header][3]
+            // matches the kernel.
+            //
+            // EGLIBC is no longer actively developed, and Debian, the largest
+            // distribution that had been using it, switched back to GLIBC in
+            // April 2015. So effectively all Linux <sys/eventfd.h> headers will
+            // be using `unsigned int` soon.
+            //
+            // [1]: https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/tree/fs/eventfd.c?id=refs/tags/v3.12.51#n397
+            // [2]: http://bazaar.launchpad.net/~ubuntu-branches/ubuntu/trusty/eglibc/trusty/view/head:/sysdeps/unix/sysv/linux/sys/eventfd.h
+            // [3]: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/sys/eventfd.h;h=6295f32e937e779e74318eb9d3bdbe76aef8a8f3;hb=4e42b5b8f89f0e288e68be7ad70f9525aebc2cff#l34
+            "eventfd" if linux => true,
+
             _ => false,
         }
     });
 
-    // Windows dllimport oddness?
-    cfg.skip_fn_ptrcheck(move |_| windows);
+    cfg.skip_fn_ptrcheck(move |name| {
+        match name {
+            // This used to be called bsd_signal in rev 18 of the android
+            // platform and is now just called signal, the old `bsd_signal`
+            // symbol, however, still remains, just gives a different function
+            // pointer.
+            "signal" if android => true,
+
+            // dllimport weirdness?
+            _ if windows => true,
+
+            _ => false,
+        }
+    });
 
     cfg.skip_field_type(move |struct_, field| {
         // This is a weird union, don't check the type.
@@ -271,5 +398,13 @@ fn main() {
         (musl && struct_ == "glob_t" && field == "gl_flags")
     });
 
+    cfg.fn_cname(move |name, cname| {
+        if windows {
+            cname.unwrap_or(name).to_string()
+        } else {
+            name.to_string()
+        }
+    });
+
     cfg.generate("../src/lib.rs", "all.rs");
 }
index 3a608c8cb99bb86ca0c81a5c015fdeb9bf03e049..c7d3dc9c2deba67253d7e0f9a5c72495cbae1a2d 100644 (file)
@@ -26,6 +26,10 @@ mod imp {
     #[lang = "copy"]
     pub trait Copy {}
 
+    #[lang = "sync"]
+    pub trait Sync {}
+    impl<T> Sync for T {}
+
     #[lang = "sized"]
     pub trait Sized {}
 
index d03b2f8375c0663cda7925337f88fe3eeed3b648..3f3133fafc731a7a4e341f215759f99bf146ad9e 100644 (file)
 #![cfg_attr(all(windows, target_arch = "x86", target_env = "msvc"), doc(
     html_root_url = "https://doc.rust-lang.org/libc/i686-pc-windows-msvc"
 ))]
-#![cfg_attr(all(target_os = "android"), doc(
+#![cfg_attr(target_os = "android", doc(
     html_root_url = "https://doc.rust-lang.org/libc/arm-linux-androideabi"
 ))]
+#![cfg_attr(target_os = "freebsd", doc(
+    html_root_url = "https://doc.rust-lang.org/libc/x86_64-unknown-freebsd"
+))]
+#![cfg_attr(target_os = "openbsd", doc(
+    html_root_url = "https://doc.rust-lang.org/libc/x86_64-unknown-openbsd"
+))]
+#![cfg_attr(target_os = "bitrig", doc(
+    html_root_url = "https://doc.rust-lang.org/libc/x86_64-unknown-bitrig"
+))]
+#![cfg_attr(target_os = "netbsd", doc(
+    html_root_url = "https://doc.rust-lang.org/libc/x86_64-unknown-netbsd"
+))]
+#![cfg_attr(target_os = "dragonfly", doc(
+    html_root_url = "https://doc.rust-lang.org/libc/x86_64-unknown-dragonfly"
+))]
 
 // Attributes needed when building as part of the standard library
 #![cfg_attr(stdbuild, feature(no_std, core, core_slice_ext, staged_api, custom_attribute))]
@@ -172,7 +187,9 @@ extern {
     pub fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int;
     pub fn ftell(stream: *mut FILE) -> c_long;
     pub fn rewind(stream: *mut FILE);
+    #[cfg_attr(target_os = "netbsd", link_name = "__fgetpos50")]
     pub fn fgetpos(stream: *mut FILE, ptr: *mut fpos_t) -> c_int;
+    #[cfg_attr(target_os = "netbsd", link_name = "__fsetpos50")]
     pub fn fsetpos(stream: *mut FILE, ptr: *const fpos_t) -> c_int;
     pub fn feof(stream: *mut FILE) -> c_int;
     pub fn ferror(stream: *mut FILE) -> c_int;
index 7cd8a243c58dd2793021de08d6af2ec0c262fe3c..3c2978ea206cca8e17b57c10dbad8330ce512974 100644 (file)
@@ -35,9 +35,10 @@ macro_rules! __cfg_if_apply {
 }
 
 macro_rules! s {
-    ($(pub struct $i:ident { $($field:tt)* })*) => ($(
+    ($($(#[$attr:meta])* pub struct $i:ident { $($field:tt)* })*) => ($(
         __item! {
             #[repr(C)]
+            $(#[$attr])*
             pub struct $i { $($field)* }
         }
         impl ::dox::Copy for $i {}
index c80986690a7228a5f1f8b65b751022a125706371..406acd10119b3f34404118433f4e0b8b3726201f 100644 (file)
@@ -14,6 +14,10 @@ pub type rlim_t = u64;
 pub type mach_timebase_info_data_t = mach_timebase_info;
 pub type pthread_key_t = c_ulong;
 pub type sigset_t = u32;
+pub type fsblkcnt_t = ::c_uint;
+pub type fsfilcnt_t = ::c_uint;
+pub type speed_t = ::c_ulong;
+pub type tcflag_t = ::c_ulong;
 
 pub enum timezone {}
 
@@ -161,6 +165,83 @@ s! {
         pub f_flag: ::c_ulong,
         pub f_namemax: ::c_ulong,
     }
+
+    pub struct Dl_info {
+        pub dli_fname: *const ::c_char,
+        pub dli_fbase: *mut ::c_void,
+        pub dli_sname: *const ::c_char,
+        pub dli_saddr: *mut ::c_void,
+    }
+
+    pub struct sockaddr_in {
+        pub sin_len: u8,
+        pub sin_family: ::sa_family_t,
+        pub sin_port: ::in_port_t,
+        pub sin_addr: ::in_addr,
+        pub sin_zero: [::c_char; 8],
+    }
+
+    pub struct statfs {
+        pub f_bsize: ::uint32_t,
+        pub f_iosize: ::int32_t,
+        pub f_blocks: ::uint64_t,
+        pub f_bfree: ::uint64_t,
+        pub f_bavail: ::uint64_t,
+        pub f_files: ::uint64_t,
+        pub f_ffree: ::uint64_t,
+        pub f_fsid: ::fsid_t,
+        pub f_owner: ::uid_t,
+        pub f_type: ::uint32_t,
+        pub f_flags: ::uint32_t,
+        pub f_fssubtype: ::uint32_t,
+        pub f_fstypename: [::c_char; 16],
+        pub f_mntonname: [::c_char; 1024],
+        pub f_mntfromname: [::c_char; 1024],
+        pub f_reserved: [::uint32_t; 8],
+    }
+
+    // FIXME: this should have align 4 but it's got align 8 on 64-bit
+    pub struct kevent {
+        pub ident: ::uintptr_t,
+        pub filter: ::int16_t,
+        pub flags: ::uint16_t,
+        pub fflags: ::uint32_t,
+        pub data: ::intptr_t,
+        pub udata: *mut ::c_void,
+    }
+
+    pub struct kevent64_s {
+        pub ident: ::uint64_t,
+        pub filter: ::int16_t,
+        pub flags: ::uint16_t,
+        pub fflags: ::uint32_t,
+        pub data: ::int64_t,
+        pub udata: ::uint64_t,
+        pub ext: [::uint64_t; 2],
+    }
+
+    pub struct dqblk {
+        pub dqb_bhardlimit: ::uint64_t,
+        pub dqb_bsoftlimit: ::uint64_t,
+        pub dqb_curbytes: ::uint64_t,
+        pub dqb_ihardlimit: ::uint32_t,
+        pub dqb_isoftlimit: ::uint32_t,
+        pub dqb_curinodes: ::uint32_t,
+        pub dqb_btime: ::uint32_t,
+        pub dqb_itime: ::uint32_t,
+        pub dqb_id: ::uint32_t,
+        pub dqb_spare: [::uint32_t; 4],
+    }
+
+    pub struct termios {
+        pub c_iflag: ::tcflag_t,
+        pub c_oflag: ::tcflag_t,
+        pub c_cflag: ::tcflag_t,
+        pub c_lflag: ::tcflag_t,
+        pub c_cc: [::cc_t; ::NCCS],
+        pub c_ispeed: ::speed_t,
+        pub c_ospeed: ::speed_t,
+    }
 }
 
 pub const EXIT_FAILURE: ::c_int = 1;
@@ -189,6 +270,7 @@ pub const O_EXCL: ::c_int = 2048;
 pub const O_NOCTTY: ::c_int = 131072;
 pub const O_TRUNC: ::c_int = 1024;
 pub const O_CLOEXEC: ::c_int = 0x1000000;
+pub const O_DIRECTORY: ::c_int = 0x100000;
 pub const S_IFIFO: mode_t = 4096;
 pub const S_IFCHR: mode_t = 8192;
 pub const S_IFBLK: mode_t = 24576;
@@ -554,8 +636,6 @@ pub const SO_DONTTRUNC: ::c_int = 0x2000;
 pub const SO_WANTMORE: ::c_int = 0x4000;
 pub const SO_WANTOOBFLAG: ::c_int = 0x8000;
 
-pub const PATH_MAX: ::c_int = 1024;
-
 pub const _SC_ARG_MAX: ::c_int = 1;
 pub const _SC_CHILD_MAX: ::c_int = 2;
 pub const _SC_CLK_TCK: ::c_int = 3;
@@ -676,7 +756,75 @@ pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
 
 pub const SIGSTKSZ: ::size_t = 131072;
 
+pub const FD_SETSIZE: usize = 1024;
+
+pub const ST_NOSUID: ::c_ulong = 2;
+
+pub const HW_AVAILCPU: ::c_int = 25;
+
+pub const EVFILT_AIO: ::int16_t = 0xfffd;
+pub const EVFILT_PROC: ::int16_t = 0xfffb;
+pub const EVFILT_READ: ::int16_t = 0xffff;
+pub const EVFILT_SIGNAL: ::int16_t = 0xfffa;
+pub const EVFILT_SYSCOUNT: ::int16_t = 0xe;
+pub const EVFILT_TIMER: ::int16_t = 0xfff9;
+pub const EVFILT_VNODE: ::int16_t = 0xfffc;
+pub const EVFILT_WRITE: ::int16_t = 0xfffe;
+pub const EVFILT_FS: ::int16_t = 0xfff7;
+pub const EVFILT_MACHPORT: ::int16_t = 0xfff8;
+pub const EVFILT_USER: ::int16_t = 0xfff6;
+pub const EVFILT_VM: ::int16_t = 0xfff4;
+
+pub const EV_DISPATCH: ::uint16_t = 0x80;
+pub const EV_FLAG0: ::uint16_t = 0x1000;
+pub const EV_OOBAND: ::uint16_t = 0x2000;
+pub const EV_POLL: ::uint16_t = 0x1000;
+pub const EV_RECEIPT: ::uint16_t = 0x40;
+
+pub const NOTE_ABSOLUTE: ::uint32_t = 0x8;
+pub const NOTE_EXITSTATUS: ::uint32_t = 0x04000000;
+pub const NOTE_EXIT_REPARENTED: ::uint32_t = 0x00080000;
+pub const NOTE_FFAND: ::uint32_t = 0x40000000;
+pub const NOTE_FFCOPY: ::uint32_t = 0xc0000000;
+pub const NOTE_FFCTRLMASK: ::uint32_t = 0xc0000000;
+pub const NOTE_FFLAGSMASK: ::uint32_t = 0x00ffffff;
+pub const NOTE_FFNOP: ::uint32_t = 0x0;
+pub const NOTE_FFOR: ::uint32_t = 0x80000000;
+pub const NOTE_NONE: ::uint32_t = 0x80;
+pub const NOTE_NSECONDS: ::uint32_t = 0x4;
+pub const NOTE_REAP: ::uint32_t = 0x10000000;
+pub const NOTE_SECONDS: ::uint32_t = 0x1;
+pub const NOTE_SIGNAL: ::uint32_t = 0x8000000;
+pub const NOTE_TRIGGER: ::uint32_t = 0x01000000;
+pub const NOTE_USECONDS: ::uint32_t = 0x2;
+pub const NOTE_VM_ERROR: ::uint32_t = 0x10000000;
+pub const NOTE_VM_PRESSURE: ::uint32_t = 0x80000000;
+pub const NOTE_VM_PRESSURE_SUDDEN_TERMINATE: ::uint32_t = 0x20000000;
+pub const NOTE_VM_PRESSURE_TERMINATE: ::uint32_t = 0x40000000;
+pub const NOTE_PCTRLMASK: ::uint32_t = 0xfff00000;
+
+pub const TAB3: ::c_int = 0x00000004;
+pub const VT0: ::c_int  = 0x00000000;
+pub const VT1: ::c_int  = 0x00010000;
+pub const IUTF8: ::tcflag_t = 0x00004000;
+pub const CRTSCTS: ::tcflag_t = 0x00030000;
+
+pub const NI_MAXHOST: ::socklen_t = 1025;
+
 extern {
+    pub fn getnameinfo(sa: *const ::sockaddr,
+                       salen: ::socklen_t,
+                       host: *mut ::c_char,
+                       hostlen: ::socklen_t,
+                       serv: *mut ::c_char,
+                       sevlen: ::socklen_t,
+                       flags: ::c_int) -> ::c_int;
+    pub fn mincore(addr: *const ::c_void, len: ::size_t,
+                   vec: *mut ::c_char) -> ::c_int;
+    pub fn sysctlnametomib(name: *const ::c_char,
+                           mibp: *mut ::c_int,
+                           sizep: *mut ::size_t)
+                           -> ::c_int;
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
                link_name = "mprotect$UNIX2003")]
     pub fn mprotect(addr: *mut ::c_void, len: ::size_t, prot: ::c_int)
@@ -703,6 +851,36 @@ extern {
     pub fn __error() -> *mut ::c_int;
     pub fn backtrace(buf: *mut *mut ::c_void,
                      sz: ::c_int) -> ::c_int;
+    #[cfg_attr(target_os = "macos", link_name = "statfs$INODE64")]
+    pub fn statfs(path: *const ::c_char, buf: *mut statfs) -> ::c_int;
+    #[cfg_attr(target_os = "macos", link_name = "fstatfs$INODE64")]
+    pub fn fstatfs(fd: ::c_int, buf: *mut statfs) -> ::c_int;
+    pub fn kevent(kq: ::c_int,
+                  changelist: *const ::kevent,
+                  nchanges: ::c_int,
+                  eventlist: *mut ::kevent,
+                  nevents: ::c_int,
+                  timeout: *const ::timespec) -> ::c_int;
+    pub fn kevent64(kq: ::c_int,
+                    changelist: *const ::kevent64_s,
+                    nchanges: ::c_int,
+                    eventlist: *mut ::kevent64_s,
+                    nevents: ::c_int,
+                    flags: ::c_uint,
+                    timeout: *const ::timespec) -> ::c_int;
+    pub fn mount(src: *const ::c_char,
+                 target: *const ::c_char,
+                 flags: ::c_int,
+                 data: *mut ::c_void) -> ::c_int;
+    pub fn ptrace(requeset: ::c_int,
+                  pid: ::pid_t,
+                  addr: *mut ::c_char,
+                  data: ::c_int) -> ::c_int;
+    pub fn quotactl(special: *const ::c_char,
+                    cmd: ::c_int,
+                    id: ::c_int,
+                    data: *mut ::c_char) -> ::c_int;
+    pub fn sethostname(name: *const ::c_char, len: ::c_int) -> ::c_int;
 }
 
 cfg_if! {
index 91fc85b45878d9767b47d85d554ae1355458d6e6..6b874f0b1f67d8648cf9eee725ba7be97d4517c4 100644 (file)
@@ -1,6 +1,7 @@
 pub const PTHREAD_STACK_MIN: ::size_t = 1024;
 pub const KERN_PROC_PATHNAME: ::c_int = 9;
-pub const SIGSTKSZ: ::size_t = 8192 /* MINSIGSTKSZ */ + 32768;
+pub const SIGSTKSZ: ::size_t = 40960;
+pub const MADV_INVAL: ::c_int = 10;
 
 extern {
     pub fn __dfly_error() -> *const ::c_int;
index 0f1aa0d36b113ba587af8d565e5fd4331eb8413e..9e6d985f9d7c06dee99263f1a539f45f3438184b 100644 (file)
@@ -1,6 +1,6 @@
 pub const PTHREAD_STACK_MIN: ::size_t = 2048;
 pub const KERN_PROC_PATHNAME: ::c_int = 12;
-pub const SIGSTKSZ: ::size_t = 2048 /* MINSIGSTKSZ */ + 32768;
+pub const SIGSTKSZ: ::size_t = 34816;
 
 extern {
     pub fn __error() -> *mut ::c_int;
index 4826357a7f8fcdcf94dcfc20639f4c4679b48e88..d41829ceb20eafca481fc9339bf1261463f229ed 100644 (file)
@@ -12,6 +12,10 @@ pub type pthread_mutexattr_t = *mut ::c_void;
 pub type pthread_cond_t = *mut ::c_void;
 pub type pthread_rwlock_t = *mut ::c_void;
 pub type pthread_key_t = ::c_int;
+pub type fsblkcnt_t = ::c_uint;
+pub type fsfilcnt_t = ::c_uint;
+pub type tcflag_t = ::c_uint;
+pub type speed_t = ::c_uint;
 
 pub enum timezone {}
 
@@ -99,6 +103,35 @@ s! {
         pub f_fsid: ::c_ulong,
         pub f_namemax: ::c_ulong,
     }
+
+    pub struct sched_param {
+        pub sched_priority: ::c_int,
+    }
+
+    pub struct Dl_info {
+        pub dli_fname: *const ::c_char,
+        pub dli_fbase: *mut ::c_void,
+        pub dli_sname: *const ::c_char,
+        pub dli_saddr: *mut ::c_void,
+    }
+
+    pub struct sockaddr_in {
+        pub sin_len: u8,
+        pub sin_family: ::sa_family_t,
+        pub sin_port: ::in_port_t,
+        pub sin_addr: ::in_addr,
+        pub sin_zero: [::c_char; 8],
+    }
+
+    pub struct termios {
+        pub c_iflag: ::tcflag_t,
+        pub c_oflag: ::tcflag_t,
+        pub c_cflag: ::tcflag_t,
+        pub c_lflag: ::tcflag_t,
+        pub c_cc: [::cc_t; ::NCCS],
+        pub c_ispeed: ::speed_t,
+        pub c_ospeed: ::speed_t,
+    }
 }
 
 pub const EXIT_FAILURE: ::c_int = 1;
@@ -514,7 +547,32 @@ 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_RECURSIVE: ::c_int = 2;
 
+pub const SCHED_FIFO: ::c_int = 1;
+pub const SCHED_OTHER: ::c_int = 2;
+pub const SCHED_RR: ::c_int = 3;
+
+pub const FD_SETSIZE: usize = 1024;
+
+pub const ST_NOSUID: ::c_ulong = 2;
+
+pub const HW_AVAILCPU: ::c_int = 25;
+
+pub const NI_MAXHOST: ::size_t = 1025;
+
 extern {
+    pub fn getnameinfo(sa: *const ::sockaddr,
+                       salen: ::socklen_t,
+                       host: *mut ::c_char,
+                       hostlen: ::size_t,
+                       serv: *mut ::c_char,
+                       servlen: ::size_t,
+                       flags: ::c_int) -> ::c_int;
+    pub fn mincore(addr: *const ::c_void, len: ::size_t,
+                   vec: *mut ::c_char) -> ::c_int;
+    pub fn sysctlnametomib(name: *const ::c_char,
+                           mibp: *mut ::c_int,
+                           sizep: *mut ::size_t)
+                           -> ::c_int;
     pub fn mprotect(addr: *const ::c_void, len: ::size_t, prot: ::c_int)
                     -> ::c_int;
     pub fn shm_open(name: *const ::c_char, oflag: ::c_int, mode: ::mode_t)
@@ -536,6 +594,9 @@ extern {
     pub fn pthread_set_name_np(tid: ::pthread_t, name: *const ::c_char);
     pub fn posix_fallocate(fd: ::c_int, offset: ::off_t,
                            len: ::off_t) -> ::c_int;
+    pub fn sched_setscheduler(pid: ::pid_t, policy: ::c_int, param: *const sched_param) -> ::c_int;
+    pub fn sched_getscheduler(pid: ::pid_t) -> ::c_int;
+    pub fn memrchr(cx: *const ::c_void, c: ::c_int, n: ::size_t) -> *mut ::c_void;
 }
 
 cfg_if! {
index 87c7ec053a0c661172ca08e531dba7418088580a..14afc34708b5b8ac8d10b9711183ae6a5f23aa47 100644 (file)
@@ -6,8 +6,7 @@ pub type blkcnt_t = i64;
 pub type socklen_t = u32;
 pub type sa_family_t = u8;
 pub type pthread_t = ::uintptr_t;
-pub type fsblkcnt_t = ::c_uint;
-pub type fsfilcnt_t = ::c_uint;
+pub type nfds_t = ::c_uint;
 
 s! {
     pub struct sockaddr {
@@ -16,14 +15,6 @@ s! {
         pub sa_data: [::c_char; 14],
     }
 
-    pub struct sockaddr_in {
-        pub sin_len: u8,
-        pub sin_family: sa_family_t,
-        pub sin_port: ::in_port_t,
-        pub sin_addr: ::in_addr,
-        pub sin_zero: [::c_char; 8],
-    }
-
     pub struct sockaddr_in6 {
         pub sin6_len: u8,
         pub sin6_family: sa_family_t,
@@ -51,7 +42,10 @@ s! {
         pub pw_shell: *mut ::c_char,
         pub pw_expire: ::time_t,
 
-        #[cfg(not(any(target_os = "macos", target_os = "ios")))]
+        #[cfg(not(any(target_os = "macos",
+                      target_os = "ios",
+                      target_os = "netbsd",
+                      target_os = "openbsd")))]
         pub pw_fields: ::c_int,
     }
 
@@ -82,11 +76,43 @@ s! {
         pub tm_gmtoff: ::c_long,
         pub tm_zone: *mut ::c_char,
     }
+
+    pub struct utsname {
+        pub sysname: [::c_char; 256],
+        pub nodename: [::c_char; 256],
+        pub release: [::c_char; 256],
+        pub version: [::c_char; 256],
+        pub machine: [::c_char; 256],
+    }
+
+    pub struct msghdr {
+        pub msg_name: *mut ::c_void,
+        pub msg_namelen: ::socklen_t,
+        pub msg_iov: *mut ::iovec,
+        pub msg_iovlen: ::c_int,
+        pub msg_control: *mut ::c_void,
+        pub msg_controllen: ::socklen_t,
+        pub msg_flags: ::c_int,
+    }
+
+    pub struct flock {
+        pub l_start: ::off_t,
+        pub l_len: ::off_t,
+        pub l_pid: ::pid_t,
+        pub l_type: ::c_short,
+        pub l_whence: ::c_short,
+    }
+
+    pub struct fsid_t {
+        __fsid_val: [::int32_t; 2],
+    }
 }
 
 pub const FIOCLEX: ::c_ulong = 0x20006601;
 pub const FIONBIO: ::c_ulong = 0x8004667e;
 
+pub const PATH_MAX: ::c_int = 1024;
+
 pub const SA_ONSTACK: ::c_int = 0x0001;
 pub const SA_SIGINFO: ::c_int = 0x0040;
 pub const SA_RESTART: ::c_int = 0x0002;
@@ -97,17 +123,162 @@ pub const SA_NOCLDWAIT: ::c_int = 0x0020;
 
 pub const SIGCHLD: ::c_int = 20;
 pub const SIGBUS: ::c_int = 10;
+pub const SIGUSR1: ::c_int = 30;
+pub const SIGUSR2: ::c_int = 31;
+pub const SIGCONT: ::c_int = 19;
+pub const SIGSTOP: ::c_int = 17;
+pub const SIGTSTP: ::c_int = 18;
+pub const SIGURG: ::c_int = 16;
+pub const SIGIO: ::c_int = 23;
+pub const SIGSYS: ::c_int = 12;
+pub const SIGTTIN: ::c_int = 21;
+pub const SIGTTOU: ::c_int = 22;
+pub const SIGXCPU: ::c_int = 24;
+pub const SIGXFSZ: ::c_int = 25;
+pub const SIGVTALRM: ::c_int = 26;
+pub const SIGPROF: ::c_int = 27;
+pub const SIGWINCH: ::c_int = 28;
+
 pub const SIG_SETMASK: ::c_int = 3;
+pub const SIG_BLOCK: ::c_int = 0x1;
+pub const SIG_UNBLOCK: ::c_int = 0x2;
 
 pub const IPV6_MULTICAST_LOOP: ::c_int = 11;
 pub const IPV6_V6ONLY: ::c_int = 27;
 
-pub const FD_SETSIZE: usize = 1024;
-
 pub const ST_RDONLY: ::c_ulong = 1;
-pub const ST_NOSUID: ::c_ulong = 2;
 
-pub const NI_MAXHOST: ::socklen_t = 1025;
+pub const CTL_HW: ::c_int = 6;
+pub const HW_NCPU: ::c_int = 3;
+
+pub const EV_ADD: ::uint16_t = 0x1;
+pub const EV_CLEAR: ::uint16_t = 0x20;
+pub const EV_DELETE: ::uint16_t = 0x2;
+pub const EV_DISABLE: ::uint16_t = 0x8;
+pub const EV_ENABLE: ::uint16_t = 0x4;
+pub const EV_EOF: ::uint16_t = 0x8000;
+pub const EV_ERROR: ::uint16_t = 0x4000;
+pub const EV_FLAG1: ::uint16_t = 0x2000;
+pub const EV_ONESHOT: ::uint16_t = 0x10;
+pub const EV_SYSFLAGS: ::uint16_t = 0xf000;
+
+pub const NOTE_ATTRIB: ::uint32_t = 0x8;
+pub const NOTE_CHILD: ::uint32_t = 0x4;
+pub const NOTE_DELETE: ::uint32_t = 0x1;
+pub const NOTE_EXEC: ::uint32_t = 0x20000000;
+pub const NOTE_EXIT: ::uint32_t = 0x80000000;
+pub const NOTE_EXTEND: ::uint32_t = 0x4;
+pub const NOTE_FORK: ::uint32_t = 0x40000000;
+pub const NOTE_LINK: ::uint32_t = 0x10;
+pub const NOTE_LOWAT: ::uint32_t = 0x1;
+pub const NOTE_PDATAMASK: ::uint32_t = 0x000fffff;
+pub const NOTE_RENAME: ::uint32_t = 0x20;
+pub const NOTE_REVOKE: ::uint32_t = 0x40;
+pub const NOTE_TRACK: ::uint32_t = 0x1;
+pub const NOTE_TRACKERR: ::uint32_t = 0x2;
+pub const NOTE_WRITE: ::uint32_t = 0x2;
+
+pub const NCCS: usize = 20;
+
+pub const O_ASYNC: ::c_int = 0x40;
+pub const O_FSYNC: ::c_int = 0x80;
+pub const O_NDELAY: ::c_int = 0x4;
+pub const O_NOFOLLOW: ::c_int = 0x100;
+
+pub const F_GETLK: ::c_int = 7;
+pub const F_GETOWN: ::c_int = 5;
+pub const F_SETLK: ::c_int = 8;
+pub const F_SETLKW: ::c_int = 9;
+pub const F_SETOWN: ::c_int = 6;
+
+pub const MNT_FORCE: ::c_int = 0x80000;
+
+pub const Q_SYNC: ::c_int = 0x600;
+pub const Q_QUOTAON: ::c_int = 0x100;
+pub const Q_QUOTAOFF: ::c_int = 0x200;
+pub const Q_GETQUOTA: ::c_int = 0x300;
+pub const Q_SETQUOTA: ::c_int = 0x400;
+
+pub const TCIOFF: ::c_int = 3;
+pub const TCION: ::c_int = 4;
+pub const TCOOFF: ::c_int = 1;
+pub const TCOON: ::c_int = 2;
+pub const TCIFLUSH: ::c_int = 1;
+pub const TCOFLUSH: ::c_int = 2;
+pub const TCIOFLUSH: ::c_int = 3;
+pub const TCSANOW: ::c_int = 0;
+pub const TCSADRAIN: ::c_int = 1;
+pub const TCSAFLUSH: ::c_int = 2;
+pub const NL0: ::c_int  = 0x00000000;
+pub const NL1: ::c_int  = 0x00000100;
+pub const TAB0: ::c_int = 0x00000000;
+pub const TAB1: ::c_int = 0x00000400;
+pub const TAB2: ::c_int = 0x00000800;
+pub const CR0: ::c_int  = 0x00000000;
+pub const CR1: ::c_int  = 0x00001000;
+pub const CR2: ::c_int  = 0x00002000;
+pub const CR3: ::c_int  = 0x00003000;
+pub const FF0: ::c_int  = 0x00000000;
+pub const FF1: ::c_int  = 0x00004000;
+pub const BS0: ::c_int  = 0x00000000;
+pub const BS1: ::c_int  = 0x00008000;
+pub const VEOF: usize = 0;
+pub const VEOL: usize = 1;
+pub const VEOL2: usize = 2;
+pub const VERASE: usize = 3;
+pub const VWERASE: usize = 4;
+pub const VKILL: usize = 5;
+pub const VREPRINT: usize = 6;
+pub const VINTR: usize = 8;
+pub const VQUIT: usize = 9;
+pub const VSUSP: usize = 10;
+pub const VSTART: usize = 12;
+pub const VSTOP: usize = 13;
+pub const VLNEXT: usize = 14;
+pub const VDISCARD: usize = 15;
+pub const VMIN: usize = 16;
+pub const VTIME: usize = 17;
+pub const IGNBRK: ::tcflag_t = 0x00000001;
+pub const BRKINT: ::tcflag_t = 0x00000002;
+pub const IGNPAR: ::tcflag_t = 0x00000004;
+pub const PARMRK: ::tcflag_t = 0x00000008;
+pub const INPCK: ::tcflag_t = 0x00000010;
+pub const ISTRIP: ::tcflag_t = 0x00000020;
+pub const INLCR: ::tcflag_t = 0x00000040;
+pub const IGNCR: ::tcflag_t = 0x00000080;
+pub const ICRNL: ::tcflag_t = 0x00000100;
+pub const IXON: ::tcflag_t = 0x00000200;
+pub const IXOFF: ::tcflag_t = 0x00000400;
+pub const IXANY: ::tcflag_t = 0x00000800;
+pub const IMAXBEL: ::tcflag_t = 0x00002000;
+pub const OPOST: ::tcflag_t = 0x1;
+pub const ONLCR: ::tcflag_t = 0x2;
+pub const CSIZE: ::tcflag_t = 0x00000300;
+pub const CS5: ::tcflag_t = 0x00000000;
+pub const CS6: ::tcflag_t = 0x00000100;
+pub const CS7: ::tcflag_t = 0x00000200;
+pub const CS8: ::tcflag_t = 0x00000300;
+pub const CSTOPB: ::tcflag_t = 0x00000400;
+pub const CREAD: ::tcflag_t = 0x00000800;
+pub const PARENB: ::tcflag_t = 0x00001000;
+pub const PARODD: ::tcflag_t = 0x00002000;
+pub const HUPCL: ::tcflag_t = 0x00004000;
+pub const CLOCAL: ::tcflag_t = 0x00008000;
+pub const ECHOKE: ::tcflag_t = 0x00000001;
+pub const ECHOE: ::tcflag_t = 0x00000002;
+pub const ECHOK: ::tcflag_t = 0x00000004;
+pub const ECHO: ::tcflag_t = 0x00000008;
+pub const ECHONL: ::tcflag_t = 0x00000010;
+pub const ECHOPRT: ::tcflag_t = 0x00000020;
+pub const ECHOCTL: ::tcflag_t = 0x00000040;
+pub const ISIG: ::tcflag_t = 0x00000080;
+pub const ICANON: ::tcflag_t = 0x00000100;
+pub const IEXTEN: ::tcflag_t = 0x00000400;
+pub const EXTPROC: ::tcflag_t = 0x00000800;
+pub const TOSTOP: ::tcflag_t = 0x00400000;
+pub const FLUSHO: ::tcflag_t = 0x00800000;
+pub const PENDIN: ::tcflag_t = 0x20000000;
+pub const NOFLSH: ::tcflag_t = 0x80000000;
 
 f! {
     pub fn FD_CLR(fd: ::c_int, set: *mut fd_set) -> () {
@@ -147,22 +318,12 @@ f! {
 }
 
 extern {
-    pub fn mincore(addr: *const ::c_void, len: ::size_t,
-                   vec: *mut c_char) -> ::c_int;
-    pub fn sysctlnametomib(name: *const c_char,
-                           mibp: *mut ::c_int,
-                           sizep: *mut ::size_t)
-                           -> ::c_int;
     pub fn setgroups(ngroups: ::c_int,
                      ptr: *const ::gid_t) -> ::c_int;
     pub fn ioctl(fd: ::c_int, request: ::c_ulong, ...) -> ::c_int;
-    pub fn getnameinfo(sa: *const ::sockaddr,
-                       salen: ::socklen_t,
-                       host: *mut ::c_char,
-                       hostlen: ::socklen_t,
-                       serv: *mut ::c_char,
-                       sevlen: ::socklen_t,
-                       flags: ::c_int) -> ::c_int;
+    pub fn kqueue() -> ::c_int;
+    pub fn unmount(target: *const ::c_char, arg: ::c_int) -> ::c_int;
+    pub fn syscall(num: ::c_int, ...) -> ::c_int;
 }
 
 cfg_if! {
index 7c9ba8263e09da51df8b05d5ff18f7b816a53119..ecb0bfdab93ea7a1868cd6ee6a85026c49d9ce65 100644 (file)
@@ -1,4 +1,27 @@
+pub type clock_t = i64;
+pub type suseconds_t = i64;
+pub type dev_t = i32;
+pub type sigset_t = ::c_uint;
+pub type blksize_t = ::uint32_t;
+pub type fsblkcnt_t = ::c_uint;
+pub type fsfilcnt_t = ::c_uint;
+pub type pthread_attr_t = *mut ::c_void;
+pub type pthread_mutex_t = *mut ::c_void;
+pub type pthread_mutexattr_t = *mut ::c_void;
+pub type pthread_cond_t = *mut ::c_void;
+pub type pthread_rwlock_t = *mut ::c_void;
+
 s! {
+    pub struct dirent {
+        pub d_fileno: ::ino_t,
+        pub d_off: ::off_t,
+        pub d_reclen: u16,
+        pub d_type: u8,
+        pub d_namelen: u8,
+        __d_padding: [u8; 4],
+        pub d_name: [::c_char; 256],
+    }
+
     pub struct glob_t {
         pub gl_pathc:  ::c_int,
         pub gl_matchc: ::c_int,
@@ -13,8 +36,210 @@ s! {
         __unused6: *mut ::c_void,
         __unused7: *mut ::c_void,
     }
+
+    pub struct stat {
+        pub st_mode: ::mode_t,
+        pub st_dev: ::dev_t,
+        pub st_ino: ::ino_t,
+        pub st_nlink: ::nlink_t,
+        pub st_uid: ::uid_t,
+        pub st_gid: ::gid_t,
+        pub st_rdev: ::dev_t,
+        pub st_atime: ::time_t,
+        pub st_atime_nsec: ::c_long,
+        pub st_mtime: ::time_t,
+        pub st_mtime_nsec: ::c_long,
+        pub st_ctime: ::time_t,
+        pub st_ctime_nsec: ::c_long,
+        pub st_size: ::off_t,
+        pub st_blocks: ::blkcnt_t,
+        pub st_blksize: ::blksize_t,
+        pub st_flags: ::uint32_t,
+        pub st_gen: ::uint32_t,
+        pub st_birthtime: ::time_t,
+        pub st_birthtime_nsec: ::c_long,
+    }
+
+    pub struct statvfs {
+        pub f_bsize: ::c_ulong,
+        pub f_frsize: ::c_ulong,
+        pub f_blocks: ::fsblkcnt_t,
+        pub f_bfree: ::fsblkcnt_t,
+        pub f_bavail: ::fsblkcnt_t,
+        pub f_files: ::fsfilcnt_t,
+        pub f_ffree: ::fsfilcnt_t,
+        pub f_favail: ::fsfilcnt_t,
+        pub f_fsid: ::c_ulong,
+        pub f_flag: ::c_ulong,
+        pub f_namemax: ::c_ulong,
+    }
+
+    pub struct addrinfo {
+        pub ai_flags: ::c_int,
+        pub ai_family: ::c_int,
+        pub ai_socktype: ::c_int,
+        pub ai_protocol: ::c_int,
+        pub ai_addrlen: ::socklen_t,
+        pub ai_addr: *mut ::sockaddr,
+        pub ai_canonname: *mut ::c_char,
+        pub ai_next: *mut ::addrinfo,
+    }
+
+    pub struct sockaddr_storage {
+        pub ss_len: u8,
+        pub ss_family: ::sa_family_t,
+        __ss_pad1: [u8; 6],
+        __ss_pad2: i64,
+        __ss_pad3: [u8; 240],
+    }
+
+    pub struct siginfo_t {
+        pub si_signo: ::c_int,
+        pub si_code: ::c_int,
+        pub si_errno: ::c_int,
+        pub si_addr: *mut ::c_void
+    }
+
+    pub struct Dl_info {
+        pub dli_fname: *const ::c_char,
+        pub dli_fbase: *mut ::c_void,
+        pub dli_sname: *const ::c_char,
+        pub dli_saddr: *mut ::c_void,
+    }
 }
 
+pub const O_CLOEXEC: ::c_int = 0x10000;
+
+pub const MS_SYNC : ::c_int = 0x0002;
+pub const MS_INVALIDATE : ::c_int = 0x0004;
+
+pub const PTHREAD_STACK_MIN : ::size_t = 2048;
+
+pub const ENOATTR : ::c_int = 83;
+pub const EILSEQ : ::c_int = 84;
+pub const EOVERFLOW : ::c_int = 87;
+pub const ECANCELED : ::c_int = 88;
+pub const EIDRM : ::c_int = 89;
+pub const ENOMSG : ::c_int = 90;
+pub const ENOTSUP : ::c_int = 91;
+pub const ELAST : ::c_int = 91;
+
+pub const F_DUPFD_CLOEXEC : ::c_int = 10;
+
+pub const RLIM_NLIMITS: ::c_int = 9;
+
+pub const SO_SNDTIMEO: ::c_int = 0x1005;
+pub const SO_RCVTIMEO: ::c_int = 0x1006;
+
+pub const KERN_PROC : ::c_int = 66;
+pub const O_DSYNC : ::c_int = 128;
+
+pub const MAP_RENAME : ::c_int = 0x0000;
+pub const MAP_NORESERVE : ::c_int = 0x0000;
+pub const MAP_HASSEMAPHORE : ::c_int = 0x0000;
+
+pub const EIPSEC : ::c_int = 82;
+pub const ENOMEDIUM : ::c_int = 85;
+pub const EMEDIUMTYPE : ::c_int = 86;
+
+pub const RUSAGE_THREAD: ::c_int = 1;
+
+pub const IPV6_ADD_MEMBERSHIP: ::c_int = 12;
+pub const IPV6_DROP_MEMBERSHIP: ::c_int = 13;
+
+pub const MAP_COPY : ::c_int = 0x0002;
+pub const MAP_NOEXTEND : ::c_int = 0x0000;
+
+pub const _SC_IOV_MAX : ::c_int = 51;
+pub const _SC_GETGR_R_SIZE_MAX : ::c_int = 100;
+pub const _SC_GETPW_R_SIZE_MAX : ::c_int = 101;
+pub const _SC_LOGIN_NAME_MAX : ::c_int = 102;
+pub const _SC_MQ_PRIO_MAX : ::c_int = 59;
+pub const _SC_THREADS : ::c_int = 91;
+pub const _SC_THREAD_ATTR_STACKADDR : ::c_int = 77;
+pub const _SC_THREAD_ATTR_STACKSIZE : ::c_int = 78;
+pub const _SC_THREAD_DESTRUCTOR_ITERATIONS : ::c_int = 80;
+pub const _SC_THREAD_KEYS_MAX : ::c_int = 81;
+pub const _SC_THREAD_PRIO_INHERIT : ::c_int = 82;
+pub const _SC_THREAD_PRIO_PROTECT : ::c_int = 83;
+pub const _SC_THREAD_PRIORITY_SCHEDULING : ::c_int = 84;
+pub const _SC_THREAD_PROCESS_SHARED : ::c_int = 85;
+pub const _SC_THREAD_SAFE_FUNCTIONS : ::c_int = 103;
+pub const _SC_THREAD_STACK_MIN : ::c_int = 89;
+pub const _SC_THREAD_THREADS_MAX : ::c_int = 90;
+pub const _SC_TTY_NAME_MAX : ::c_int = 107;
+pub const _SC_ATEXIT_MAX : ::c_int = 46;
+pub const _SC_CLK_TCK : ::c_int = 3;
+pub const _SC_AIO_LISTIO_MAX : ::c_int = 42;
+pub const _SC_AIO_MAX : ::c_int = 43;
+pub const _SC_ASYNCHRONOUS_IO : ::c_int = 45;
+pub const _SC_MAPPED_FILES : ::c_int = 53;
+pub const _SC_MEMLOCK : ::c_int = 54;
+pub const _SC_MEMLOCK_RANGE : ::c_int = 55;
+pub const _SC_MEMORY_PROTECTION : ::c_int = 56;
+pub const _SC_MESSAGE_PASSING : ::c_int = 57;
+pub const _SC_MQ_OPEN_MAX : ::c_int = 58;
+pub const _SC_PRIORITY_SCHEDULING : ::c_int = 61;
+pub const _SC_SEMAPHORES : ::c_int = 67;
+pub const _SC_SHARED_MEMORY_OBJECTS : ::c_int = 68;
+pub const _SC_SYNCHRONIZED_IO : ::c_int = 75;
+pub const _SC_TIMERS : ::c_int = 94;
+pub const _SC_XOPEN_CRYPT : ::c_int = 117;
+pub const _SC_XOPEN_ENH_I18N : ::c_int = 118;
+pub const _SC_XOPEN_LEGACY : ::c_int = 119;
+pub const _SC_XOPEN_REALTIME : ::c_int = 120;
+pub const _SC_XOPEN_REALTIME_THREADS : ::c_int = 121;
+pub const _SC_XOPEN_UNIX : ::c_int = 123;
+pub const _SC_XOPEN_VERSION : ::c_int = 125;
+pub const _SC_SEM_NSEMS_MAX : ::c_int = 31;
+pub const _SC_SEM_VALUE_MAX : ::c_int = 32;
+pub const _SC_AIO_PRIO_DELTA_MAX : ::c_int = 44;
+pub const _SC_DELAYTIMER_MAX : ::c_int = 50;
+pub const _SC_PRIORITIZED_IO : ::c_int = 60;
+pub const _SC_REALTIME_SIGNALS : ::c_int = 64;
+pub const _SC_RTSIG_MAX : ::c_int = 66;
+pub const _SC_SIGQUEUE_MAX : ::c_int = 70;
+pub const _SC_TIMER_MAX : ::c_int = 93;
+
+pub const SIGSTKSZ: ::size_t = 131072;
+
+pub const FD_SETSIZE: usize = 1024;
+
+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_RECURSIVE: ::c_int = 2;
+
+pub const HW_AVAILCPU: ::c_int = 25;
+pub const KERN_PROC_ARGS: ::c_int = 55;
+
+pub const TMP_MAX : ::c_uint = 0x7fffffff;
+
+pub const NI_MAXHOST: ::size_t = 256;
+
 extern {
-    pub fn pthread_set_name_np(tid: ::pthread_t, name: *const ::c_char);
-}
\ No newline at end of file
+    pub fn getnameinfo(sa: *const ::sockaddr,
+                       salen: ::socklen_t,
+                       host: *mut ::c_char,
+                       hostlen: ::size_t,
+                       serv: *mut ::c_char,
+                       servlen: ::size_t,
+                       flags: ::c_int) -> ::c_int;
+    pub fn mprotect(addr: *const ::c_void, len: ::size_t, prot: ::c_int)
+                    -> ::c_int;
+    pub fn sysctl(name: *mut ::c_int,
+                  namelen: ::c_uint,
+                  oldp: *mut ::c_void,
+                  oldlenp: *mut ::size_t,
+                  newp: *mut ::c_void,
+                  newlen: ::size_t)
+                  -> ::c_int;
+    pub fn sysctlbyname(name: *const ::c_char,
+                        oldp: *mut ::c_void,
+                        oldlenp: *mut ::size_t,
+                        newp: *mut ::c_void,
+                        newlen: ::size_t)
+                        -> ::c_int;
+}
index e61814b1ad0527fa64962ae36e6009980c361a1e..72d681c86d671f11ef06eaf6c7b48351e18bbcb4 100644 (file)
@@ -1,44 +1,17 @@
 pub type c_long = i64;
 pub type c_ulong = u64;
-pub type clock_t = i64;
 pub type time_t = i64;
-pub type suseconds_t = i64;
-pub type dev_t = i32;
 pub type mode_t = u32;
 pub type nlink_t = ::uint32_t;
-pub type blksize_t = ::uint32_t;
 pub type ino_t = ::uint64_t;
-pub type fflags_t = u32;
-pub type pthread_attr_t = *mut ::c_void;
-pub type sigset_t = ::c_uint;
 pub type pthread_key_t = ::c_int;
-pub type pthread_mutex_t = *mut ::c_void;
-pub type pthread_mutexattr_t = *mut ::c_void;
-pub type pthread_cond_t = *mut ::c_void;
-pub type pthread_rwlock_t = *mut ::c_void;
 pub type rlim_t = u64;
-
+pub type speed_t = ::c_uint;
+pub type tcflag_t = ::c_uint;
 
 pub enum timezone {}
 
 s! {
-    pub struct dirent {
-        pub d_fileno: ::ino_t,
-        pub d_off: ::off_t,
-        pub d_reclen: u16,
-        pub d_type: u8,
-        pub d_namelen: u8,
-        __d_padding: [u8; 4],
-        pub d_name: [::c_char; 256],
-    }
-
-    pub struct siginfo_t {
-        pub si_signo: ::c_int,
-        pub si_code: ::c_int,
-        pub si_errno: ::c_int,
-        pub si_addr: *mut ::c_void
-    }
-
     pub struct sigaction {
         pub sa_sigaction: ::sighandler_t,
         pub sa_mask: ::sigset_t,
@@ -51,60 +24,22 @@ s! {
         pub ss_flags: ::c_int,
     }
 
-    pub struct sockaddr_storage {
-        pub ss_len: u8,
-        pub ss_family: ::sa_family_t,
-        __ss_pad1: [u8; 6],
-        __ss_pad2: i64,
-        __ss_pad3: [u8; 240],
-    }
-
-    pub struct addrinfo {
-        pub ai_flags: ::c_int,
-        pub ai_family: ::c_int,
-        pub ai_socktype: ::c_int,
-        pub ai_protocol: ::c_int,
-        pub ai_addrlen: ::socklen_t,
-        pub ai_addr: *mut ::sockaddr,
-        pub ai_canonname: *mut ::c_char,
-        pub ai_next: *mut ::addrinfo,
-    }
-
-    pub struct stat {
-        pub st_mode: ::mode_t,
-        pub st_dev: ::dev_t,
-        pub st_ino: ::ino_t,
-        pub st_nlink: ::nlink_t,
-        pub st_uid: ::uid_t,
-        pub st_gid: ::gid_t,
-        pub st_rdev: ::dev_t,
-        pub st_atime: ::time_t,
-        pub st_atime_nsec: ::c_long,
-        pub st_mtime: ::time_t,
-        pub st_mtime_nsec: ::c_long,
-        pub st_ctime: ::time_t,
-        pub st_ctime_nsec: ::c_long,
-        pub st_size: ::off_t,
-        pub st_blocks: ::blkcnt_t,
-        pub st_blksize: ::blksize_t,
-        pub st_flags: ::fflags_t,
-        pub st_gen: ::uint32_t,
-        pub st_birthtime: ::time_t,
-        pub st_birthtime_nsec: ::c_long,
+    pub struct sockaddr_in {
+        pub sin_len: u8,
+        pub sin_family: ::sa_family_t,
+        pub sin_port: ::in_port_t,
+        pub sin_addr: ::in_addr,
+        pub sin_zero: [::int8_t; 8],
     }
 
-    pub struct statvfs {
-        pub f_bsize: ::c_ulong,
-        pub f_frsize: ::c_ulong,
-        pub f_blocks: ::fsblkcnt_t,
-        pub f_bfree: ::fsblkcnt_t,
-        pub f_bavail: ::fsblkcnt_t,
-        pub f_files: ::fsfilcnt_t,
-        pub f_ffree: ::fsfilcnt_t,
-        pub f_favail: ::fsfilcnt_t,
-        pub f_fsid: ::c_ulong,
-        pub f_flag: ::c_ulong,
-        pub f_namemax: ::c_ulong,
+    pub struct termios {
+        pub c_iflag: ::tcflag_t,
+        pub c_oflag: ::tcflag_t,
+        pub c_cflag: ::tcflag_t,
+        pub c_lflag: ::tcflag_t,
+        pub c_cc: [::cc_t; ::NCCS],
+        pub c_ispeed: ::c_int,
+        pub c_ospeed: ::c_int,
     }
 }
 
@@ -122,7 +57,6 @@ pub const BUFSIZ : ::c_uint = 1024;
 pub const FOPEN_MAX : ::c_uint = 20;
 pub const FILENAME_MAX : ::c_uint = 1024;
 pub const L_tmpnam : ::c_uint = 1024;
-pub const TMP_MAX : ::c_uint = 308915776;
 pub const O_RDONLY : ::c_int = 0;
 pub const O_WRONLY : ::c_int = 1;
 pub const O_RDWR : ::c_int = 2;
@@ -131,7 +65,7 @@ pub const O_CREAT : ::c_int = 512;
 pub const O_EXCL : ::c_int = 2048;
 pub const O_NOCTTY : ::c_int = 32768;
 pub const O_TRUNC : ::c_int = 1024;
-pub const O_CLOEXEC: ::c_int = 0x10000;
+pub const O_SYNC : ::c_int = 128;
 pub const S_IFIFO : mode_t = 4096;
 pub const S_IFCHR : mode_t = 8192;
 pub const S_IFBLK : mode_t = 24576;
@@ -195,8 +129,6 @@ pub const MCL_CURRENT : ::c_int = 0x0001;
 pub const MCL_FUTURE : ::c_int = 0x0002;
 
 pub const MS_ASYNC : ::c_int = 0x0001;
-pub const MS_SYNC : ::c_int = 0x0002;
-pub const MS_INVALIDATE : ::c_int = 0x0004;
 
 pub const EPERM : ::c_int = 1;
 pub const ENOENT : ::c_int = 2;
@@ -280,29 +212,12 @@ pub const ENOSYS : ::c_int = 78;
 pub const EFTYPE : ::c_int = 79;
 pub const EAUTH : ::c_int = 80;
 pub const ENEEDAUTH : ::c_int = 81;
-pub const EIPSEC : ::c_int = 82;
-pub const ENOATTR : ::c_int = 83;
-pub const EILSEQ : ::c_int = 84;
-pub const ENOMEDIUM : ::c_int = 85;
-pub const EMEDIUMTYPE : ::c_int = 86;
-pub const EOVERFLOW : ::c_int = 87;
-pub const ECANCELED : ::c_int = 88;
-pub const EIDRM : ::c_int = 89;
-pub const ENOMSG : ::c_int = 90;
-pub const ENOTSUP : ::c_int = 91;
-pub const ELAST : ::c_int = 91; // must be equal to largest errno
 
 pub const F_DUPFD : ::c_int = 0;
 pub const F_GETFD : ::c_int = 1;
 pub const F_SETFD : ::c_int = 2;
 pub const F_GETFL : ::c_int = 3;
 pub const F_SETFL : ::c_int = 4;
-pub const F_GETOWN : ::c_int = 5;
-pub const F_SETOWN : ::c_int = 6;
-pub const F_GETLK : ::c_int = 7;
-pub const F_SETLK : ::c_int = 8;
-pub const F_SETLKW : ::c_int = 9;
-pub const F_DUPFD_CLOEXEC : ::c_int = 10;
 
 pub const SIGTRAP : ::c_int = 5;
 
@@ -325,37 +240,10 @@ pub const POSIX_MADV_SEQUENTIAL : ::c_int = 2;
 pub const POSIX_MADV_WILLNEED : ::c_int = 3;
 pub const POSIX_MADV_DONTNEED : ::c_int = 4;
 
-pub const _SC_IOV_MAX : ::c_int = 51;
-pub const _SC_GETGR_R_SIZE_MAX : ::c_int = 100;
-pub const _SC_GETPW_R_SIZE_MAX : ::c_int = 101;
-pub const _SC_LOGIN_NAME_MAX : ::c_int = 102;
-pub const _SC_MQ_PRIO_MAX : ::c_int = 59;
-pub const _SC_THREAD_ATTR_STACKADDR : ::c_int = 77;
-pub const _SC_THREAD_ATTR_STACKSIZE : ::c_int = 78;
-pub const _SC_THREAD_DESTRUCTOR_ITERATIONS : ::c_int = 80;
-pub const _SC_THREAD_KEYS_MAX : ::c_int = 81;
-pub const _SC_THREAD_PRIO_INHERIT : ::c_int = 82;
-pub const _SC_THREAD_PRIO_PROTECT : ::c_int = 83;
-pub const _SC_THREAD_PRIORITY_SCHEDULING : ::c_int = 84;
-pub const _SC_THREAD_PROCESS_SHARED : ::c_int = 85;
-pub const _SC_THREAD_SAFE_FUNCTIONS : ::c_int = 103;
-pub const _SC_THREAD_STACK_MIN : ::c_int = 89;
-pub const _SC_THREAD_THREADS_MAX : ::c_int = 90;
-pub const _SC_THREADS : ::c_int = 91;
-pub const _SC_TTY_NAME_MAX : ::c_int = 107;
-pub const _SC_ATEXIT_MAX : ::c_int = 46;
-pub const _SC_XOPEN_CRYPT : ::c_int = 117;
-pub const _SC_XOPEN_ENH_I18N : ::c_int = 118;
-pub const _SC_XOPEN_LEGACY : ::c_int = 119;
-pub const _SC_XOPEN_REALTIME : ::c_int = 120;
-pub const _SC_XOPEN_REALTIME_THREADS : ::c_int = 121;
 pub const _SC_XOPEN_SHM : ::c_int = 30;
-pub const _SC_XOPEN_UNIX : ::c_int = 123;
-pub const _SC_XOPEN_VERSION : ::c_int = 125;
 
 pub const PTHREAD_CREATE_JOINABLE : ::c_int = 0;
 pub const PTHREAD_CREATE_DETACHED : ::c_int = 1;
-pub const PTHREAD_STACK_MIN : ::size_t = 2048;
 
 pub const CLOCK_REALTIME : ::c_int = 0;
 pub const CLOCK_MONOTONIC : ::c_int = 3;
@@ -369,7 +257,6 @@ pub const RLIMIT_RSS: ::c_int = 5;
 pub const RLIMIT_MEMLOCK: ::c_int = 6;
 pub const RLIMIT_NPROC: ::c_int = 7;
 pub const RLIMIT_NOFILE: ::c_int = 8;
-pub const RLIM_NLIMITS: ::c_int = 9;
 
 pub const RLIM_INFINITY: rlim_t = 0x7fff_ffff_ffff_ffff;
 pub const RLIM_SAVED_MAX: rlim_t = RLIM_INFINITY;
@@ -377,7 +264,6 @@ pub const RLIM_SAVED_CUR: rlim_t = RLIM_INFINITY;
 
 pub const RUSAGE_SELF: ::c_int = 0;
 pub const RUSAGE_CHILDREN: ::c_int = -1;
-pub const RUSAGE_THREAD: ::c_int = 1;
 
 pub const MADV_NORMAL : ::c_int = 0;
 pub const MADV_RANDOM : ::c_int = 1;
@@ -401,8 +287,6 @@ pub const IP_TTL: ::c_int = 4;
 pub const IP_HDRINCL: ::c_int = 2;
 pub const IP_ADD_MEMBERSHIP: ::c_int = 12;
 pub const IP_DROP_MEMBERSHIP: ::c_int = 13;
-pub const IPV6_ADD_MEMBERSHIP: ::c_int = 12; // don't exist
-pub const IPV6_DROP_MEMBERSHIP: ::c_int = 13; // don't exist
 
 pub const TCP_NODELAY: ::c_int = 0x01;
 pub const SOL_SOCKET: ::c_int = 0xffff;
@@ -420,8 +304,6 @@ pub const SO_SNDBUF: ::c_int = 0x1001;
 pub const SO_RCVBUF: ::c_int = 0x1002;
 pub const SO_SNDLOWAT: ::c_int = 0x1003;
 pub const SO_RCVLOWAT: ::c_int = 0x1004;
-pub const SO_SNDTIMEO: ::c_int = 0x1005;
-pub const SO_RCVTIMEO: ::c_int = 0x1006;
 pub const SO_ERROR: ::c_int = 0x1007;
 pub const SO_TYPE: ::c_int = 0x1008;
 
@@ -436,25 +318,13 @@ pub const LOCK_EX: ::c_int = 2;
 pub const LOCK_NB: ::c_int = 4;
 pub const LOCK_UN: ::c_int = 8;
 
-pub const O_DSYNC : ::c_int = 128; // same as SYNC
-pub const O_SYNC : ::c_int = 128;
 pub const O_NONBLOCK : ::c_int = 4;
 pub const CTL_KERN : ::c_int = 1;
-pub const KERN_PROC : ::c_int = 66;
-
-pub const MAP_COPY : ::c_int = 0x0002;
-pub const MAP_RENAME : ::c_int = 0x0000;
-pub const MAP_NORESERVE : ::c_int = 0x0000;
-pub const MAP_NOEXTEND : ::c_int = 0x0000;
-pub const MAP_HASSEMAPHORE : ::c_int = 0x0000;
 
 pub const IPPROTO_RAW : ::c_int = 255;
 
-pub const PATH_MAX: ::c_int = 1024;
-
 pub const _SC_ARG_MAX : ::c_int = 1;
 pub const _SC_CHILD_MAX : ::c_int = 2;
-pub const _SC_CLK_TCK : ::c_int = 3;
 pub const _SC_NGROUPS_MAX : ::c_int = 4;
 pub const _SC_OPEN_MAX : ::c_int = 5;
 pub const _SC_JOB_CONTROL : ::c_int = 6;
@@ -481,61 +351,22 @@ pub const _SC_STREAM_MAX : ::c_int = 26;
 pub const _SC_TZNAME_MAX : ::c_int = 27;
 pub const _SC_PAGESIZE : ::c_int = 28;
 pub const _SC_FSYNC : ::c_int = 29;
-pub const _SC_SEM_NSEMS_MAX : ::c_int = 31;
-pub const _SC_SEM_VALUE_MAX : ::c_int = 32;
-pub const _SC_AIO_LISTIO_MAX : ::c_int = 42;
-pub const _SC_AIO_MAX : ::c_int = 43;
-pub const _SC_AIO_PRIO_DELTA_MAX : ::c_int = 44;
-pub const _SC_ASYNCHRONOUS_IO : ::c_int = 45;
-pub const _SC_DELAYTIMER_MAX : ::c_int = 50;
-pub const _SC_MAPPED_FILES : ::c_int = 53;
-pub const _SC_MEMLOCK : ::c_int = 54;
-pub const _SC_MEMLOCK_RANGE : ::c_int = 55;
-pub const _SC_MEMORY_PROTECTION : ::c_int = 56;
-pub const _SC_MESSAGE_PASSING : ::c_int = 57;
-pub const _SC_MQ_OPEN_MAX : ::c_int = 58;
-pub const _SC_PRIORITIZED_IO : ::c_int = 60;
-pub const _SC_PRIORITY_SCHEDULING : ::c_int = 61;
-pub const _SC_REALTIME_SIGNALS : ::c_int = 64;
-pub const _SC_RTSIG_MAX : ::c_int = 66;
-pub const _SC_SEMAPHORES : ::c_int = 67;
-pub const _SC_SHARED_MEMORY_OBJECTS : ::c_int = 68;
-pub const _SC_SIGQUEUE_MAX : ::c_int = 70;
-pub const _SC_SYNCHRONIZED_IO : ::c_int = 75;
-pub const _SC_TIMER_MAX : ::c_int = 93;
-pub const _SC_TIMERS : ::c_int = 94;
-
-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_RECURSIVE: ::c_int = 2;
-pub const SIGSTKSZ: ::size_t = 131072;
+
+pub const KERN_PROC_ARGV: ::c_int = 1;
 
 extern {
-    pub fn mprotect(addr: *const ::c_void, len: ::size_t, prot: ::c_int)
-                    -> ::c_int;
+    pub fn mincore(addr: *mut ::c_void, len: ::size_t,
+                   vec: *mut ::c_char) -> ::c_int;
+    #[cfg_attr(target_os = "netbsd", link_name = "__clock_gettime50")]
+    pub fn clock_gettime(clk_id: ::c_int, tp: *mut ::timespec) -> ::c_int;
+    pub fn __errno() -> *mut ::c_int;
     pub fn shm_open(name: *const ::c_char, oflag: ::c_int, mode: ::mode_t)
                     -> ::c_int;
-    pub fn sysctl(name: *mut ::c_int,
-                  namelen: ::c_uint,
-                  oldp: *mut ::c_void,
-                  oldlenp: *mut ::size_t,
-                  newp: *mut ::c_void,
-                  newlen: ::size_t)
-                  -> ::c_int;
-    pub fn sysctlbyname(name: *const ::c_char,
-                        oldp: *mut ::c_void,
-                        oldlenp: *mut ::size_t,
-                        newp: *mut ::c_void,
-                        newlen: ::size_t)
-                        -> ::c_int;
-    pub fn clock_gettime(clk_id: ::c_int, tp: *mut ::timespec) -> ::c_int;
-    pub fn pthread_main_np() -> ::c_uint;
+    pub fn pthread_main_np() -> ::c_int;
+    pub fn pthread_set_name_np(tid: ::pthread_t, name: *const ::c_char);
     pub fn pthread_stackseg_np(thread: ::pthread_t,
-                               sinfo: *mut ::stack_t) -> ::c_uint;
-    pub fn __errno() -> *const ::c_int;
-    pub fn backtrace(buf: *mut *mut ::c_void,
-                     sz: ::size_t) -> ::size_t;
+                               sinfo: *mut ::stack_t) -> ::c_int;
+    pub fn memrchr(cx: *const ::c_void, c: ::c_int, n: ::size_t) -> *mut ::c_void;
 }
 
 cfg_if! {
index 17d809df59fe3d708d3bf5cd4faedb04fe2dd838..f2a10f4c7b19c2040a25293dfc2562a68dc35bda 100644 (file)
@@ -1,8 +1,23 @@
+pub type clock_t = ::c_uint;
+pub type suseconds_t = ::c_int;
+pub type dev_t = u64;
+pub type blksize_t = ::int32_t;
+pub type fsblkcnt_t = ::uint64_t;
+pub type fsfilcnt_t = ::uint64_t;
+
 s! {
+    pub struct dirent {
+        pub d_fileno: ::ino_t,
+        pub d_reclen: u16,
+        pub d_namlen: u16,
+        pub d_type: u8,
+        pub d_name: [::c_char; 512],
+    }
+
     pub struct glob_t {
-        pub gl_pathc:   ::c_int,
+        pub gl_pathc:   ::size_t,
         __unused1:      ::c_int,
-        pub gl_offs:    ::c_int,
+        pub gl_offs:    ::size_t,
         __unused2:      ::c_int,
         pub gl_pathv:   *mut *mut ::c_char,
 
@@ -13,10 +28,341 @@ s! {
         __unused6: *mut ::c_void,
         __unused7: *mut ::c_void,
         __unused8: *mut ::c_void,
-        __unused9: *mut ::c_void,
+    }
+
+    pub struct sigset_t {
+        __bits: [u32; 4],
+    }
+
+    pub struct stat {
+        pub st_dev: ::dev_t,
+        pub st_mode: ::mode_t,
+        pub st_ino: ::ino_t,
+        pub st_nlink: ::nlink_t,
+        pub st_uid: ::uid_t,
+        pub st_gid: ::gid_t,
+        pub st_rdev: ::dev_t,
+        pub st_atime: ::time_t,
+        pub st_atimensec: ::c_long,
+        pub st_mtime: ::time_t,
+        pub st_mtimensec: ::c_long,
+        pub st_ctime: ::time_t,
+        pub st_ctimensec: ::c_long,
+        pub st_birthtime: ::time_t,
+        pub st_birthtimensec: ::c_long,
+        pub st_size: ::off_t,
+        pub st_blocks: ::blkcnt_t,
+        pub st_blksize: ::blksize_t,
+        pub st_flags: ::uint32_t,
+        pub st_gen: ::uint32_t,
+        pub st_spare: [::uint32_t; 2],
+    }
+
+    pub struct statvfs {
+        pub f_flag: ::c_ulong,
+        pub f_bsize: ::c_ulong,
+        pub f_frsize: ::c_ulong,
+        pub f_iosize: ::c_ulong,
+
+        pub f_blocks: ::fsblkcnt_t,
+        pub f_bfree: ::fsblkcnt_t,
+        pub f_bavail: ::fsblkcnt_t,
+        pub f_bresvd: ::fsblkcnt_t,
+
+        pub f_files: ::fsfilcnt_t,
+        pub f_ffree: ::fsfilcnt_t,
+        pub f_favail: ::fsfilcnt_t,
+        pub f_fresvd: ::fsfilcnt_t,
+
+        pub f_syncreads: ::uint64_t,
+        pub f_syncwrites: ::uint64_t,
+
+        pub f_asyncreads: ::uint64_t,
+        pub f_asyncwrites: ::uint64_t,
+
+        pub f_fsidx: ::fsid_t,
+        pub f_fsid: ::c_ulong,
+        pub f_namemax: ::c_ulong,
+        pub f_owner: ::uid_t,
+
+        pub f_spare: [::uint32_t; 4],
+
+        pub f_fstypename: [::c_char; 32],
+        pub f_mntonname: [::c_char; 1024],
+        pub f_mntfromname: [::c_char; 1024],
+    }
+
+    pub struct addrinfo {
+        pub ai_flags: ::c_int,
+        pub ai_family: ::c_int,
+        pub ai_socktype: ::c_int,
+        pub ai_protocol: ::c_int,
+        pub ai_addrlen: ::socklen_t,
+        pub ai_canonname: *mut ::c_char,
+        pub ai_addr: *mut ::sockaddr,
+        pub ai_next: *mut ::addrinfo,
+    }
+
+    pub struct sockaddr_storage {
+        pub ss_len: u8,
+        pub ss_family: ::sa_family_t,
+        __ss_pad1: [u8; 6],
+        __ss_pad2: i64,
+        __ss_pad3: [u8; 112],
+    }
+
+    pub struct siginfo_t {
+        pub si_signo: ::c_int,
+        pub si_code: ::c_int,
+        pub si_errno: ::c_int,
+        __pad1: ::c_int,
+        __pad2: [u64; 14],
+    }
+
+    pub struct pthread_attr_t {
+        pta_magic: ::c_uint,
+        pta_flags: ::c_int,
+        pta_private: *mut ::c_void,
+    }
+
+    pub struct pthread_mutex_t {
+        ptm_magic: ::c_uint,
+        ptm_errorcheck: ::c_uchar,
+        ptm_pad1: [u8; 3],
+        ptm_interlock: ::c_uchar,
+        ptm_pad2: [u8; 3],
+        ptm_owner: ::pthread_t,
+        ptm_waiters: *mut u8,
+        ptm_recursed: ::c_uint,
+        ptm_spare2: *mut ::c_void,
+    }
+
+    pub struct pthread_mutexattr_t {
+        ptma_magic: ::c_uint,
+        ptma_private: *mut ::c_void,
+    }
+
+    pub struct pthread_cond_t {
+        ptc_magic: ::c_uint,
+        ptc_lock: ::c_uchar,
+        ptc_waiters_first: *mut u8,
+        ptc_waiters_last: *mut u8,
+        ptc_mutex: *mut ::pthread_mutex_t,
+        ptc_private: *mut ::c_void,
+    }
+
+    pub struct pthread_rwlock_t {
+        ptr_magic: ::c_uint,
+        ptr_interlock: ::c_uchar,
+        ptr_rblocked_first: *mut u8,
+        ptr_rblocked_last: *mut u8,
+        ptr_wblocked_first: *mut u8,
+        ptr_wblocked_last: *mut u8,
+        ptr_nreaders: ::c_uint,
+        ptr_owner: ::pthread_t,
+        ptr_private: *mut ::c_void,
+    }
+
+    pub struct kevent {
+        pub ident: ::uintptr_t,
+        pub filter: ::uint32_t,
+        pub flags: ::uint32_t,
+        pub fflags: ::uint32_t,
+        pub data: ::int64_t,
+        pub udata: ::intptr_t,
+    }
+
+    pub struct dqblk {
+        pub dqb_bhardlimit: ::uint32_t,
+        pub dqb_bsoftlimit: ::uint32_t,
+        pub dqb_curblocks: ::uint32_t,
+        pub dqb_ihardlimit: ::uint32_t,
+        pub dqb_isoftlimit: ::uint32_t,
+        pub dqb_curinodes: ::uint32_t,
+        pub dqb_btime: ::int32_t,
+        pub dqb_itime: ::int32_t,
+    }
+
+    pub struct Dl_info {
+        pub dli_fname: *const ::c_char,
+        pub dli_fbase: *mut ::c_void,
+        pub dli_sname: *const ::c_char,
+        pub dli_saddr: *const ::c_void,
     }
 }
 
+pub const O_CLOEXEC: ::c_int = 0x400000;
+pub const O_ALT_IO: ::c_int = 0x40000;
+pub const O_NOSIGPIPE: ::c_int = 0x1000000;
+pub const O_SEARCH: ::c_int = 0x800000;
+pub const O_EXLOCK: ::c_int = 0x20;
+pub const O_SHLOCK: ::c_int = 0x10;
+pub const O_DIRECTORY: ::c_int = 0x200000;
+
+pub const MS_SYNC : ::c_int = 0x4;
+pub const MS_INVALIDATE : ::c_int = 0x2;
+
+pub const RLIM_NLIMITS: ::c_int = 12;
+
+pub const ENOATTR : ::c_int = 93;
+pub const EILSEQ : ::c_int = 85;
+pub const EOVERFLOW : ::c_int = 84;
+pub const ECANCELED : ::c_int = 87;
+pub const EIDRM : ::c_int = 82;
+pub const ENOMSG : ::c_int = 83;
+pub const ENOTSUP : ::c_int = 86;
+pub const ELAST : ::c_int = 96;
+
+pub const F_DUPFD_CLOEXEC : ::c_int = 12;
+pub const F_CLOSEM: ::c_int = 10;
+pub const F_GETNOSIGPIPE: ::c_int = 13;
+pub const F_SETNOSIGPIPE: ::c_int = 14;
+pub const F_MAXFD: ::c_int = 11;
+
+pub const IPV6_JOIN_GROUP: ::c_int = 12;
+pub const IPV6_LEAVE_GROUP: ::c_int = 13;
+
+pub const SO_SNDTIMEO: ::c_int = 0x100b;
+pub const SO_RCVTIMEO: ::c_int = 0x100c;
+
+pub const KERN_PROC : ::c_int = 14;
+pub const O_DSYNC : ::c_int = 0x10000;
+
+pub const MAP_RENAME : ::c_int = 0x20;
+pub const MAP_NORESERVE : ::c_int = 0x40;
+pub const MAP_HASSEMAPHORE : ::c_int = 0x200;
+pub const MAP_WIRED: ::c_int = 0x800;
+
+pub const _SC_IOV_MAX : ::c_int = 32;
+pub const _SC_GETGR_R_SIZE_MAX : ::c_int = 47;
+pub const _SC_GETPW_R_SIZE_MAX : ::c_int = 48;
+pub const _SC_LOGIN_NAME_MAX : ::c_int = 37;
+pub const _SC_MQ_PRIO_MAX : ::c_int = 55;
+pub const _SC_THREADS : ::c_int = 41;
+pub const _SC_THREAD_ATTR_STACKADDR : ::c_int = 61;
+pub const _SC_THREAD_ATTR_STACKSIZE : ::c_int = 62;
+pub const _SC_THREAD_DESTRUCTOR_ITERATIONS : ::c_int = 57;
+pub const _SC_THREAD_KEYS_MAX : ::c_int = 58;
+pub const _SC_THREAD_PRIO_INHERIT : ::c_int = 64;
+pub const _SC_THREAD_PRIO_PROTECT : ::c_int = 65;
+pub const _SC_THREAD_PRIORITY_SCHEDULING : ::c_int = 63;
+pub const _SC_THREAD_PROCESS_SHARED : ::c_int = 66;
+pub const _SC_THREAD_SAFE_FUNCTIONS : ::c_int = 67;
+pub const _SC_THREAD_STACK_MIN : ::c_int = 59;
+pub const _SC_THREAD_THREADS_MAX : ::c_int = 60;
+pub const _SC_TTY_NAME_MAX : ::c_int = 68;
+pub const _SC_ATEXIT_MAX : ::c_int = 40;
+pub const _SC_CLK_TCK : ::c_int = 39;
+pub const _SC_AIO_LISTIO_MAX : ::c_int = 51;
+pub const _SC_AIO_MAX : ::c_int = 52;
+pub const _SC_ASYNCHRONOUS_IO : ::c_int = 50;
+pub const _SC_MAPPED_FILES : ::c_int = 33;
+pub const _SC_MEMLOCK : ::c_int = 34;
+pub const _SC_MEMLOCK_RANGE : ::c_int = 35;
+pub const _SC_MEMORY_PROTECTION : ::c_int = 36;
+pub const _SC_MESSAGE_PASSING : ::c_int = 53;
+pub const _SC_MQ_OPEN_MAX : ::c_int = 54;
+pub const _SC_PRIORITY_SCHEDULING : ::c_int = 56;
+pub const _SC_SEMAPHORES : ::c_int = 42;
+pub const _SC_SHARED_MEMORY_OBJECTS : ::c_int = 87;
+pub const _SC_SYNCHRONIZED_IO : ::c_int = 31;
+pub const _SC_TIMERS : ::c_int = 44;
+
+pub const SIGSTKSZ: ::size_t = 0xa000;
+
+pub const FD_SETSIZE: usize = 0x100;
+
+pub const ST_NOSUID: ::c_ulong = 8;
+
+pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
+    ptm_magic: 0x33330003,
+    ptm_errorcheck: 0,
+    ptm_interlock: 0,
+    ptm_waiters: 0 as *mut _,
+    ptm_owner: 0,
+    ptm_pad1: [0; 3],
+    ptm_pad2: [0; 3],
+    ptm_recursed: 0,
+    ptm_spare2: 0 as *mut _,
+};
+pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
+    ptc_magic: 0x55550005,
+    ptc_lock: 0,
+    ptc_waiters_first: 0 as *mut _,
+    ptc_waiters_last: 0 as *mut _,
+    ptc_mutex: 0 as *mut _,
+    ptc_private: 0 as *mut _,
+};
+pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
+    ptr_magic: 0x99990009,
+    ptr_interlock: 0,
+    ptr_rblocked_first: 0 as *mut _,
+    ptr_rblocked_last: 0 as *mut _,
+    ptr_wblocked_first: 0 as *mut _,
+    ptr_wblocked_last: 0 as *mut _,
+    ptr_nreaders: 0,
+    ptr_owner: 0,
+    ptr_private: 0 as *mut _,
+};
+pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 2;
+pub const KERN_PROC_ARGS: ::c_int = 48;
+
+pub const EVFILT_AIO: ::int16_t = 2;
+pub const EVFILT_PROC: ::int16_t = 4;
+pub const EVFILT_READ: ::int16_t = 0;
+pub const EVFILT_SIGNAL: ::int16_t = 5;
+pub const EVFILT_SYSCOUNT: ::int16_t = 7;
+pub const EVFILT_TIMER: ::int16_t = 6;
+pub const EVFILT_VNODE: ::int16_t = 3;
+pub const EVFILT_WRITE: ::int16_t = 1;
+
+pub const NOTE_PCTRLMASK: ::uint32_t = 0xf0000000;
+
+pub const CRTSCTS: ::tcflag_t = 0x00010000;
+
+pub const TMP_MAX : ::c_uint = 308915776;
+
+pub const NI_MAXHOST: ::socklen_t = 1025;
+
 extern {
-    pub fn pthread_setname_np(tid: ::pthread_t, format: *const ::c_char, name: *const ::c_void);
-}
\ No newline at end of file
+    pub fn getnameinfo(sa: *const ::sockaddr,
+                       salen: ::socklen_t,
+                       host: *mut ::c_char,
+                       hostlen: ::socklen_t,
+                       serv: *mut ::c_char,
+                       sevlen: ::socklen_t,
+                       flags: ::c_int) -> ::c_int;
+    pub fn mprotect(addr: *mut ::c_void, len: ::size_t, prot: ::c_int)
+                    -> ::c_int;
+    pub fn sysctl(name: *const ::c_int,
+                  namelen: ::c_uint,
+                  oldp: *mut ::c_void,
+                  oldlenp: *mut ::size_t,
+                  newp: *const ::c_void,
+                  newlen: ::size_t)
+                  -> ::c_int;
+    pub fn sysctlbyname(name: *const ::c_char,
+                        oldp: *mut ::c_void,
+                        oldlenp: *mut ::size_t,
+                        newp: *const ::c_void,
+                        newlen: ::size_t)
+                        -> ::c_int;
+    #[link_name = "__kevent50"]
+    pub fn kevent(kq: ::c_int,
+                  changelist: *const ::kevent,
+                  nchanges: ::size_t,
+                  eventlist: *mut ::kevent,
+                  nevents: ::size_t,
+                  timeout: *const ::timespec) -> ::c_int;
+    #[link_name = "__mount50"]
+    pub fn mount(src: *const ::c_char,
+                 target: *const ::c_char,
+                 flags: ::c_int,
+                 data: *mut ::c_void,
+                 size: ::size_t) -> ::c_int;
+    pub fn ptrace(requeset: ::c_int,
+                  pid: ::pid_t,
+                  addr: *mut ::c_void,
+                  data: ::c_int) -> ::c_int;
+    pub fn sethostname(name: *const ::c_char, len: ::size_t) -> ::c_int;
+}
index 6b0577e10dfd9cb84aa45de879cb70f822fa4b01..fddde4506fee9e42763ddbe9d28696520b63cd4f 100644 (file)
@@ -1,4 +1,27 @@
+pub type clock_t = i64;
+pub type suseconds_t = i64;
+pub type dev_t = i32;
+pub type sigset_t = ::c_uint;
+pub type blksize_t = ::int32_t;
+pub type fsblkcnt_t = ::uint64_t;
+pub type fsfilcnt_t = ::uint64_t;
+pub type pthread_attr_t = *mut ::c_void;
+pub type pthread_mutex_t = *mut ::c_void;
+pub type pthread_mutexattr_t = *mut ::c_void;
+pub type pthread_cond_t = *mut ::c_void;
+pub type pthread_rwlock_t = *mut ::c_void;
+
 s! {
+    pub struct dirent {
+        pub d_fileno: ::ino_t,
+        pub d_off: ::off_t,
+        pub d_reclen: u16,
+        pub d_type: u8,
+        pub d_namlen: u8,
+        __d_padding: [u8; 4],
+        pub d_name: [::c_char; 256],
+    }
+
     pub struct glob_t {
         pub gl_pathc:   ::c_int,
         __unused1:      ::c_int,
@@ -15,8 +38,202 @@ s! {
         __unused8: *mut ::c_void,
         __unused9: *mut ::c_void,
     }
+
+    pub struct stat {
+        pub st_mode: ::mode_t,
+        pub st_dev: ::dev_t,
+        pub st_ino: ::ino_t,
+        pub st_nlink: ::nlink_t,
+        pub st_uid: ::uid_t,
+        pub st_gid: ::gid_t,
+        pub st_rdev: ::dev_t,
+        pub st_atime: ::time_t,
+        pub st_atime_nsec: ::c_long,
+        pub st_mtime: ::time_t,
+        pub st_mtime_nsec: ::c_long,
+        pub st_ctime: ::time_t,
+        pub st_ctime_nsec: ::c_long,
+        pub st_size: ::off_t,
+        pub st_blocks: ::blkcnt_t,
+        pub st_blksize: ::blksize_t,
+        pub st_flags: ::uint32_t,
+        pub st_gen: ::uint32_t,
+        pub st_birthtime: ::time_t,
+        pub st_birthtime_nsec: ::c_long,
+    }
+
+    pub struct statvfs {
+        pub f_bsize: ::c_ulong,
+        pub f_frsize: ::c_ulong,
+        pub f_blocks: ::fsblkcnt_t,
+        pub f_bfree: ::fsblkcnt_t,
+        pub f_bavail: ::fsblkcnt_t,
+        pub f_files: ::fsfilcnt_t,
+        pub f_ffree: ::fsfilcnt_t,
+        pub f_favail: ::fsfilcnt_t,
+        pub f_fsid: ::c_ulong,
+        pub f_flag: ::c_ulong,
+        pub f_namemax: ::c_ulong,
+    }
+
+    pub struct addrinfo {
+        pub ai_flags: ::c_int,
+        pub ai_family: ::c_int,
+        pub ai_socktype: ::c_int,
+        pub ai_protocol: ::c_int,
+        pub ai_addrlen: ::socklen_t,
+        pub ai_addr: *mut ::sockaddr,
+        pub ai_canonname: *mut ::c_char,
+        pub ai_next: *mut ::addrinfo,
+    }
+
+    pub struct sockaddr_storage {
+        pub ss_len: u8,
+        pub ss_family: ::sa_family_t,
+        __ss_pad1: [u8; 6],
+        __ss_pad2: i64,
+        __ss_pad3: [u8; 240],
+    }
+
+    pub struct siginfo_t {
+        pub si_signo: ::c_int,
+        pub si_code: ::c_int,
+        pub si_errno: ::c_int,
+        pub si_addr: *mut ::c_char,
+        __pad: [u8; 108],
+    }
+
+    pub struct Dl_info {
+        pub dli_fname: *const ::c_char,
+        pub dli_fbase: *mut ::c_void,
+        pub dli_sname: *const ::c_char,
+        pub dli_saddr: *mut ::c_void,
+    }
 }
 
+pub const O_CLOEXEC: ::c_int = 0x10000;
+
+pub const MS_SYNC : ::c_int = 0x0002;
+pub const MS_INVALIDATE : ::c_int = 0x0004;
+
+pub const PTHREAD_STACK_MIN : ::size_t = 2048;
+
+pub const ENOATTR : ::c_int = 83;
+pub const EILSEQ : ::c_int = 84;
+pub const EOVERFLOW : ::c_int = 87;
+pub const ECANCELED : ::c_int = 88;
+pub const EIDRM : ::c_int = 89;
+pub const ENOMSG : ::c_int = 90;
+pub const ENOTSUP : ::c_int = 91;
+pub const ELAST : ::c_int = 91;
+
+pub const F_DUPFD_CLOEXEC : ::c_int = 10;
+
+pub const RLIM_NLIMITS: ::c_int = 9;
+
+pub const SO_SNDTIMEO: ::c_int = 0x1005;
+pub const SO_RCVTIMEO: ::c_int = 0x1006;
+
+pub const KERN_PROC : ::c_int = 66;
+pub const O_DSYNC : ::c_int = 128;
+
+pub const MAP_RENAME : ::c_int = 0x0000;
+pub const MAP_NORESERVE : ::c_int = 0x0000;
+pub const MAP_HASSEMAPHORE : ::c_int = 0x0000;
+
+pub const EIPSEC : ::c_int = 82;
+pub const ENOMEDIUM : ::c_int = 85;
+pub const EMEDIUMTYPE : ::c_int = 86;
+
+pub const RUSAGE_THREAD: ::c_int = 1;
+
+pub const MAP_COPY : ::c_int = 0x0002;
+pub const MAP_NOEXTEND : ::c_int = 0x0000;
+
+pub const _SC_CLK_TCK : ::c_int = 3;
+pub const _SC_IOV_MAX : ::c_int = 51;
+pub const _SC_GETGR_R_SIZE_MAX : ::c_int = 100;
+pub const _SC_GETPW_R_SIZE_MAX : ::c_int = 101;
+pub const _SC_LOGIN_NAME_MAX : ::c_int = 102;
+pub const _SC_MQ_PRIO_MAX : ::c_int = 59;
+pub const _SC_THREADS : ::c_int = 91;
+pub const _SC_THREAD_ATTR_STACKADDR : ::c_int = 77;
+pub const _SC_THREAD_ATTR_STACKSIZE : ::c_int = 78;
+pub const _SC_THREAD_DESTRUCTOR_ITERATIONS : ::c_int = 80;
+pub const _SC_THREAD_KEYS_MAX : ::c_int = 81;
+pub const _SC_THREAD_PRIO_INHERIT : ::c_int = 82;
+pub const _SC_THREAD_PRIO_PROTECT : ::c_int = 83;
+pub const _SC_THREAD_PRIORITY_SCHEDULING : ::c_int = 84;
+pub const _SC_THREAD_PROCESS_SHARED : ::c_int = 85;
+pub const _SC_THREAD_SAFE_FUNCTIONS : ::c_int = 103;
+pub const _SC_THREAD_STACK_MIN : ::c_int = 89;
+pub const _SC_THREAD_THREADS_MAX : ::c_int = 90;
+pub const _SC_TTY_NAME_MAX : ::c_int = 107;
+pub const _SC_ATEXIT_MAX : ::c_int = 46;
+pub const _SC_AIO_LISTIO_MAX : ::c_int = 42;
+pub const _SC_AIO_MAX : ::c_int = 43;
+pub const _SC_ASYNCHRONOUS_IO : ::c_int = 45;
+pub const _SC_MAPPED_FILES : ::c_int = 53;
+pub const _SC_MEMLOCK : ::c_int = 54;
+pub const _SC_MEMLOCK_RANGE : ::c_int = 55;
+pub const _SC_MEMORY_PROTECTION : ::c_int = 56;
+pub const _SC_MESSAGE_PASSING : ::c_int = 57;
+pub const _SC_MQ_OPEN_MAX : ::c_int = 58;
+pub const _SC_PRIORITY_SCHEDULING : ::c_int = 61;
+pub const _SC_SEMAPHORES : ::c_int = 67;
+pub const _SC_SHARED_MEMORY_OBJECTS : ::c_int = 68;
+pub const _SC_SYNCHRONIZED_IO : ::c_int = 75;
+pub const _SC_TIMERS : ::c_int = 94;
+pub const _SC_XOPEN_CRYPT : ::c_int = 117;
+pub const _SC_XOPEN_ENH_I18N : ::c_int = 118;
+pub const _SC_XOPEN_LEGACY : ::c_int = 119;
+pub const _SC_XOPEN_REALTIME : ::c_int = 120;
+pub const _SC_XOPEN_REALTIME_THREADS : ::c_int = 121;
+pub const _SC_XOPEN_UNIX : ::c_int = 123;
+pub const _SC_XOPEN_VERSION : ::c_int = 125;
+pub const _SC_SEM_NSEMS_MAX : ::c_int = 31;
+pub const _SC_SEM_VALUE_MAX : ::c_int = 32;
+pub const _SC_AIO_PRIO_DELTA_MAX : ::c_int = 44;
+pub const _SC_DELAYTIMER_MAX : ::c_int = 50;
+pub const _SC_PRIORITIZED_IO : ::c_int = 60;
+pub const _SC_REALTIME_SIGNALS : ::c_int = 64;
+pub const _SC_RTSIG_MAX : ::c_int = 66;
+pub const _SC_SIGQUEUE_MAX : ::c_int = 70;
+pub const _SC_TIMER_MAX : ::c_int = 93;
+
+pub const SIGSTKSZ: ::size_t = 40960;
+
+pub const FD_SETSIZE: usize = 1024;
+
+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_RECURSIVE: ::c_int = 2;
+
+pub const KERN_PROC_ARGS: ::c_int = 55;
+
+pub const TMP_MAX : ::c_uint = 0x7fffffff;
+
+pub const NI_MAXHOST: ::size_t = 256;
+
 extern {
-    pub fn pthread_set_name_np(tid: ::pthread_t, name: *const ::c_char);
-}
\ No newline at end of file
+    pub fn getnameinfo(sa: *const ::sockaddr,
+                       salen: ::socklen_t,
+                       host: *mut ::c_char,
+                       hostlen: ::size_t,
+                       serv: *mut ::c_char,
+                       servlen: ::size_t,
+                       flags: ::c_int) -> ::c_int;
+    pub fn mprotect(addr: *mut ::c_void, len: ::size_t, prot: ::c_int)
+                    -> ::c_int;
+    pub fn sysctl(name: *const ::c_int,
+                  namelen: ::c_uint,
+                  oldp: *mut ::c_void,
+                  oldlenp: *mut ::size_t,
+                  newp: *mut ::c_void,
+                  newlen: ::size_t)
+                  -> ::c_int;
+    pub fn getentropy(buf: *mut ::c_void, buflen: ::size_t) -> ::c_int;
+}
index 3339003dd16c58bf043e61f05161aaeced402429..ee3a9745764eb3d57d58b7464f377ed064882bd7 100644 (file)
@@ -9,6 +9,7 @@ pub type gid_t = u32;
 pub type in_addr_t = u32;
 pub type in_port_t = u16;
 pub type sighandler_t = ::size_t;
+pub type cc_t = ::c_uchar;
 
 pub enum DIR {}
 
@@ -55,6 +56,7 @@ s! {
         __reserved: [c_long; 16],
     }
 
+    #[cfg_attr(target_os = "netbsd", repr(packed))]
     pub struct in_addr {
         pub s_addr: in_addr_t,
     }
@@ -77,13 +79,6 @@ s! {
         pub ipv6mr_interface: ::c_uint,
     }
 
-    pub struct Dl_info {
-        pub dli_fname: *const ::c_char,
-        pub dli_fbase: *mut ::c_void,
-        pub dli_sname: *const ::c_char,
-        pub dli_saddr: *mut ::c_void,
-    }
-
     pub struct hostent {
         pub h_name: *mut ::c_char,
         pub h_aliases: *mut *mut ::c_char,
@@ -91,6 +86,17 @@ s! {
         pub h_length: ::c_int,
         pub h_addr_list: *mut *mut ::c_char,
     }
+
+    pub struct iovec {
+        pub iov_base: *mut ::c_void,
+        pub iov_len: ::size_t,
+    }
+
+    pub struct pollfd {
+        pub fd: ::c_int,
+        pub events: ::c_short,
+        pub revents: ::c_short,
+    }
 }
 
 pub const WNOHANG: ::c_int = 1;
@@ -98,6 +104,32 @@ pub const SIG_DFL: sighandler_t = 0 as sighandler_t;
 pub const SIG_IGN: sighandler_t = 1 as sighandler_t;
 pub const SIG_ERR: sighandler_t = !0 as sighandler_t;
 
+pub const DT_FIFO: u8 = 1;
+pub const DT_CHR: u8 = 2;
+pub const DT_DIR: u8 = 4;
+pub const DT_BLK: u8 = 6;
+pub const DT_REG: u8 = 8;
+pub const DT_LNK: u8 = 10;
+pub const DT_SOCK: u8 = 12;
+
+pub const FD_CLOEXEC: ::c_int = 0x1;
+
+pub const USRQUOTA: ::c_int = 0;
+pub const GRPQUOTA: ::c_int = 1;
+
+pub const SIGIOT: ::c_int = 6;
+
+pub const S_ISUID: ::c_int = 0x800;
+pub const S_ISGID: ::c_int = 0x400;
+pub const S_ISVTX: ::c_int = 0x200;
+
+pub const POLLIN: ::c_short = 0x1;
+pub const POLLPRI: ::c_short = 0x2;
+pub const POLLOUT: ::c_short = 0x4;
+pub const POLLERR: ::c_short = 0x8;
+pub const POLLHUP: ::c_short = 0x10;
+pub const POLLNVAL: ::c_short = 0x20;
+
 cfg_if! {
     if #[cfg(feature = "default")] {
         // cargo build, don't pull in anything extra as the libstd  dep
@@ -105,6 +137,9 @@ cfg_if! {
     } else if #[cfg(target_env = "musl")] {
         #[link(name = "c", kind = "static")]
         extern {}
+    } else if #[cfg(target_os = "emscripten")] {
+        #[link(name = "c")]
+        extern {}
     } else if #[cfg(any(target_os = "macos",
                         target_os = "ios",
                         target_os = "android",
@@ -122,6 +157,7 @@ cfg_if! {
 }
 
 extern {
+    #[cfg_attr(target_os = "netbsd", link_name = "__socket30")]
     pub fn socket(domain: ::c_int, ty: ::c_int, protocol: ::c_int) -> ::c_int;
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
                link_name = "connect$UNIX2003")]
@@ -168,11 +204,13 @@ extern {
     pub fn fchmod(fd: ::c_int, mode: mode_t) -> ::c_int;
 
     #[cfg_attr(target_os = "macos", link_name = "fstat$INODE64")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__fstat50")]
     pub fn fstat(fildes: ::c_int, buf: *mut stat) -> ::c_int;
 
     pub fn mkdir(path: *const c_char, mode: mode_t) -> ::c_int;
 
     #[cfg_attr(target_os = "macos", link_name = "stat$INODE64")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__stat50")]
     pub fn stat(path: *const c_char, buf: *mut stat) -> ::c_int;
 
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
@@ -199,8 +237,10 @@ extern {
                link_name = "opendir$INODE64")]
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
                link_name = "opendir$INODE64$UNIX2003")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__opendir30")]
     pub fn opendir(dirname: *const c_char) -> *mut ::DIR;
     #[cfg_attr(target_os = "macos", link_name = "readdir_r$INODE64")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__readdir_r30")]
     pub fn readdir_r(dirp: *mut ::DIR, entry: *mut ::dirent,
                      result: *mut *mut ::dirent) -> ::c_int;
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
@@ -254,6 +294,9 @@ extern {
                link_name = "pause$UNIX2003")]
     pub fn pause() -> ::c_int;
     pub fn pipe(fds: *mut ::c_int) -> ::c_int;
+    pub fn posix_memalign(memptr: *mut *mut ::c_void,
+                      align: ::size_t,
+                      size: ::size_t) -> ::c_int;
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
                link_name = "read$UNIX2003")]
     pub fn read(fd: ::c_int, buf: *mut ::c_void, count: ::size_t)
@@ -268,6 +311,7 @@ extern {
     pub fn sleep(secs: ::c_uint) -> ::c_uint;
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
                link_name = "nanosleep$UNIX2003")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__nanosleep50")]
     pub fn nanosleep(rqtp: *const timespec,
                      rmtp: *mut timespec) -> ::c_int;
     pub fn tcgetpgrp(fd: ::c_int) -> pid_t;
@@ -292,6 +336,9 @@ extern {
                link_name = "pwrite$UNIX2003")]
     pub fn pwrite(fd: ::c_int, buf: *const ::c_void, count: ::size_t,
                   offset: off_t) -> ::ssize_t;
+    pub fn umask(mask: mode_t) -> mode_t;
+
+    #[cfg_attr(target_os = "netbsd", link_name = "__utime50")]
     pub fn utime(file: *const c_char, buf: *const utimbuf) -> ::c_int;
 
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
@@ -319,6 +366,7 @@ extern {
     pub fn if_nametoindex(ifname: *const c_char) -> ::c_uint;
 
     #[cfg_attr(target_os = "macos", link_name = "lstat$INODE64")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__lstat50")]
     pub fn lstat(path: *const c_char, buf: *mut stat) -> ::c_int;
 
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
@@ -331,6 +379,7 @@ extern {
                   overwrite: ::c_int) -> ::c_int;
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
                link_name = "unsetenv$UNIX2003")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__unsetenv13")]
     pub fn unsetenv(name: *const c_char) -> ::c_int;
 
     pub fn symlink(path1: *const c_char,
@@ -347,6 +396,7 @@ extern {
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
                link_name = "setrlimit$UNIX2003")]
     pub fn setrlimit(resource: ::c_int, rlim: *const rlimit) -> ::c_int;
+    #[cfg_attr(target_os = "netbsd", link_name = "__getrusage50")]
     pub fn getrusage(resource: ::c_int, usage: *mut rusage) -> ::c_int;
 
     pub fn getdtablesize() -> ::c_int;
@@ -357,6 +407,7 @@ extern {
 
     pub fn flock(fd: ::c_int, operation: ::c_int) -> ::c_int;
 
+    #[cfg_attr(arget_os = "netbsd", link_name = "__gettimeofday50")]
     pub fn gettimeofday(tp: *mut ::timeval,
                         tz: *mut ::c_void) -> ::c_int;
 
@@ -376,6 +427,7 @@ extern {
     pub fn pthread_attr_setdetachstate(attr: *mut ::pthread_attr_t,
                                        state: ::c_int) -> ::c_int;
     pub fn pthread_detach(thread: ::pthread_t) -> ::c_int;
+    #[cfg_attr(target_os = "netbsd", link_name = "__libc_thr_yield")]
     pub fn sched_yield() -> ::c_int;
     pub fn pthread_key_create(key: *mut pthread_key_t,
                               dtor: ::dox::Option<unsafe extern fn(*mut ::c_void)>)
@@ -451,6 +503,7 @@ extern {
     pub fn sigaltstack(ss: *const stack_t,
                        oss: *mut stack_t) -> ::c_int;
 
+    #[cfg_attr(target_os = "netbsd", link_name = "__utimes50")]
     pub fn utimes(filename: *const ::c_char,
                   times: *const ::timeval) -> ::c_int;
     pub fn dlopen(filename: *const ::c_char,
@@ -468,11 +521,52 @@ extern {
     pub fn freeaddrinfo(res: *mut addrinfo);
     pub fn gai_strerror(errcode: ::c_int) -> *const ::c_char;
 
+    #[cfg_attr(target_os = "netbsd", link_name = "__gmtime_r50")]
     pub fn gmtime_r(time_p: *const time_t, result: *mut tm) -> *mut tm;
+    #[cfg_attr(target_os = "netbsd", link_name = "__localtime_r50")]
     pub fn localtime_r(time_p: *const time_t, result: *mut tm) -> *mut tm;
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
                link_name = "mktime$UNIX2003")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__mktime50")]
     pub fn mktime(tm: *mut tm) -> time_t;
+
+    #[cfg_attr(target_os = "netbsd", link_name = "__mknod50")]
+    pub fn mknod(pathname: *const ::c_char, mode: ::mode_t,
+                 dev: ::dev_t) -> ::c_int;
+    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
+               link_name = "writev$UNIX2003")]
+    pub fn writev(fd: ::c_int, iov: *const ::iovec, iovcnt: ::c_int) -> ::ssize_t;
+    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
+               link_name = "readv$UNIX2003")]
+    pub fn readv(fd: ::c_int, iov: *const ::iovec, iovcnt: ::c_int) -> ::ssize_t;
+    pub fn uname(buf: *mut ::utsname) -> ::c_int;
+    pub fn daemon(nochdir: ::c_int, noclose: ::c_int) -> ::c_int;
+    pub fn gethostname(name: *mut ::c_char, len: ::size_t) -> ::c_int;
+    pub fn chroot(name: *const ::c_char) -> ::c_int;
+    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
+               link_name = "usleep$UNIX2003")]
+    pub fn usleep(secs: ::c_uint) -> ::c_int;
+    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
+               link_name = "send$UNIX2003")]
+    pub fn send(socket: ::c_int, buf: *const ::c_void, len: ::size_t,
+                flags: ::c_int) -> ::ssize_t;
+    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
+               link_name = "recv$UNIX2003")]
+    pub fn recv(socket: ::c_int, buf: *mut ::c_void, len: ::size_t,
+                flags: ::c_int) -> ::ssize_t;
+    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
+               link_name = "putenv$UNIX2003")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__putenv50")]
+    pub fn putenv(string: *mut c_char) -> ::c_int;
+    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
+               link_name = "sendmsg$UNIX2003")]
+    pub fn sendmsg(fd: ::c_int, msg: *const msghdr, flags: ::c_int) -> ::ssize_t;
+    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
+               link_name = "recvmsg$UNIX2003")]
+    pub fn recvmsg(fd: ::c_int, msg: *mut msghdr, flags: ::c_int) -> ::ssize_t;
+    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
+               link_name = "poll$UNIX2003")]
+    pub fn poll(fds: *mut pollfd, nfds: nfds_t, timeout: ::c_int) -> ::c_int;
 }
 
 // TODO: get rid of this #[cfg(not(...))]
@@ -481,11 +575,13 @@ extern {
     pub fn getifaddrs(ifap: *mut *mut ifaddrs) -> ::c_int;
     pub fn freeifaddrs(ifa: *mut ifaddrs);
     #[cfg_attr(target_os = "macos", link_name = "glob$INODE64")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__glob30")]
     pub fn glob(pattern: *const c_char,
                 flags: ::c_int,
                 errfunc: ::dox::Option<extern "C" fn(epath: *const c_char,
                                                      errno: ::c_int) -> ::c_int>,
                 pglob: *mut glob_t) -> ::c_int;
+    #[cfg_attr(target_os = "netbsd", link_name = "__globfree30")]
     pub fn globfree(pglob: *mut glob_t);
 
     pub fn posix_madvise(addr: *mut ::c_void, len: ::size_t, advice: ::c_int)
@@ -508,9 +604,6 @@ extern {
     pub fn getsid(pid: pid_t) -> pid_t;
     pub fn madvise(addr: *mut ::c_void, len: ::size_t, advice: ::c_int)
                    -> ::c_int;
-    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
-               link_name = "putenv$UNIX2003")]
-    pub fn putenv(string: *mut c_char) -> ::c_int;
     pub fn readlink(path: *const c_char,
                     buf: *mut c_char,
                     bufsz: ::size_t)
@@ -518,43 +611,37 @@ extern {
 
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
                link_name = "msync$UNIX2003")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__msync13")]
     pub fn msync(addr: *mut ::c_void, len: ::size_t, flags: ::c_int) -> ::c_int;
     pub fn sysconf(name: ::c_int) -> c_long;
-    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
-               link_name = "usleep$UNIX2003")]
-    pub fn usleep(secs: ::c_uint) -> ::c_int;
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
                link_name = "recvfrom$UNIX2003")]
     pub fn recvfrom(socket: ::c_int, buf: *mut ::c_void, len: ::size_t,
                     flags: ::c_int, addr: *mut sockaddr,
                     addrlen: *mut socklen_t) -> ::ssize_t;
-    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
-               link_name = "send$UNIX2003")]
-    pub fn send(socket: ::c_int, buf: *const ::c_void, len: ::size_t,
-                flags: ::c_int) -> ::ssize_t;
-    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
-               link_name = "recv$UNIX2003")]
-    pub fn recv(socket: ::c_int, buf: *mut ::c_void, len: ::size_t,
-                flags: ::c_int) -> ::ssize_t;
     pub fn mkfifo(path: *const c_char, mode: mode_t) -> ::c_int;
 
+    #[cfg_attr(target_os = "netbsd", link_name = "__getpwuid_r50")]
     pub fn getpwuid_r(uid: ::uid_t,
                       pwd: *mut passwd,
                       buf: *mut ::c_char,
                       buflen: ::size_t,
                       result: *mut *mut passwd) -> ::c_int;
-    pub fn posix_memalign(memptr: *mut *mut ::c_void,
-                          align: ::size_t,
-                          size: ::size_t) -> ::c_int;
+    #[cfg_attr(target_os = "netbsd", link_name = "__sigemptyset14")]
     pub fn sigemptyset(set: *mut sigset_t) -> ::c_int;
+    #[cfg_attr(target_os = "netbsd", link_name = "__sigaddset14")]
     pub fn sigaddset(set: *mut sigset_t, signum: ::c_int) -> ::c_int;
+    #[cfg_attr(target_os = "netbsd", link_name = "__sigfillset14")]
     pub fn sigfillset(set: *mut sigset_t) -> ::c_int;
+    #[cfg_attr(target_os = "netbsd", link_name = "__sigdelset14")]
     pub fn sigdelset(set: *mut sigset_t, signum: ::c_int) -> ::c_int;
+    #[cfg_attr(target_os = "netbsd", link_name = "__sigismember14")]
     pub fn sigismember(set: *const sigset_t, signum: ::c_int) -> ::c_int;
     #[cfg_attr(all(target_os = "macos", target_arch = "x86_64"),
                link_name = "select$1050")]
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
                link_name = "select$UNIX2003")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__select50")]
     pub fn select(nfds: ::c_int,
                   readfs: *mut fd_set,
                   writefds: *mut fd_set,
@@ -564,6 +651,7 @@ extern {
                link_name = "pselect$1050")]
     #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
                link_name = "pselect$UNIX2003")]
+    #[cfg_attr(target_os = "netbsd", link_name = "__pselect50")]
     pub fn pselect(nfds: ::c_int,
                    readfs: *mut fd_set,
                    writefds: *mut fd_set,
@@ -574,13 +662,30 @@ extern {
                   offset: ::off_t,
                   whence: ::c_int) -> ::c_int;
     pub fn ftello(stream: *mut ::FILE) -> ::off_t;
+    #[cfg_attr(target_os = "netbsd", link_name = "__timegm50")]
     pub fn timegm(tm: *mut ::tm) -> time_t;
     pub fn statvfs(path: *const c_char, buf: *mut statvfs) -> ::c_int;
     pub fn fstatvfs(fd: ::c_int, buf: *mut statvfs) -> ::c_int;
+    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
+               link_name = "tcdrain$UNIX2003")]
+    pub fn tcdrain(fd: ::c_int) -> ::c_int;
+    pub fn cfgetispeed(termios: *const ::termios) -> ::speed_t;
+    pub fn cfgetospeed(termios: *const ::termios) -> ::speed_t;
+    pub fn cfsetispeed(termios: *mut ::termios, speed: ::speed_t) -> ::c_int;
+    pub fn cfsetospeed(termios: *mut ::termios, speed: ::speed_t) -> ::c_int;
+    pub fn tcgetattr(fd: ::c_int, termios: *mut ::termios) -> ::c_int;
+    pub fn tcsetattr(fd: ::c_int,
+                     optional_actions: ::c_int,
+                     termios: *const ::termios) -> ::c_int;
+    pub fn tcflow(fd: ::c_int, action: ::c_int) -> ::c_int;
+    pub fn tcflush(fd: ::c_int, action: ::c_int) -> ::c_int;
+    pub fn tcsendbreak(fd: ::c_int, duration: ::c_int) -> ::c_int;
 }
 
 cfg_if! {
-    if #[cfg(any(target_os = "linux", target_os = "android"))] {
+    if #[cfg(any(target_os = "linux",
+                 target_os = "android",
+                 target_os = "emscripten"))] {
         mod notbsd;
         pub use self::notbsd::*;
     } else if #[cfg(any(target_os = "macos",
index 2c01b25196942094a27a2337f7e235a36e13482d..25558d3d27ea6eb6c2be06fb89d83ef786d44326 100644 (file)
@@ -13,13 +13,16 @@ pub type blkcnt_t = u32;
 pub type blksize_t = u32;
 pub type dev_t = u32;
 pub type mode_t = u16;
-pub type nlink_t = u16;
-pub type useconds_t = i32;
+pub type nlink_t = u32;
+pub type useconds_t = u32;
 pub type socklen_t = i32;
 pub type pthread_t = c_long;
 pub type pthread_mutexattr_t = ::c_long;
 pub type sigset_t = c_ulong;
 pub type time64_t = i64;
+pub type fsfilcnt_t = ::c_ulong;
+pub type fsblkcnt_t = ::c_ulong;
+pub type nfds_t = ::c_uint;
 
 s! {
     pub struct stat {
@@ -28,8 +31,8 @@ s! {
         __st_ino: ::ino_t,
         pub st_mode: ::c_uint,
         pub st_nlink: ::c_uint,
-        pub st_uid: ::c_ulong,
-        pub st_gid: ::c_ulong,
+        pub st_uid: ::uid_t,
+        pub st_gid: ::gid_t,
         pub st_rdev: ::c_ulonglong,
         __pad3: [::c_uchar; 4],
         pub st_size: ::c_longlong,
@@ -96,6 +99,52 @@ s! {
         pub si_code: ::c_int,
         pub _pad: [::c_int; 29],
     }
+
+    pub struct statfs {
+        pub f_type: ::uint32_t,
+        pub f_bsize: ::uint32_t,
+        pub f_blocks: ::uint64_t,
+        pub f_bfree: ::uint64_t,
+        pub f_bavail: ::uint64_t,
+        pub f_files: ::uint64_t,
+        pub f_ffree: ::uint64_t,
+        pub f_fsid: ::__fsid_t,
+        pub f_namelen: ::uint32_t,
+        pub f_frsize: ::uint32_t,
+        pub f_flags: ::uint32_t,
+        pub f_spare: [::uint32_t; 4],
+    }
+
+    pub struct __fsid_t {
+        __val: [::c_int; 2],
+    }
+
+    pub struct msghdr {
+        pub msg_name: *mut ::c_void,
+        pub msg_namelen: ::c_int,
+        pub msg_iov: *mut ::iovec,
+        pub msg_iovlen: ::size_t,
+        pub msg_control: *mut ::c_void,
+        pub msg_controllen: ::size_t,
+        pub msg_flags: ::c_int,
+    }
+
+    pub struct termios {
+        pub c_iflag: ::tcflag_t,
+        pub c_oflag: ::tcflag_t,
+        pub c_cflag: ::tcflag_t,
+        pub c_lflag: ::tcflag_t,
+        pub c_line: ::cc_t,
+        pub c_cc: [::cc_t; ::NCCS],
+    }
+
+    pub struct flock {
+        pub l_type: ::c_short,
+        pub l_whence: ::c_short,
+        pub l_start: ::off_t,
+        pub l_len: ::off_t,
+        pub l_pid: ::pid_t,
+    }
 }
 
 pub const BUFSIZ: ::c_uint = 1024;
@@ -163,6 +212,7 @@ pub const _SC_THREAD_PRIORITY_SCHEDULING: ::c_int = 82;
 pub const _SC_THREAD_PRIO_INHERIT: ::c_int = 83;
 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_STACK_MIN: ::size_t = 8192;
 pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
@@ -190,7 +240,28 @@ pub const SA_NOCLDWAIT: ::c_ulong = 0x00000002;
 
 pub const SIGCHLD: ::c_int = 17;
 pub const SIGBUS: ::c_int = 7;
+pub const SIGUSR1: ::c_int = 10;
+pub const SIGUSR2: ::c_int = 12;
+pub const SIGCONT: ::c_int = 18;
+pub const SIGSTOP: ::c_int = 19;
+pub const SIGTSTP: ::c_int = 20;
+pub const SIGURG: ::c_int = 23;
+pub const SIGIO: ::c_int = 29;
+pub const SIGSYS: ::c_int = 31;
+pub const SIGSTKFLT: ::c_int = 16;
+pub const SIGUNUSED: ::c_int = 31;
+pub const SIGTTIN: ::c_int = 21;
+pub const SIGTTOU: ::c_int = 22;
+pub const SIGXCPU: ::c_int = 24;
+pub const SIGXFSZ: ::c_int = 25;
+pub const SIGVTALRM: ::c_int = 26;
+pub const SIGPROF: ::c_int = 27;
+pub const SIGWINCH: ::c_int = 28;
+pub const SIGPOLL: ::c_int = 29;
+pub const SIGPWR: ::c_int = 30;
 pub const SIG_SETMASK: ::c_int = 2;
+pub const SIG_BLOCK: ::c_int = 0x000000;
+pub const SIG_UNBLOCK: ::c_int = 0x01;
 
 pub const RUSAGE_CHILDREN: ::c_int = -1;
 
@@ -303,6 +374,7 @@ pub const SO_RCVBUF: ::c_int = 8;
 pub const SO_KEEPALIVE: ::c_int = 9;
 pub const SO_OOBINLINE: ::c_int = 10;
 pub const SO_LINGER: ::c_int = 13;
+pub const SO_REUSEPORT: ::c_int = 15;
 pub const SO_RCVLOWAT: ::c_int = 18;
 pub const SO_SNDLOWAT: ::c_int = 19;
 pub const SO_RCVTIMEO: ::c_int = 20;
@@ -315,10 +387,120 @@ pub const O_CREAT: ::c_int = 64;
 pub const O_EXCL: ::c_int = 128;
 pub const O_NOCTTY: ::c_int = 256;
 pub const O_NONBLOCK: ::c_int = 2048;
-pub const O_SYNC: ::c_int = 0x1000;
+pub const O_SYNC: ::c_int = 0x101000;
+pub const O_DIRECT: ::c_int = 0x10000;
+pub const O_DIRECTORY: ::c_int = 0x4000;
+pub const O_NOFOLLOW: ::c_int = 0x8000;
+pub const O_ASYNC: ::c_int = 0x2000;
+pub const O_NDELAY: ::c_int = 0x800;
 
 pub const NI_MAXHOST: ::size_t = 1025;
 
+pub const NCCS: usize = 19;
+pub const TCSBRKP: ::c_int = 0x5425;
+pub const TCSANOW: ::c_int = 0;
+pub const TCSADRAIN: ::c_int = 0x1;
+pub const TCSAFLUSH: ::c_int = 0x2;
+pub const IUTF8: ::tcflag_t = 0x00004000;
+pub const VEOF: usize = 4;
+pub const VEOL: usize = 11;
+pub const VEOL2: usize = 16;
+pub const VMIN: usize = 6;
+pub const IEXTEN: ::tcflag_t = 0x00008000;
+pub const TOSTOP: ::tcflag_t = 0x00000100;
+pub const FLUSHO: ::tcflag_t = 0x00001000;
+
+pub const ADFS_SUPER_MAGIC: ::c_long = 0x0000adf5;
+pub const AFFS_SUPER_MAGIC: ::c_long = 0x0000adff;
+pub const CODA_SUPER_MAGIC: ::c_long = 0x73757245;
+pub const CRAMFS_MAGIC: ::c_long = 0x28cd3d45;
+pub const EFS_SUPER_MAGIC: ::c_long = 0x00414a53;
+pub const EXT2_SUPER_MAGIC: ::c_long = 0x0000ef53;
+pub const EXT3_SUPER_MAGIC: ::c_long = 0x0000ef53;
+pub const EXT4_SUPER_MAGIC: ::c_long = 0x0000ef53;
+pub const HPFS_SUPER_MAGIC: ::c_long = 0xf995e849;
+pub const HUGETLBFS_MAGIC: ::c_long = 0x958458f6;
+pub const ISOFS_SUPER_MAGIC: ::c_long = 0x00009660;
+pub const JFFS2_SUPER_MAGIC: ::c_long = 0x000072b6;
+pub const MINIX_SUPER_MAGIC: ::c_long = 0x0000137f;
+pub const MINIX_SUPER_MAGIC2: ::c_long = 0x0000138f;
+pub const MINIX2_SUPER_MAGIC: ::c_long = 0x00002468;
+pub const MINIX2_SUPER_MAGIC2: ::c_long = 0x00002478;
+pub const MSDOS_SUPER_MAGIC: ::c_long = 0x00004d44;
+pub const NCP_SUPER_MAGIC: ::c_long = 0x0000564c;
+pub const NFS_SUPER_MAGIC: ::c_long = 0x00006969;
+pub const OPENPROM_SUPER_MAGIC: ::c_long = 0x00009fa1;
+pub const PROC_SUPER_MAGIC: ::c_long = 0x00009fa0;
+pub const QNX4_SUPER_MAGIC: ::c_long = 0x0000002f;
+pub const REISERFS_SUPER_MAGIC: ::c_long = 0x52654973;
+pub const SMB_SUPER_MAGIC: ::c_long = 0x0000517b;
+pub const TMPFS_MAGIC: ::c_long = 0x01021994;
+pub const USBDEVICE_SUPER_MAGIC: ::c_long = 0x00009fa2;
+
+pub const MADV_HUGEPAGE: ::c_int = 14;
+pub const MADV_NOHUGEPAGE: ::c_int = 15;
+pub const MAP_HUGETLB: ::c_int = 0x040000;
+
+pub const PTRACE_TRACEME: ::c_int = 0;
+pub const PTRACE_PEEKTEXT: ::c_int = 1;
+pub const PTRACE_PEEKDATA: ::c_int = 2;
+pub const PTRACE_PEEKUSER: ::c_int = 3;
+pub const PTRACE_POKETEXT: ::c_int = 4;
+pub const PTRACE_POKEDATA: ::c_int = 5;
+pub const PTRACE_POKEUSER: ::c_int = 6;
+pub const PTRACE_CONT: ::c_int = 7;
+pub const PTRACE_KILL: ::c_int = 8;
+pub const PTRACE_SINGLESTEP: ::c_int = 9;
+pub const PTRACE_ATTACH: ::c_int = 16;
+pub const PTRACE_DETACH: ::c_int = 17;
+pub const PTRACE_SYSCALL: ::c_int = 24;
+pub const PTRACE_SETOPTIONS: ::c_int = 0x4200;
+pub const PTRACE_GETEVENTMSG: ::c_int = 0x4201;
+pub const PTRACE_GETSIGINFO: ::c_int = 0x4202;
+pub const PTRACE_SETSIGINFO: ::c_int = 0x4203;
+pub const PTRACE_GETFPREGS: ::c_int = 14;
+pub const PTRACE_SETFPREGS: ::c_int = 15;
+pub const PTRACE_GETREGS: ::c_int = 12;
+pub const PTRACE_SETREGS: ::c_int = 13;
+
+pub const EFD_NONBLOCK: ::c_int = 0x800;
+
+pub const F_GETLK: ::c_int = 5;
+pub const F_GETOWN: ::c_int = 9;
+pub const F_SETOWN: ::c_int = 8;
+
+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 TIOCINQ: ::c_int = 0x541B;
+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;
+
 f! {
     pub fn sigemptyset(set: *mut sigset_t) -> ::c_int {
         *set = 0;
@@ -339,13 +521,43 @@ f! {
     pub fn sigismember(set: *const sigset_t, signum: ::c_int) -> ::c_int {
         (*set & (signum as sigset_t)) as ::c_int
     }
+    pub fn cfgetispeed(termios: *const ::termios) -> ::speed_t {
+        (*termios).c_cflag & ::CBAUD
+    }
+    pub fn cfgetospeed(termios: *const ::termios) -> ::speed_t {
+        (*termios).c_cflag & ::CBAUD
+    }
+    pub fn cfsetispeed(termios: *mut ::termios, speed: ::speed_t) -> ::c_int {
+        (*termios).c_cflag = ((*termios).c_cflag & !::CBAUD) | (speed & ::CBAUD);
+        return 0
+    }
+    pub fn cfsetospeed(termios: *mut ::termios, speed: ::speed_t) -> ::c_int {
+        (*termios).c_cflag = ((*termios).c_cflag & !::CBAUD) | (speed & ::CBAUD);
+        return 0
+    }
+    pub fn tcgetattr(fd: ::c_int, termios: *mut ::termios) -> ::c_int {
+        ioctl(fd, ::TCGETS, termios)
+    }
+    pub fn tcsetattr(fd: ::c_int,
+                     optional_actions: ::c_int,
+                     termios: *const ::termios) -> ::c_int {
+        ioctl(fd, optional_actions, termios)
+    }
+    pub fn tcflow(fd: ::c_int, action: ::c_int) -> ::c_int {
+        ioctl(fd, ::TCXONC, action as *mut ::c_void)
+    }
+    pub fn tcflush(fd: ::c_int, action: ::c_int) -> ::c_int {
+        ioctl(fd, ::TCFLSH, action as *mut ::c_void)
+    }
+    pub fn tcsendbreak(fd: ::c_int, duration: ::c_int) -> ::c_int {
+        ioctl(fd, TCSBRKP, duration as *mut ::c_void)
+    }
 }
 
 extern {
     pub fn madvise(addr: *const ::c_void, len: ::size_t, advice: ::c_int)
                    -> ::c_int;
     pub fn ioctl(fd: ::c_int, request: ::c_int, ...) -> ::c_int;
-    pub fn putenv(string: *const ::c_char) -> ::c_int;
     pub fn readlink(path: *const ::c_char,
                     buf: *mut ::c_char,
                     bufsz: ::size_t)
@@ -355,14 +567,9 @@ extern {
     pub fn mprotect(addr: *const ::c_void, len: ::size_t, prot: ::c_int)
                     -> ::c_int;
     pub fn sysconf(name: ::c_int) -> ::c_long;
-    pub fn usleep(secs: ::c_ulong) -> ::c_int;
     pub fn recvfrom(socket: ::c_int, buf: *mut ::c_void, len: ::size_t,
-                    flags: ::c_uint, addr: *const ::sockaddr,
+                    flags: ::c_int, addr: *const ::sockaddr,
                     addrlen: *mut ::socklen_t) -> ::ssize_t;
-    pub fn send(socket: ::c_int, buf: *const ::c_void, len: ::size_t,
-                flags: ::c_uint) -> ::ssize_t;
-    pub fn recv(socket: ::c_int, buf: *mut ::c_void, len: ::size_t,
-                flags: ::c_uint) -> ::ssize_t;
     pub fn getnameinfo(sa: *const ::sockaddr,
                        salen: ::socklen_t,
                        host: *mut ::c_char,
@@ -371,6 +578,8 @@ extern {
                        sevlen: ::size_t,
                        flags: ::c_int) -> ::c_int;
     pub fn timegm64(tm: *const ::tm) -> time64_t;
+    pub fn eventfd(init: ::c_uint, flags: ::c_int) -> ::c_int;
+    pub fn ptrace(request: ::c_int, ...) -> ::c_long;
 }
 
 cfg_if! {
index 8045119f9010f13853bf62945222d605674b9337..ebf2d230f7ee10a3f9b9d1d7382edd675c4071e6 100644 (file)
@@ -63,7 +63,7 @@ s! {
     }
 
     pub struct sigaction {
-        pub sa_flags: ::c_uint,
+        pub sa_flags: ::c_int,
         pub sa_sigaction: ::sighandler_t,
         pub sa_mask: sigset_t,
         _restorer: *mut ::c_void,
@@ -99,6 +99,76 @@ s! {
         __unused4: *mut ::c_void,
         __unused5: *mut ::c_void,
     }
+
+    pub struct ipc_perm {
+        pub __key: ::key_t,
+        pub uid: ::uid_t,
+        pub gid: ::gid_t,
+        pub cuid: ::uid_t,
+        pub cgid: ::gid_t,
+        pub mode: ::c_uint,
+        pub __seq: ::c_ushort,
+        __pad1: ::c_ushort,
+        __unused1: ::c_ulong,
+        __unused2: ::c_ulong
+    }
+
+    pub struct shmid_ds {
+        pub shm_perm: ::ipc_perm,
+        pub shm_segsz: ::size_t,
+        pub shm_atime: ::time_t,
+        pub shm_dtime: ::time_t,
+        pub shm_ctime: ::time_t,
+        pub shm_cpid: ::pid_t,
+        pub shm_lpid: ::pid_t,
+        pub shm_nattch: ::shmatt_t,
+        __unused4: ::c_ulong,
+        __unused5: ::c_ulong
+    }
+
+    pub struct statfs {
+        pub f_type: ::c_long,
+        pub f_bsize: ::c_long,
+        pub f_frsize: ::c_long,
+        pub f_blocks: ::fsblkcnt_t,
+        pub f_bfree: ::fsblkcnt_t,
+        pub f_files: ::fsblkcnt_t,
+        pub f_ffree: ::fsblkcnt_t,
+        pub f_bavail: ::fsblkcnt_t,
+        pub f_fsid: ::fsid_t,
+
+        pub f_namelen: ::c_long,
+        f_spare: [::c_long; 6],
+    }
+
+    pub struct msghdr {
+        pub msg_name: *mut ::c_void,
+        pub msg_namelen: ::socklen_t,
+        pub msg_iov: *mut ::iovec,
+        pub msg_iovlen: ::size_t,
+        pub msg_control: *mut ::c_void,
+        pub msg_controllen: ::size_t,
+        pub msg_flags: ::c_int,
+    }
+
+    pub struct termios {
+        pub c_iflag: ::tcflag_t,
+        pub c_oflag: ::tcflag_t,
+        pub c_cflag: ::tcflag_t,
+        pub c_lflag: ::tcflag_t,
+        pub c_line: ::cc_t,
+        pub c_cc: [::cc_t; ::NCCS],
+    }
+
+    pub struct flock {
+        pub l_type: ::c_short,
+        pub l_whence: ::c_short,
+        pub l_start: ::off_t,
+        pub l_len: ::off_t,
+        pub l_sysid: ::c_long,
+        pub l_pid: ::pid_t,
+        pad: [::c_long; 4],
+    }
 }
 
 pub const BUFSIZ: ::c_uint = 8192;
@@ -108,6 +178,9 @@ pub const POSIX_MADV_DONTNEED: ::c_int = 4;
 pub const _SC_2_C_VERSION: ::c_int = 96;
 pub const RUSAGE_THREAD: ::c_int = 1;
 pub const O_ACCMODE: ::c_int = 3;
+pub const O_DIRECT: ::c_int = 0x8000;
+pub const O_DIRECTORY: ::c_int = 0x10000;
+pub const O_NOFOLLOW: ::c_int = 0x20000;
 pub const RUSAGE_CHILDREN: ::c_int = -1;
 pub const ST_RELATIME: ::c_ulong = 4096;
 pub const NI_MAXHOST: ::socklen_t = 1025;
@@ -117,7 +190,7 @@ pub const RLIMIT_AS: ::c_int = 6;
 pub const RLIMIT_RSS: ::c_int = 7;
 pub const RLIMIT_NPROC: ::c_int = 8;
 pub const RLIMIT_MEMLOCK: ::c_int = 9;
-pub const RLIMIT_NLIMITS: ::c_int = 15;
+pub const RLIMIT_NLIMITS: ::c_int = 16;
 pub const RLIM_INFINITY: ::rlim_t = 0x7fffffff;
 
 pub const O_APPEND: ::c_int = 8;
@@ -125,9 +198,12 @@ pub const O_CREAT: ::c_int = 256;
 pub const O_EXCL: ::c_int = 1024;
 pub const O_NOCTTY: ::c_int = 2048;
 pub const O_NONBLOCK: ::c_int = 128;
-pub const O_SYNC: ::c_int = 0x10;
-pub const O_RSYNC: ::c_int = 0x10;
+pub const O_SYNC: ::c_int = 0x4010;
+pub const O_RSYNC: ::c_int = 0x4010;
 pub const O_DSYNC: ::c_int = 0x10;
+pub const O_FSYNC: ::c_int = 0x4010;
+pub const O_ASYNC: ::c_int = 0x1000;
+pub const O_NDELAY: ::c_int = 0x80;
 
 pub const EDEADLK: ::c_int = 45;
 pub const ENAMETOOLONG: ::c_int = 78;
@@ -251,16 +327,140 @@ pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 4;
 pub const FIOCLEX: ::c_ulong = 0x6601;
 pub const FIONBIO: ::c_ulong = 0x667e;
 
-pub const SA_ONSTACK: ::c_uint = 0x08000000;
-pub const SA_SIGINFO: ::c_uint = 0x00000008;
-pub const SA_NOCLDWAIT: ::c_uint = 0x00010000;
+pub const SA_ONSTACK: ::c_int = 0x08000000;
+pub const SA_SIGINFO: ::c_int = 0x00000008;
+pub const SA_NOCLDWAIT: ::c_int = 0x00010000;
 
 pub const SIGCHLD: ::c_int = 18;
 pub const SIGBUS: ::c_int = 10;
-
+pub const SIGTTIN: ::c_int = 26;
+pub const SIGTTOU: ::c_int = 27;
+pub const SIGXCPU: ::c_int = 30;
+pub const SIGXFSZ: ::c_int = 31;
+pub const SIGVTALRM: ::c_int = 28;
+pub const SIGPROF: ::c_int = 29;
+pub const SIGWINCH: ::c_int = 20;
+pub const SIGUSR1: ::c_int = 16;
+pub const SIGUSR2: ::c_int = 17;
+pub const SIGCONT: ::c_int = 25;
+pub const SIGSTOP: ::c_int = 23;
+pub const SIGTSTP: ::c_int = 24;
+pub const SIGURG: ::c_int = 21;
+pub const SIGIO: ::c_int = 22;
+pub const SIGSYS: ::c_int = 12;
+pub const SIGPOLL: ::c_int = 22;
+pub const SIGPWR: ::c_int = 19;
 pub const SIG_SETMASK: ::c_int = 3;
+pub const SIG_BLOCK: ::c_int = 0x1;
+pub const SIG_UNBLOCK: ::c_int = 0x2;
+
 pub const PTHREAD_STACK_MIN: ::size_t = 131072;
 
+pub const ADFS_SUPER_MAGIC: ::c_long = 0x0000adf5;
+pub const AFFS_SUPER_MAGIC: ::c_long = 0x0000adff;
+pub const CODA_SUPER_MAGIC: ::c_long = 0x73757245;
+pub const CRAMFS_MAGIC: ::c_long = 0x28cd3d45;
+pub const EFS_SUPER_MAGIC: ::c_long = 0x00414a53;
+pub const EXT2_SUPER_MAGIC: ::c_long = 0x0000ef53;
+pub const EXT3_SUPER_MAGIC: ::c_long = 0x0000ef53;
+pub const EXT4_SUPER_MAGIC: ::c_long = 0x0000ef53;
+pub const HPFS_SUPER_MAGIC: ::c_long = 0xf995e849;
+pub const HUGETLBFS_MAGIC: ::c_long = 0x958458f6;
+pub const ISOFS_SUPER_MAGIC: ::c_long = 0x00009660;
+pub const JFFS2_SUPER_MAGIC: ::c_long = 0x000072b6;
+pub const MINIX_SUPER_MAGIC: ::c_long = 0x0000137f;
+pub const MINIX_SUPER_MAGIC2: ::c_long = 0x0000138f;
+pub const MINIX2_SUPER_MAGIC: ::c_long = 0x00002468;
+pub const MINIX2_SUPER_MAGIC2: ::c_long = 0x00002478;
+pub const MSDOS_SUPER_MAGIC: ::c_long = 0x00004d44;
+pub const NCP_SUPER_MAGIC: ::c_long = 0x0000564c;
+pub const NFS_SUPER_MAGIC: ::c_long = 0x00006969;
+pub const OPENPROM_SUPER_MAGIC: ::c_long = 0x00009fa1;
+pub const PROC_SUPER_MAGIC: ::c_long = 0x00009fa0;
+pub const QNX4_SUPER_MAGIC: ::c_long = 0x0000002f;
+pub const REISERFS_SUPER_MAGIC: ::c_long = 0x52654973;
+pub const SMB_SUPER_MAGIC: ::c_long = 0x0000517b;
+pub const TMPFS_MAGIC: ::c_long = 0x01021994;
+pub const USBDEVICE_SUPER_MAGIC: ::c_long = 0x00009fa2;
+
+pub const VEOF: usize = 16;
+pub const VEOL: usize = 17;
+pub const VEOL2: usize = 6;
+pub const VMIN: usize = 4;
+pub const IEXTEN: ::tcflag_t = 0x00000100;
+pub const TOSTOP: ::tcflag_t = 0x00008000;
+pub const FLUSHO: ::tcflag_t = 0x00002000;
+pub const IUTF8: ::tcflag_t = 0x00004000;
+pub const TCSANOW: ::c_int = 0x540e;
+pub const TCSADRAIN: ::c_int = 0x540f;
+pub const TCSAFLUSH: ::c_int = 0x5410;
+
+pub const CPU_SETSIZE: ::c_int = 0x400;
+
+pub const PTRACE_TRACEME: ::c_uint = 0;
+pub const PTRACE_PEEKTEXT: ::c_uint = 1;
+pub const PTRACE_PEEKDATA: ::c_uint = 2;
+pub const PTRACE_PEEKUSER: ::c_uint = 3;
+pub const PTRACE_POKETEXT: ::c_uint = 4;
+pub const PTRACE_POKEDATA: ::c_uint = 5;
+pub const PTRACE_POKEUSER: ::c_uint = 6;
+pub const PTRACE_CONT: ::c_uint = 7;
+pub const PTRACE_KILL: ::c_uint = 8;
+pub const PTRACE_SINGLESTEP: ::c_uint = 9;
+pub const PTRACE_ATTACH: ::c_uint = 16;
+pub const PTRACE_DETACH: ::c_uint = 17;
+pub const PTRACE_SYSCALL: ::c_uint = 24;
+pub const PTRACE_SETOPTIONS: ::c_uint = 0x4200;
+pub const PTRACE_GETEVENTMSG: ::c_uint = 0x4201;
+pub const PTRACE_GETSIGINFO: ::c_uint = 0x4202;
+pub const PTRACE_SETSIGINFO: ::c_uint = 0x4203;
+pub const PTRACE_GETFPREGS: ::c_uint = 14;
+pub const PTRACE_SETFPREGS: ::c_uint = 15;
+pub const PTRACE_GETFPXREGS: ::c_uint = 18;
+pub const PTRACE_SETFPXREGS: ::c_uint = 19;
+pub const PTRACE_GETREGS: ::c_uint = 12;
+pub const PTRACE_SETREGS: ::c_uint = 13;
+
+pub const EFD_NONBLOCK: ::c_int = 0x80;
+
+pub const F_GETLK: ::c_int = 14;
+pub const F_GETOWN: ::c_int = 23;
+pub const F_SETOWN: ::c_int = 24;
+
+pub const SFD_NONBLOCK: ::c_int = 0x80;
+
+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 TIOCINQ: ::c_ulong = 0x467f;
+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 = 0x741a;
+pub const FIONREAD: ::c_ulong = 0x467f;
+pub const TIOCCONS: ::c_ulong = 0x80047478;
+
 extern {
     pub fn sysctl(name: *mut ::c_int,
                   namelen: ::c_int,
@@ -284,5 +484,7 @@ extern {
                        hostlen: ::socklen_t,
                        serv: *mut ::c_char,
                        sevlen: ::socklen_t,
-                       flags: ::c_uint) -> ::c_int;
+                       flags: ::c_int) -> ::c_int;
+    pub fn eventfd(init: ::c_uint, flags: ::c_int) -> ::c_int;
+    pub fn ptrace(request: ::c_uint, ...) -> ::c_long;
 }
index e584353977a8cc099de38d332290c73394b94fe5..3787f4bc71532039e21d5cd3c38868854dea6609 100644 (file)
@@ -11,6 +11,10 @@ pub type blkcnt64_t = i64;
 pub type rlim64_t = u64;
 pub type fsblkcnt_t = ::c_ulong;
 pub type fsfilcnt_t = ::c_ulong;
+pub type key_t = ::c_int;
+pub type shmatt_t = ::c_ulong;
+pub type mqd_t = ::c_int;
+pub type nfds_t = ::c_ulong;
 
 pub enum fpos64_t {} // TODO: fill this out with a struct
 
@@ -80,9 +84,11 @@ s! {
     }
 
     pub struct pthread_mutexattr_t {
-        #[cfg(target_arch = "x86_64")]
+        #[cfg(any(target_arch = "x86_64", target_arch = "powerpc64",
+                  target_arch = "powerpc64le"))]
         __align: [::c_int; 0],
-        #[cfg(not(target_arch = "x86_64"))]
+        #[cfg(not(any(target_arch = "x86_64", target_arch = "powerpc64",
+                      target_arch = "powerpc64le")))]
         __align: [::c_long; 0],
         size: [u8; __SIZEOF_PTHREAD_MUTEXATTR_T],
     }
@@ -125,6 +131,57 @@ s! {
         pub nl_pid: u32,
         pub nl_groups: u32
     }
+
+    pub struct dqblk {
+        pub dqb_bhardlimit: ::uint64_t,
+        pub dqb_bsoftlimit: ::uint64_t,
+        pub dqb_curspace: ::uint64_t,
+        pub dqb_ihardlimit: ::uint64_t,
+        pub dqb_isoftlimit: ::uint64_t,
+        pub dqb_curinodes: ::uint64_t,
+        pub dqb_btime: ::uint64_t,
+        pub dqb_itime: ::uint64_t,
+        pub dqb_valid: ::uint32_t,
+    }
+
+    pub struct signalfd_siginfo {
+        pub ssi_signo: ::uint32_t,
+        pub ssi_errno: ::int32_t,
+        pub ssi_code: ::int32_t,
+        pub ssi_pid: ::uint32_t,
+        pub ssi_uid: ::uint32_t,
+        pub ssi_fd: ::int32_t,
+        pub ssi_tid: ::uint32_t,
+        pub ssi_band: ::uint32_t,
+        pub ssi_overrun: ::uint32_t,
+        pub ssi_trapno: ::uint32_t,
+        pub ssi_status: ::int32_t,
+        pub ssi_int: ::int32_t,
+        pub ssi_ptr: ::uint64_t,
+        pub ssi_utime: ::uint64_t,
+        pub ssi_stime: ::uint64_t,
+        pub ssi_addr: ::uint64_t,
+        _pad: [::uint8_t; 48],
+    }
+
+    pub struct fsid_t {
+        __val: [::c_int; 2],
+    }
+
+    pub struct mq_attr {
+        pub mq_flags: ::c_long,
+        pub mq_maxmsg: ::c_long,
+        pub mq_msgsize: ::c_long,
+        pub mq_curmsgs: ::c_long,
+        pad: [::c_long; 4]
+    }
+
+    pub struct cpu_set_t {
+        #[cfg(target_pointer_width = "32")]
+        bits: [u32; 32],
+        #[cfg(target_pointer_width = "64")]
+        bits: [u64; 16],
+    }
 }
 
 pub const FILENAME_MAX: ::c_uint = 4096;
@@ -256,6 +313,9 @@ pub const ST_IMMUTABLE: ::c_ulong = 512;
 pub const ST_NOATIME: ::c_ulong = 1024;
 pub const ST_NODIRATIME: ::c_ulong = 2048;
 
+pub const RTLD_NEXT: *mut ::c_void = -1i64 as *mut ::c_void;
+pub const RTLD_DEFAULT: *mut ::c_void = 0i64 as *mut ::c_void;
+
 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
 pub const MAP_32BIT: ::c_int = 0x0040;
 
@@ -278,9 +338,66 @@ pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
 pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 1;
 pub const __SIZEOF_PTHREAD_COND_T: usize = 48;
 
+pub const SCHED_OTHER: ::c_int = 0;
+pub const SCHED_FIFO: ::c_int = 1;
+pub const SCHED_RR: ::c_int = 2;
+pub const SCHED_BATCH: ::c_int = 3;
+pub const SCHED_IDLE: ::c_int = 5;
+
+pub const IPC_CREAT: ::c_int = 0o1000;
+pub const IPC_EXCL: ::c_int = 0o2000;
+pub const IPC_NOWAIT: ::c_int = 0o4000;
+
+pub const IPC_RMID: ::c_int = 0;
+pub const IPC_SET: ::c_int = 1;
+pub const IPC_STAT: ::c_int = 2;
+pub const IPC_INFO: ::c_int = 3;
+
+pub const SHM_R: ::c_int = 0o400;
+pub const SHM_W: ::c_int = 0o200;
+
+pub const SHM_RDONLY: ::c_int = 0o10000;
+pub const SHM_RND: ::c_int = 0o20000;
+pub const SHM_REMAP: ::c_int = 0o40000;
+pub const SHM_EXEC: ::c_int = 0o100000;
+
+pub const SHM_LOCK: ::c_int = 11;
+pub const SHM_UNLOCK: ::c_int = 12;
+
+pub const SHM_HUGETLB: ::c_int = 0o4000;
+pub const SHM_NORESERVE: ::c_int = 0o10000;
+
+pub const MS_RELATIME: ::c_ulong = 0x200000;
+pub const MS_KERNMOUNT: ::c_ulong = 0x400000;
+pub const MS_I_VERSION: ::c_ulong = 0x800000;
+pub const MS_STRICTATIME: ::c_ulong = 0x01000000;
+
+pub const EPOLLRDHUP: ::c_int = 0x2000;
+pub const EPOLLONESHOT: ::c_int = 0x40000000;
+
+pub const QFMT_VFS_OLD: ::c_int = 1;
+pub const QFMT_VFS_V0: ::c_int = 2;
+
+pub const SFD_CLOEXEC: ::c_int = 0x080000;
+
+pub const EFD_SEMAPHORE: ::c_int = 0x1;
+
+pub const NCCS: usize = 32;
+
+pub const CLONE_NEWUTS: ::c_uint = 0x04000000;
+pub const CLONE_NEWIPC: ::c_uint = 0x08000000;
+pub const CLONE_NEWUSER: ::c_uint = 0x10000000;
+pub const CLONE_NEWPID: ::c_uint = 0x20000000;
+pub const CLONE_NEWNET: ::c_uint = 0x40000000;
+pub const CLONE_IO: ::c_uint = 0x80000000;
+
 extern {
     pub fn shm_open(name: *const c_char, oflag: ::c_int,
                     mode: mode_t) -> ::c_int;
+    pub fn shmget(key: ::key_t, size: ::size_t, shmflg: ::c_int) -> ::c_int;
+    pub fn shmat(shmid: ::c_int, shmaddr: *const ::c_void, shmflg: ::c_int) -> *mut ::c_void;
+    pub fn shmdt(shmaddr: *const ::c_void) -> ::c_int;
+    pub fn shmctl(shmid: ::c_int, cmd: ::c_int, buf: *mut ::shmid_ds) -> ::c_int;
     pub fn mprotect(addr: *mut ::c_void, len: ::size_t, prot: ::c_int)
                     -> ::c_int;
     pub fn __errno_location() -> *mut ::c_int;
@@ -325,10 +442,79 @@ extern {
                            len: ::off_t) -> ::c_int;
     pub fn readahead(fd: ::c_int, offset: ::off64_t,
                      count: ::size_t) -> ::ssize_t;
+    pub fn getxattr(path: *const c_char, name: *const c_char,
+                    value: *mut ::c_void, size: ::size_t) -> ::ssize_t;
+    pub fn lgetxattr(path: *const c_char, name: *const c_char,
+                     value: *mut ::c_void, size: ::size_t) -> ::ssize_t;
+    pub fn fgetxattr(filedes: ::c_int, name: *const c_char,
+                     value: *mut ::c_void, size: ::size_t) -> ::ssize_t;
+    pub fn setxattr(path: *const c_char, name: *const c_char,
+                    value: *const ::c_void, size: ::size_t,
+                    flags: ::c_int) -> ::c_int;
+    pub fn lsetxattr(path: *const c_char, name: *const c_char,
+                     value: *const ::c_void, size: ::size_t,
+                     flags: ::c_int) -> ::c_int;
+    pub fn fsetxattr(filedes: ::c_int, name: *const c_char,
+                     value: *const ::c_void, size: ::size_t,
+                     flags: ::c_int) -> ::c_int;
+    pub fn listxattr(path: *const c_char, list: *mut c_char,
+                     size: ::size_t) -> ::ssize_t;
+    pub fn llistxattr(path: *const c_char, list: *mut c_char,
+                      size: ::size_t) -> ::ssize_t;
+    pub fn flistxattr(filedes: ::c_int, list: *mut c_char,
+                      size: ::size_t) -> ::ssize_t;
+    pub fn removexattr(path: *const c_char, name: *const c_char) -> ::c_int;
+    pub fn lremovexattr(path: *const c_char, name: *const c_char) -> ::c_int;
+    pub fn fremovexattr(filedes: ::c_int, name: *const c_char) -> ::c_int;
+    pub fn signalfd(fd: ::c_int,
+                    mask: *const ::sigset_t,
+                    flags: ::c_int) -> ::c_int;
+    pub fn pwritev(fd: ::c_int,
+                   iov: *const ::iovec,
+                   iovcnt: ::c_int,
+                   offset: ::off_t) -> ::ssize_t;
+    pub fn preadv(fd: ::c_int,
+                  iov: *const ::iovec,
+                  iovcnt: ::c_int,
+                  offset: ::off_t) -> ::ssize_t;
+    pub fn quotactl(cmd: ::c_int,
+                    special: *const ::c_char,
+                    id: ::c_int,
+                    data: *mut ::c_char) -> ::c_int;
+    pub fn mq_open(name: *const ::c_char, oflag: ::c_int, ...) -> ::mqd_t;
+    pub fn mq_close(mqd: ::mqd_t) -> ::c_int;
+    pub fn mq_unlink(name: *const ::c_char) -> ::c_int;
+    pub fn mq_receive(mqd: ::mqd_t,
+                      msg_ptr: *mut ::c_char,
+                      msg_len: ::size_t,
+                      msq_prio: *mut ::c_uint) -> ::ssize_t;
+    pub fn mq_send(mqd: ::mqd_t,
+                   msg_ptr: *const ::c_char,
+                   msg_len: ::size_t,
+                   msq_prio: ::c_uint) -> ::c_int;
+    pub fn mq_getattr(mqd: ::mqd_t, attr: *mut ::mq_attr) -> ::c_int;
+    pub fn mq_setattr(mqd: ::mqd_t,
+                      newattr: *const ::mq_attr,
+                      oldattr: *mut ::mq_attr) -> ::c_int;
+    pub fn sched_getaffinity(pid: ::pid_t,
+                             cpusetsize: ::size_t,
+                             cpuset: *mut cpu_set_t) -> ::c_int;
+    pub fn sched_setaffinity(pid: ::pid_t,
+                             cpusetsize: ::size_t,
+                             cpuset: *const cpu_set_t) -> ::c_int;
+    pub fn epoll_pwait(epfd: ::c_int,
+                       events: *mut ::epoll_event,
+                       maxevents: ::c_int,
+                       timeout: ::c_int,
+                       sigmask: *const ::sigset_t) -> ::c_int;
+    pub fn dup3(oldfd: ::c_int, newfd: ::c_int, flags: ::c_int) -> ::c_int;
+    pub fn unshare(flags: ::c_int) -> ::c_int;
+    pub fn sethostname(name: *const ::c_char, len: ::size_t) -> ::c_int;
 }
 
 cfg_if! {
-    if #[cfg(target_env = "musl")] {
+    if #[cfg(any(target_env = "musl",
+                 target_os = "emscripten"))] {
         mod musl;
         pub use self::musl::*;
     } else if #[cfg(any(target_arch = "mips", target_arch = "mipsel"))] {
index e8a643aa3ddf21d6ec49cb07b2102dc7e26a5665..3c1d7b6d2303d39d7e35ae79d55ed84970bc8c98 100644 (file)
@@ -82,6 +82,77 @@ s! {
         pub _pad: [::c_int; 29],
         _align: [usize; 0],
     }
+
+    pub struct ipc_perm {
+        pub __ipc_perm_key: ::key_t,
+        pub uid: ::uid_t,
+        pub gid: ::gid_t,
+        pub cuid: ::uid_t,
+        pub cgid: ::gid_t,
+        pub mode: ::mode_t,
+        pub __seq: ::c_int,
+        __unused1: ::c_long,
+        __unused2: ::c_long
+    }
+
+    pub struct shmid_ds {
+        pub shm_perm: ::ipc_perm,
+        pub shm_segsz: ::size_t,
+        pub shm_atime: ::time_t,
+        pub shm_dtime: ::time_t,
+        pub shm_ctime: ::time_t,
+        pub shm_cpid: ::pid_t,
+        pub shm_lpid: ::pid_t,
+        pub shm_nattch: ::c_ulong,
+        __pad1: ::c_ulong,
+        __pad2: ::c_ulong,
+    }
+
+    pub struct statfs {
+        pub f_type: ::c_ulong,
+        pub f_bsize: ::c_ulong,
+        pub f_blocks: ::fsblkcnt_t,
+        pub f_bfree: ::fsblkcnt_t,
+        pub f_bavail: ::fsblkcnt_t,
+        pub f_files: ::fsfilcnt_t,
+        pub f_ffree: ::fsfilcnt_t,
+        pub f_fsid: ::fsid_t,
+        pub f_namelen: ::c_ulong,
+        pub f_frsize: ::c_ulong,
+        pub f_flags: ::c_ulong,
+        pub f_spare: [::c_ulong; 4],
+    }
+
+    pub struct msghdr {
+        pub msg_name: *mut ::c_void,
+        pub msg_namelen: ::socklen_t,
+        pub msg_iov: *mut ::iovec,
+        pub msg_iovlen: ::c_int,
+        __pad1: ::c_int,
+        pub msg_control: *mut ::c_void,
+        pub msg_controllen: ::socklen_t,
+        __pad2: ::socklen_t,
+        pub msg_flags: ::c_int,
+    }
+
+    pub struct termios {
+        pub c_iflag: ::tcflag_t,
+        pub c_oflag: ::tcflag_t,
+        pub c_cflag: ::tcflag_t,
+        pub c_lflag: ::tcflag_t,
+        pub c_line: ::cc_t,
+        pub c_cc: [::cc_t; ::NCCS],
+        pub __c_ispeed: ::speed_t,
+        pub __c_ospeed: ::speed_t,
+    }
+
+    pub struct flock {
+        pub l_type: ::c_short,
+        pub l_whence: ::c_short,
+        pub l_start: ::off_t,
+        pub l_len: ::off_t,
+        pub l_pid: ::pid_t,
+    }
 }
 
 pub const BUFSIZ: ::c_uint = 1024;
@@ -89,6 +160,11 @@ pub const TMP_MAX: ::c_uint = 10000;
 pub const FOPEN_MAX: ::c_uint = 1000;
 pub const POSIX_MADV_DONTNEED: ::c_int = 0;
 pub const O_ACCMODE: ::c_int = 0o10000003;
+pub const O_DIRECT: ::c_int = 0x4000;
+pub const O_DIRECTORY: ::c_int = 0x10000;
+pub const O_NOFOLLOW: ::c_int = 0x20000;
+pub const O_ASYNC: ::c_int = 0x2000;
+pub const O_NDELAY: ::c_int = 0x800;
 pub const RUSAGE_CHILDREN: ::c_int = 1;
 pub const NI_MAXHOST: ::socklen_t = 255;
 pub const PTHREAD_STACK_MIN: ::size_t = 2048;
@@ -249,7 +325,28 @@ pub const SA_NOCLDWAIT: ::c_int = 0x00000002;
 
 pub const SIGCHLD: ::c_int = 17;
 pub const SIGBUS: ::c_int = 7;
+pub const SIGTTIN: ::c_int = 21;
+pub const SIGTTOU: ::c_int = 22;
+pub const SIGXCPU: ::c_int = 24;
+pub const SIGXFSZ: ::c_int = 25;
+pub const SIGVTALRM: ::c_int = 26;
+pub const SIGPROF: ::c_int = 27;
+pub const SIGWINCH: ::c_int = 28;
+pub const SIGUSR1: ::c_int = 10;
+pub const SIGUSR2: ::c_int = 12;
+pub const SIGCONT: ::c_int = 18;
+pub const SIGSTOP: ::c_int = 19;
+pub const SIGTSTP: ::c_int = 20;
+pub const SIGURG: ::c_int = 23;
+pub const SIGIO: ::c_int = 29;
+pub const SIGSYS: ::c_int = 31;
+pub const SIGSTKFLT: ::c_int = 16;
+pub const SIGUNUSED: ::c_int = 31;
+pub const SIGPOLL: ::c_int = 29;
+pub const SIGPWR: ::c_int = 30;
 pub const SIG_SETMASK: ::c_int = 2;
+pub const SIG_BLOCK: ::c_int = 0x000000;
+pub const SIG_UNBLOCK: ::c_int = 0x01;
 
 pub const FALLOC_FL_KEEP_SIZE: ::c_int = 0x01;
 pub const FALLOC_FL_PUNCH_HOLE: ::c_int = 0x02;
@@ -260,6 +357,104 @@ pub const __SIZEOF_PTHREAD_RWLOCK_T: usize = 56;
 pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 40;
 pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 4;
 
+pub const CPU_SETSIZE: ::c_int = 128;
+
+pub const EXTPROC: ::tcflag_t = 0x00010000;
+
+pub const QFMT_VFS_V1: ::c_int = 4;
+
+pub const PTRACE_TRACEME: ::c_int = 0;
+pub const PTRACE_PEEKTEXT: ::c_int = 1;
+pub const PTRACE_PEEKDATA: ::c_int = 2;
+pub const PTRACE_PEEKUSER: ::c_int = 3;
+pub const PTRACE_POKETEXT: ::c_int = 4;
+pub const PTRACE_POKEDATA: ::c_int = 5;
+pub const PTRACE_POKEUSER: ::c_int = 6;
+pub const PTRACE_CONT: ::c_int = 7;
+pub const PTRACE_KILL: ::c_int = 8;
+pub const PTRACE_SINGLESTEP: ::c_int = 9;
+pub const PTRACE_ATTACH: ::c_int = 16;
+pub const PTRACE_DETACH: ::c_int = 17;
+pub const PTRACE_SYSCALL: ::c_int = 24;
+pub const PTRACE_SETOPTIONS: ::c_int = 0x4200;
+pub const PTRACE_GETEVENTMSG: ::c_int = 0x4201;
+pub const PTRACE_GETSIGINFO: ::c_int = 0x4202;
+pub const PTRACE_SETSIGINFO: ::c_int = 0x4203;
+pub const PTRACE_GETREGSET: ::c_int = 0x4204;
+pub const PTRACE_SETREGSET: ::c_int = 0x4205;
+pub const PTRACE_SEIZE: ::c_int = 0x4206;
+pub const PTRACE_INTERRUPT: ::c_int = 0x4207;
+pub const PTRACE_LISTEN: ::c_int = 0x4208;
+pub const PTRACE_PEEKSIGINFO: ::c_int = 0x4209;
+
+pub const MADV_DODUMP: ::c_int = 17;
+pub const MADV_DONTDUMP: ::c_int = 16;
+
+pub const EPOLLWAKEUP: ::c_int = 0x20000000;
+
+pub const MADV_HUGEPAGE: ::c_int = 14;
+pub const MADV_NOHUGEPAGE: ::c_int = 15;
+pub const MAP_HUGETLB: ::c_int = 0x040000;
+
+pub const PTRACE_GETFPREGS: ::c_uint = 14;
+pub const PTRACE_SETFPREGS: ::c_uint = 15;
+pub const PTRACE_GETFPXREGS: ::c_uint = 18;
+pub const PTRACE_SETFPXREGS: ::c_uint = 19;
+pub const PTRACE_GETREGS: ::c_uint = 12;
+pub const PTRACE_SETREGS: ::c_uint = 13;
+
+pub const EFD_NONBLOCK: ::c_int = 0x800;
+
+pub const F_GETLK: ::c_int = 5;
+pub const F_GETOWN: ::c_int = 9;
+pub const F_SETOWN: ::c_int = 8;
+
+pub const VEOF: usize = 4;
+pub const VEOL: usize = 11;
+pub const VEOL2: usize = 16;
+pub const VMIN: usize = 6;
+pub const IEXTEN: ::tcflag_t = 0x00008000;
+pub const TOSTOP: ::tcflag_t = 0x00000100;
+pub const FLUSHO: ::tcflag_t = 0x00001000;
+
+pub const SFD_NONBLOCK: ::c_int = 0x0800;
+
+pub const TCSANOW: ::c_int = 0;
+pub const TCSADRAIN: ::c_int = 1;
+pub const TCSAFLUSH: ::c_int = 2;
+
+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 TIOCINQ: ::c_ulong = 0x541B;
+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;
+
 extern {
     pub fn getnameinfo(sa: *const ::sockaddr,
                        salen: ::socklen_t,
@@ -269,4 +464,6 @@ extern {
                        sevlen: ::socklen_t,
                        flags: ::c_int) -> ::c_int;
     pub fn ioctl(fd: ::c_int, request: ::c_int, ...) -> ::c_int;
+    pub fn eventfd(init: ::c_uint, flags: ::c_int) -> ::c_int;
+    pub fn ptrace(request: ::c_int, ...) -> ::c_long;
 }
index 1a92e3b4fa3412169caf35628d91ccfae399d6e9..aabe8edbe76cb6f246a0c67bfcc87d1afc896e67 100644 (file)
@@ -1,2 +1,20 @@
 pub type c_char = u8;
 pub type wchar_t = u32;
+
+pub const O_DIRECT: ::c_int = 0x10000;
+pub const O_DIRECTORY: ::c_int = 0x4000;
+pub const O_NOFOLLOW: ::c_int = 0x8000;
+
+pub const MAP_LOCKED: ::c_int = 0x02000;
+pub const MAP_NORESERVE: ::c_int = 0x04000;
+
+pub const EDEADLOCK: ::c_int = 35;
+
+pub const SO_PEERCRED: ::c_int = 17;
+pub const SO_RCVLOWAT: ::c_int = 18;
+pub const SO_SNDLOWAT: ::c_int = 19;
+pub const SO_RCVTIMEO: ::c_int = 20;
+pub const SO_SNDTIMEO: ::c_int = 21;
+
+pub const FIOCLEX: ::c_ulong = 0x5451;
+pub const FIONBIO: ::c_ulong = 0x5421;
index e0a691c2b1b98a4827693d89ebfbf9d1cb0844ab..0833d21fd29050558ee5cebba6773b10a32fd2f0 100644 (file)
@@ -8,6 +8,7 @@ pub type suseconds_t = i32;
 pub type ino_t = u32;
 pub type off_t = i32;
 pub type blkcnt_t = i32;
+pub type __fsword_t = i32;
 
 pub type blksize_t = i32;
 pub type nlink_t = u32;
@@ -16,6 +17,13 @@ pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 24;
 pub const __SIZEOF_PTHREAD_RWLOCK_T: usize = 32;
 pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 4;
 
+pub const PTRACE_GETFPREGS: ::c_uint = 14;
+pub const PTRACE_SETFPREGS: ::c_uint = 15;
+pub const PTRACE_GETFPXREGS: ::c_uint = 18;
+pub const PTRACE_SETFPXREGS: ::c_uint = 19;
+pub const PTRACE_GETREGS: ::c_uint = 12;
+pub const PTRACE_SETREGS: ::c_uint = 13;
+
 s! {
     pub struct stat {
         pub st_dev: ::dev_t,
index 76ec3ce823e8fedfc49c9e9118285af65b5d86fb..45fa03f90dea0259a931b325f38bfcf2577b1de6 100644 (file)
@@ -1,2 +1,20 @@
 pub type c_char = i8;
 pub type wchar_t = i32;
+
+pub const O_DIRECT: ::c_int = 0x4000;
+pub const O_DIRECTORY: ::c_int = 0x10000;
+pub const O_NOFOLLOW: ::c_int = 0x20000;
+
+pub const MAP_LOCKED: ::c_int = 0x02000;
+pub const MAP_NORESERVE: ::c_int = 0x04000;
+
+pub const EDEADLOCK: ::c_int = 35;
+
+pub const SO_PEERCRED: ::c_int = 17;
+pub const SO_RCVLOWAT: ::c_int = 18;
+pub const SO_SNDLOWAT: ::c_int = 19;
+pub const SO_RCVTIMEO: ::c_int = 20;
+pub const SO_SNDTIMEO: ::c_int = 21;
+
+pub const FIOCLEX: ::c_ulong = 0x5451;
+pub const FIONBIO: ::c_ulong = 0x5421;
index 8b35e39969c45c883c44d6846fee05619220930c..b56cb48b5dc6d6a701e2321ea23a232a27eb2eeb 100644 (file)
@@ -8,6 +8,24 @@ pub type blksize_t = i32;
 pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 48;
 pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 8;
 
+pub const O_DIRECT: ::c_int = 0x10000;
+pub const O_DIRECTORY: ::c_int = 0x4000;
+pub const O_NOFOLLOW: ::c_int = 0x8000;
+
+pub const MAP_LOCKED: ::c_int = 0x02000;
+pub const MAP_NORESERVE: ::c_int = 0x04000;
+
+pub const EDEADLOCK: ::c_int = 35;
+
+pub const SO_PEERCRED: ::c_int = 17;
+pub const SO_RCVLOWAT: ::c_int = 18;
+pub const SO_SNDLOWAT: ::c_int = 19;
+pub const SO_RCVTIMEO: ::c_int = 20;
+pub const SO_SNDTIMEO: ::c_int = 21;
+
+pub const FIOCLEX: ::c_ulong = 0x5451;
+pub const FIONBIO: ::c_ulong = 0x5421;
+
 s! {
     pub struct stat {
         pub st_dev: ::dev_t,
index ce3f3381c070f46b4f8fb58ecafa5ab4a0b7e27b..c9d82af4b42b8b1e82e69f2f45120926f2419432 100644 (file)
@@ -8,6 +8,7 @@ pub type suseconds_t = i64;
 pub type ino_t = u64;
 pub type off_t = i64;
 pub type blkcnt_t = i64;
+pub type __fsword_t = ::c_long;
 
 s! {
     pub struct sigset_t {
@@ -21,6 +22,9 @@ cfg_if! {
     if #[cfg(target_arch = "aarch64")] {
         mod aarch64;
         pub use self::aarch64::*;
+    } else if #[cfg(any(target_arch = "powerpc64", target_arch = "powerpc64le"))] {
+        mod powerpc64;
+        pub use self::powerpc64::*;
     } else {
         mod x86_64;
         pub use self::x86_64::*;
diff --git a/src/liblibc/src/unix/notbsd/linux/other/b64/powerpc64.rs b/src/liblibc/src/unix/notbsd/linux/other/b64/powerpc64.rs
new file mode 100644 (file)
index 0000000..c853a01
--- /dev/null
@@ -0,0 +1,75 @@
+//! PowerPC64-specific definitions for 64-bit linux-like values
+
+pub type c_char = u8;
+pub type wchar_t = u32;
+pub type nlink_t = u64;
+pub type blksize_t = i64;
+
+pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 40;
+pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 4;
+
+pub const O_DIRECTORY: ::c_int = 0x4000;
+pub const O_NOFOLLOW: ::c_int = 0x8000;
+pub const O_DIRECT: ::c_int = 0x20000;
+
+pub const MAP_LOCKED: ::c_int = 0x00080;
+pub const MAP_NORESERVE: ::c_int = 0x00040;
+
+pub const EDEADLOCK: ::c_int = 58;
+
+pub const SO_PEERCRED: ::c_int = 21;
+pub const SO_RCVLOWAT: ::c_int = 16;
+pub const SO_SNDLOWAT: ::c_int = 17;
+pub const SO_RCVTIMEO: ::c_int = 18;
+pub const SO_SNDTIMEO: ::c_int = 19;
+
+pub const FIOCLEX: ::c_ulong = 0x20006601;
+pub const FIONBIO: ::c_ulong = 0x8004667e;
+
+s! {
+    pub struct stat {
+        pub st_dev: ::dev_t,
+        pub st_ino: ::ino_t,
+        pub st_nlink: ::nlink_t,
+        pub st_mode: ::mode_t,
+        pub st_uid: ::uid_t,
+        pub st_gid: ::gid_t,
+        __pad0: ::c_int,
+        pub st_rdev: ::dev_t,
+        pub st_size: ::off_t,
+        pub st_blksize: ::blksize_t,
+        pub st_blocks: ::blkcnt_t,
+        pub st_atime: ::time_t,
+        pub st_atime_nsec: ::c_long,
+        pub st_mtime: ::time_t,
+        pub st_mtime_nsec: ::c_long,
+        pub st_ctime: ::time_t,
+        pub st_ctime_nsec: ::c_long,
+        __unused: [::c_long; 3],
+    }
+
+    pub struct stat64 {
+        pub st_dev: ::dev_t,
+        pub st_ino: ::ino64_t,
+        pub st_nlink: ::nlink_t,
+        pub st_mode: ::mode_t,
+        pub st_uid: ::uid_t,
+        pub st_gid: ::gid_t,
+        __pad0: ::c_int,
+        pub st_rdev: ::dev_t,
+        pub st_size: ::off64_t,
+        pub st_blksize: ::blksize_t,
+        pub st_blocks: ::blkcnt64_t,
+        pub st_atime: ::time_t,
+        pub st_atime_nsec: ::c_long,
+        pub st_mtime: ::time_t,
+        pub st_mtime_nsec: ::c_long,
+        pub st_ctime: ::time_t,
+        pub st_ctime_nsec: ::c_long,
+        __reserved: [::c_long; 3],
+    }
+
+    pub struct pthread_attr_t {
+        __size: [u64; 7]
+    }
+}
index 98d9b6eb808598269ed6b1f7958b0ead0037e0e2..fc82c71e2d8773f1ce3839a5f9faa8d238169ccb 100644 (file)
@@ -8,6 +8,31 @@ pub type blksize_t = i64;
 pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 40;
 pub const __SIZEOF_PTHREAD_MUTEXATTR_T: usize = 4;
 
+pub const O_DIRECT: ::c_int = 0x4000;
+pub const O_DIRECTORY: ::c_int = 0x10000;
+pub const O_NOFOLLOW: ::c_int = 0x20000;
+
+pub const MAP_LOCKED: ::c_int = 0x02000;
+pub const MAP_NORESERVE: ::c_int = 0x04000;
+
+pub const EDEADLOCK: ::c_int = 35;
+
+pub const SO_PEERCRED: ::c_int = 17;
+pub const SO_RCVLOWAT: ::c_int = 18;
+pub const SO_SNDLOWAT: ::c_int = 19;
+pub const SO_RCVTIMEO: ::c_int = 20;
+pub const SO_SNDTIMEO: ::c_int = 21;
+
+pub const FIOCLEX: ::c_ulong = 0x5451;
+pub const FIONBIO: ::c_ulong = 0x5421;
+
+pub const PTRACE_GETFPREGS: ::c_uint = 14;
+pub const PTRACE_SETFPREGS: ::c_uint = 15;
+pub const PTRACE_GETFPXREGS: ::c_uint = 18;
+pub const PTRACE_SETFPXREGS: ::c_uint = 19;
+pub const PTRACE_GETREGS: ::c_uint = 12;
+pub const PTRACE_SETREGS: ::c_uint = 13;
+
 s! {
     pub struct stat {
         pub st_dev: ::dev_t,
index 247867793903c78d6aa946f80592bc8062754372..d0d59dae8da9ca4f34417b1ec11436423aee9055 100644 (file)
@@ -32,6 +32,58 @@ s! {
         __unused4: *mut ::c_void,
         __unused5: *mut ::c_void,
     }
+
+
+    pub struct ucred {
+        pub pid: ::pid_t,
+        pub uid: ::uid_t,
+        pub gid: ::gid_t,
+    }
+
+    pub struct statfs {
+        pub f_type: __fsword_t,
+        pub f_bsize: __fsword_t,
+        pub f_blocks: ::fsblkcnt_t,
+        pub f_bfree: ::fsblkcnt_t,
+        pub f_bavail: ::fsblkcnt_t,
+
+        pub f_files: ::fsfilcnt_t,
+        pub f_ffree: ::fsfilcnt_t,
+        pub f_fsid: ::fsid_t,
+
+        pub f_namelen: __fsword_t,
+        pub f_frsize: __fsword_t,
+        f_spare: [__fsword_t; 5],
+    }
+
+    pub struct msghdr {
+        pub msg_name: *mut ::c_void,
+        pub msg_namelen: ::socklen_t,
+        pub msg_iov: *mut ::iovec,
+        pub msg_iovlen: ::size_t,
+        pub msg_control: *mut ::c_void,
+        pub msg_controllen: ::size_t,
+        pub msg_flags: ::c_int,
+    }
+
+    pub struct termios {
+        pub c_iflag: ::tcflag_t,
+        pub c_oflag: ::tcflag_t,
+        pub c_cflag: ::tcflag_t,
+        pub c_lflag: ::tcflag_t,
+        pub c_line: ::cc_t,
+        pub c_cc: [::cc_t; ::NCCS],
+        pub c_ispeed: ::speed_t,
+        pub c_ospeed: ::speed_t,
+    }
+
+    pub struct flock {
+        pub l_type: ::c_short,
+        pub l_whence: ::c_short,
+        pub l_start: ::off_t,
+        pub l_len: ::off_t,
+        pub l_pid: ::pid_t,
+    }
 }
 
 pub const RLIMIT_RSS: ::c_int = 5;
@@ -51,14 +103,13 @@ pub const O_NONBLOCK: ::c_int = 2048;
 pub const O_SYNC: ::c_int = 1052672;
 pub const O_RSYNC: ::c_int = 1052672;
 pub const O_DSYNC: ::c_int = 4096;
+pub const O_FSYNC: ::c_int = 0x101000;
 
 pub const MAP_ANON: ::c_int = 0x0020;
 pub const MAP_ANONYMOUS: ::c_int = 0x0020;
 pub const MAP_GROWSDOWN: ::c_int = 0x0100;
 pub const MAP_DENYWRITE: ::c_int = 0x0800;
 pub const MAP_EXECUTABLE: ::c_int = 0x01000;
-pub const MAP_LOCKED: ::c_int = 0x02000;
-pub const MAP_NORESERVE: ::c_int = 0x04000;
 pub const MAP_POPULATE: ::c_int = 0x08000;
 pub const MAP_NONBLOCK: ::c_int = 0x010000;
 pub const MAP_STACK: ::c_int = 0x020000;
@@ -85,7 +136,6 @@ pub const EXFULL: ::c_int = 54;
 pub const ENOANO: ::c_int = 55;
 pub const EBADRQC: ::c_int = 56;
 pub const EBADSLT: ::c_int = 57;
-pub const EDEADLOCK: ::c_int = EDEADLK;
 pub const EMULTIHOP: ::c_int = 72;
 pub const EOVERFLOW: ::c_int = 75;
 pub const ENOTUNIQ: ::c_int = 76;
@@ -164,10 +214,6 @@ pub const SO_KEEPALIVE: ::c_int = 9;
 pub const SO_OOBINLINE: ::c_int = 10;
 pub const SO_LINGER: ::c_int = 13;
 pub const SO_REUSEPORT: ::c_int = 15;
-pub const SO_RCVLOWAT: ::c_int = 18;
-pub const SO_SNDLOWAT: ::c_int = 19;
-pub const SO_RCVTIMEO: ::c_int = 20;
-pub const SO_SNDTIMEO: ::c_int = 21;
 pub const SO_ACCEPTCONN: ::c_int = 30;
 
 pub const TCP_COOKIE_TRANSACTIONS: ::c_int = 15;
@@ -187,14 +233,32 @@ pub const SA_NOCLDWAIT: ::c_int = 0x00000002;
 
 pub const SIGCHLD: ::c_int = 17;
 pub const SIGBUS: ::c_int = 7;
+pub const SIGUSR1: ::c_int = 10;
+pub const SIGUSR2: ::c_int = 12;
+pub const SIGCONT: ::c_int = 18;
+pub const SIGSTOP: ::c_int = 19;
+pub const SIGTSTP: ::c_int = 20;
+pub const SIGURG: ::c_int = 23;
+pub const SIGIO: ::c_int = 29;
+pub const SIGSYS: ::c_int = 31;
+pub const SIGSTKFLT: ::c_int = 16;
+pub const SIGUNUSED: ::c_int = 31;
+pub const SIGTTIN: ::c_int = 21;
+pub const SIGTTOU: ::c_int = 22;
+pub const SIGXCPU: ::c_int = 24;
+pub const SIGXFSZ: ::c_int = 25;
+pub const SIGVTALRM: ::c_int = 26;
+pub const SIGPROF: ::c_int = 27;
+pub const SIGWINCH: ::c_int = 28;
+pub const SIGPOLL: ::c_int = 29;
+pub const SIGPWR: ::c_int = 30;
 pub const SIG_SETMASK: ::c_int = 2;
+pub const SIG_BLOCK: ::c_int = 0x000000;
+pub const SIG_UNBLOCK: ::c_int = 0x01;
 
 pub const FALLOC_FL_KEEP_SIZE: ::c_int = 0x01;
 pub const FALLOC_FL_PUNCH_HOLE: ::c_int = 0x02;
 
-pub const FIOCLEX: ::c_ulong = 0x5451;
-pub const FIONBIO: ::c_ulong = 0x5421;
-
 pub const BUFSIZ: ::c_uint = 8192;
 pub const TMP_MAX: ::c_uint = 238328;
 pub const FOPEN_MAX: ::c_uint = 16;
@@ -202,10 +266,131 @@ pub const POSIX_MADV_DONTNEED: ::c_int = 4;
 pub const _SC_2_C_VERSION: ::c_int = 96;
 pub const RUSAGE_THREAD: ::c_int = 1;
 pub const O_ACCMODE: ::c_int = 3;
+pub const O_ASYNC: ::c_int = 0x2000;
+pub const O_NDELAY: ::c_int = 0x800;
 pub const RUSAGE_CHILDREN: ::c_int = -1;
 pub const ST_RELATIME: ::c_ulong = 4096;
 pub const NI_MAXHOST: ::socklen_t = 1025;
 
+pub const ADFS_SUPER_MAGIC: ::c_long = 0x0000adf5;
+pub const AFFS_SUPER_MAGIC: ::c_long = 0x0000adff;
+pub const CODA_SUPER_MAGIC: ::c_long = 0x73757245;
+pub const CRAMFS_MAGIC: ::c_long = 0x28cd3d45;
+pub const EFS_SUPER_MAGIC: ::c_long = 0x00414a53;
+pub const EXT2_SUPER_MAGIC: ::c_long = 0x0000ef53;
+pub const EXT3_SUPER_MAGIC: ::c_long = 0x0000ef53;
+pub const EXT4_SUPER_MAGIC: ::c_long = 0x0000ef53;
+pub const HPFS_SUPER_MAGIC: ::c_long = 0xf995e849;
+pub const HUGETLBFS_MAGIC: ::c_long = 0x958458f6;
+pub const ISOFS_SUPER_MAGIC: ::c_long = 0x00009660;
+pub const JFFS2_SUPER_MAGIC: ::c_long = 0x000072b6;
+pub const MINIX_SUPER_MAGIC: ::c_long = 0x0000137f;
+pub const MINIX_SUPER_MAGIC2: ::c_long = 0x0000138f;
+pub const MINIX2_SUPER_MAGIC: ::c_long = 0x00002468;
+pub const MINIX2_SUPER_MAGIC2: ::c_long = 0x00002478;
+pub const MSDOS_SUPER_MAGIC: ::c_long = 0x00004d44;
+pub const NCP_SUPER_MAGIC: ::c_long = 0x0000564c;
+pub const NFS_SUPER_MAGIC: ::c_long = 0x00006969;
+pub const OPENPROM_SUPER_MAGIC: ::c_long = 0x00009fa1;
+pub const PROC_SUPER_MAGIC: ::c_long = 0x00009fa0;
+pub const QNX4_SUPER_MAGIC: ::c_long = 0x0000002f;
+pub const REISERFS_SUPER_MAGIC: ::c_long = 0x52654973;
+pub const SMB_SUPER_MAGIC: ::c_long = 0x0000517b;
+pub const TMPFS_MAGIC: ::c_long = 0x01021994;
+pub const USBDEVICE_SUPER_MAGIC: ::c_long = 0x00009fa2;
+
+pub const VEOF: usize = 4;
+pub const VEOL: usize = 11;
+pub const VEOL2: usize = 16;
+pub const VMIN: usize = 6;
+pub const IEXTEN: ::tcflag_t = 0x00008000;
+pub const TOSTOP: ::tcflag_t = 0x00000100;
+pub const FLUSHO: ::tcflag_t = 0x00001000;
+pub const IUTF8: ::tcflag_t = 0x00004000;
+
+pub const CPU_SETSIZE: ::c_int = 0x400;
+
+pub const EXTPROC: ::tcflag_t = 0x00010000;
+
+pub const QFMT_VFS_V1: ::c_int = 4;
+
+pub const PTRACE_TRACEME: ::c_uint = 0;
+pub const PTRACE_PEEKTEXT: ::c_uint = 1;
+pub const PTRACE_PEEKDATA: ::c_uint = 2;
+pub const PTRACE_PEEKUSER: ::c_uint = 3;
+pub const PTRACE_POKETEXT: ::c_uint = 4;
+pub const PTRACE_POKEDATA: ::c_uint = 5;
+pub const PTRACE_POKEUSER: ::c_uint = 6;
+pub const PTRACE_CONT: ::c_uint = 7;
+pub const PTRACE_KILL: ::c_uint = 8;
+pub const PTRACE_SINGLESTEP: ::c_uint = 9;
+pub const PTRACE_ATTACH: ::c_uint = 16;
+pub const PTRACE_DETACH: ::c_uint = 17;
+pub const PTRACE_SYSCALL: ::c_uint = 24;
+pub const PTRACE_SETOPTIONS: ::c_uint = 0x4200;
+pub const PTRACE_GETEVENTMSG: ::c_uint = 0x4201;
+pub const PTRACE_GETSIGINFO: ::c_uint = 0x4202;
+pub const PTRACE_SETSIGINFO: ::c_uint = 0x4203;
+pub const PTRACE_GETREGSET: ::c_uint = 0x4204;
+pub const PTRACE_SETREGSET: ::c_uint = 0x4205;
+pub const PTRACE_SEIZE: ::c_uint = 0x4206;
+pub const PTRACE_INTERRUPT: ::c_uint = 0x4207;
+pub const PTRACE_LISTEN: ::c_uint = 0x4208;
+pub const PTRACE_PEEKSIGINFO: ::c_uint = 0x4209;
+
+pub const MADV_DODUMP: ::c_int = 17;
+pub const MADV_DONTDUMP: ::c_int = 16;
+
+pub const EPOLLWAKEUP: ::c_int = 0x20000000;
+
+pub const MADV_HUGEPAGE: ::c_int = 14;
+pub const MADV_NOHUGEPAGE: ::c_int = 15;
+pub const MAP_HUGETLB: ::c_int = 0x040000;
+
+pub const EFD_NONBLOCK: ::c_int = 0x800;
+
+pub const F_GETLK: ::c_int = 5;
+pub const F_GETOWN: ::c_int = 9;
+pub const F_SETOWN: ::c_int = 8;
+
+pub const SFD_NONBLOCK: ::c_int = 0x0800;
+
+pub const TCSANOW: ::c_int = 0;
+pub const TCSADRAIN: ::c_int = 1;
+pub const TCSAFLUSH: ::c_int = 2;
+
+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 TIOCINQ: ::c_ulong = 0x541B;
+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;
+
 cfg_if! {
     if #[cfg(any(target_arch = "arm", target_arch = "x86",
                  target_arch = "x86_64"))] {
@@ -239,16 +424,56 @@ extern {
                        serv: *mut ::c_char,
                        sevlen: ::socklen_t,
                        flags: ::c_int) -> ::c_int;
+    pub fn eventfd(init: ::c_int, flags: ::c_int) -> ::c_int;
+    pub fn ptrace(request: ::c_uint, ...) -> ::c_long;
 }
 
 cfg_if! {
     if #[cfg(any(target_arch = "x86", target_arch = "arm"))] {
         mod b32;
         pub use self::b32::*;
-    } else if #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] {
+    } else if #[cfg(any(target_arch = "x86_64",
+                        target_arch = "aarch64",
+                        target_arch = "powerpc64",
+                        target_arch = "powerpc64le"))] {
         mod b64;
         pub use self::b64::*;
     } else {
         // ...
     }
 }
+
+s! {
+    pub struct ipc_perm {
+        pub __key: ::key_t,
+        pub uid: ::uid_t,
+        pub gid: ::gid_t,
+        pub cuid: ::uid_t,
+        pub cgid: ::gid_t,
+        pub mode: ::c_ushort,
+        __pad1: ::c_ushort,
+        pub __seq: ::c_ushort,
+        __pad2: ::c_ushort,
+        __unused1: ::c_ulong,
+        __unused2: ::c_ulong
+    }
+
+    pub struct shmid_ds {
+        pub shm_perm: ::ipc_perm,
+        pub shm_segsz: ::size_t,
+        pub shm_atime: ::time_t,
+        #[cfg(target_pointer_width = "32")]
+        __unused1: ::c_ulong,
+        pub shm_dtime: ::time_t,
+        #[cfg(target_pointer_width = "32")]
+        __unused2: ::c_ulong,
+        pub shm_ctime: ::time_t,
+        #[cfg(target_pointer_width = "32")]
+        __unused3: ::c_ulong,
+        pub shm_cpid: ::pid_t,
+        pub shm_lpid: ::pid_t,
+        pub shm_nattch: ::shmatt_t,
+        __unused4: ::c_ulong,
+        __unused5: ::c_ulong
+    }
+}
index add7bb9a5b47b8a876e30ae6941dfe581847bac2..d69515676cc9fb3b0995b019e38c4cd751837b61 100644 (file)
@@ -3,6 +3,8 @@ use dox::mem;
 pub type rlim_t = c_ulong;
 pub type sa_family_t = u16;
 pub type pthread_key_t = ::c_uint;
+pub type speed_t = ::c_uint;
+pub type tcflag_t = ::c_uint;
 
 pub enum timezone {}
 
@@ -48,7 +50,7 @@ s! {
         pub ai_protocol: ::c_int,
         pub ai_addrlen: socklen_t,
 
-        #[cfg(target_os = "linux")]
+        #[cfg(any(target_os = "linux", target_os = "emscripten"))]
         pub ai_addr: *mut ::sockaddr,
 
         pub ai_canonname: *mut c_char,
@@ -86,6 +88,41 @@ s! {
         pub tm_gmtoff: ::c_long,
         pub tm_zone: *const ::c_char,
     }
+
+    pub struct sched_param {
+        pub sched_priority: ::c_int,
+        #[cfg(target_env = "musl")]
+        pub sched_ss_low_priority: ::c_int,
+        #[cfg(target_env = "musl")]
+        pub sched_ss_repl_period: ::timespec,
+        #[cfg(target_env = "musl")]
+        pub sched_ss_init_budget: ::timespec,
+        #[cfg(target_env = "musl")]
+        pub sched_ss_max_repl: ::c_int,
+    }
+
+    pub struct Dl_info {
+        pub dli_fname: *const ::c_char,
+        pub dli_fbase: *mut ::c_void,
+        pub dli_sname: *const ::c_char,
+        pub dli_saddr: *mut ::c_void,
+    }
+
+    #[cfg_attr(any(target_arch = "x86", target_arch = "x86_64"),
+               repr(packed))]
+    pub struct epoll_event {
+        pub events: ::uint32_t,
+        pub u64: ::uint64_t,
+    }
+
+    pub struct utsname {
+        pub sysname: [::c_char; 65],
+        pub nodename: [::c_char; 65],
+        pub release: [::c_char; 65],
+        pub version: [::c_char; 65],
+        pub machine: [::c_char; 65],
+        pub domainname: [::c_char; 65]
+    }
 }
 
 // intentionally not public, only used for fd_set
@@ -194,6 +231,30 @@ pub const MCL_FUTURE: ::c_int = 0x0002;
 pub const MS_ASYNC: ::c_int = 0x0001;
 pub const MS_INVALIDATE: ::c_int = 0x0002;
 pub const MS_SYNC: ::c_int = 0x0004;
+pub const MS_RDONLY: ::c_ulong = 0x01;
+pub const MS_NOSUID: ::c_ulong = 0x02;
+pub const MS_NODEV: ::c_ulong = 0x04;
+pub const MS_NOEXEC: ::c_ulong = 0x08;
+pub const MS_SYNCHRONOUS: ::c_ulong = 0x10;
+pub const MS_REMOUNT: ::c_ulong = 0x20;
+pub const MS_MANDLOCK: ::c_ulong = 0x40;
+pub const MS_DIRSYNC: ::c_ulong = 0x80;
+pub const MS_NOATIME: ::c_ulong = 0x0400;
+pub const MS_NODIRATIME: ::c_ulong = 0x0800;
+pub const MS_BIND: ::c_ulong = 0x1000;
+pub const MS_MOVE: ::c_ulong = 0x2000;
+pub const MS_REC: ::c_ulong = 0x4000;
+pub const MS_SILENT: ::c_ulong = 0x8000;
+pub const MS_POSIXACL: ::c_ulong = 0x010000;
+pub const MS_UNBINDABLE: ::c_ulong = 0x020000;
+pub const MS_PRIVATE: ::c_ulong = 0x040000;
+pub const MS_SLAVE: ::c_ulong = 0x080000;
+pub const MS_SHARED: ::c_ulong = 0x100000;
+pub const MS_ACTIVE: ::c_ulong = 0x40000000;
+pub const MS_NOUSER: ::c_ulong = 0x80000000;
+pub const MS_MGC_VAL: ::c_ulong = 0xc0ed0000;
+pub const MS_MGC_MSK: ::c_ulong = 0xffff0000;
+pub const MS_RMT_MASK: ::c_ulong = 0x800051;
 
 pub const EPERM: ::c_int = 1;
 pub const ENOENT: ::c_int = 2;
@@ -319,8 +380,149 @@ pub const SA_RESETHAND: ::c_int = 0x80000000;
 pub const SA_RESTART: ::c_int = 0x10000000;
 pub const SA_NOCLDSTOP: ::c_int = 0x00000001;
 
+pub const PATH_MAX: ::c_int = 4096;
+
 pub const FD_SETSIZE: usize = 1024;
 
+pub const EPOLLIN: ::c_int = 0x1;
+pub const EPOLLPRI: ::c_int = 0x2;
+pub const EPOLLOUT: ::c_int = 0x4;
+pub const EPOLLRDNORM: ::c_int = 0x40;
+pub const EPOLLRDBAND: ::c_int = 0x80;
+pub const EPOLLWRNORM: ::c_int = 0x100;
+pub const EPOLLWRBAND: ::c_int = 0x200;
+pub const EPOLLMSG: ::c_int = 0x400;
+pub const EPOLLERR: ::c_int = 0x8;
+pub const EPOLLHUP: ::c_int = 0x10;
+pub const EPOLLET: ::c_int = 0x80000000;
+
+pub const EPOLL_CTL_ADD: ::c_int = 1;
+pub const EPOLL_CTL_MOD: ::c_int = 3;
+pub const EPOLL_CTL_DEL: ::c_int = 2;
+
+pub const MNT_DETACH: ::c_int = 0x2;
+pub const MNT_EXPIRE: ::c_int = 0x4;
+
+pub const Q_GETFMT: ::c_int = 0x800004;
+pub const Q_GETINFO: ::c_int = 0x800005;
+pub const Q_SETINFO: ::c_int = 0x800006;
+pub const QIF_BLIMITS: ::uint32_t = 1;
+pub const QIF_SPACE: ::uint32_t = 2;
+pub const QIF_ILIMITS: ::uint32_t = 4;
+pub const QIF_INODES: ::uint32_t = 8;
+pub const QIF_BTIME: ::uint32_t = 16;
+pub const QIF_ITIME: ::uint32_t = 32;
+pub const QIF_LIMITS: ::uint32_t = 5;
+pub const QIF_USAGE: ::uint32_t = 10;
+pub const QIF_TIMES: ::uint32_t = 48;
+pub const QIF_ALL: ::uint32_t = 63;
+
+pub const CBAUD: ::tcflag_t = 0o0010017;
+
+pub const EFD_CLOEXEC: ::c_int = 0x80000;
+
+pub const F_SETLK: ::c_int = 6;
+pub const F_SETLKW: ::c_int = 7;
+
+pub const MNT_FORCE: ::c_int = 0x1;
+
+pub const Q_SYNC: ::c_int = 0x800001;
+pub const Q_QUOTAON: ::c_int = 0x800002;
+pub const Q_QUOTAOFF: ::c_int = 0x800003;
+pub const Q_GETQUOTA: ::c_int = 0x800007;
+pub const Q_SETQUOTA: ::c_int = 0x800008;
+
+pub const TCIOFF: ::c_int = 2;
+pub const TCION: ::c_int = 3;
+pub const TCOOFF: ::c_int = 0;
+pub const TCOON: ::c_int = 1;
+pub const TCIFLUSH: ::c_int = 0;
+pub const TCOFLUSH: ::c_int = 1;
+pub const TCIOFLUSH: ::c_int = 2;
+pub const NL0: ::c_int  = 0x00000000;
+pub const NL1: ::c_int  = 0x00000100;
+pub const TAB0: ::c_int = 0x00000000;
+pub const TAB1: ::c_int = 0x00000800;
+pub const TAB2: ::c_int = 0x00001000;
+pub const TAB3: ::c_int = 0x00001800;
+pub const CR0: ::c_int  = 0x00000000;
+pub const CR1: ::c_int  = 0x00000200;
+pub const CR2: ::c_int  = 0x00000400;
+pub const CR3: ::c_int  = 0x00000600;
+pub const FF0: ::c_int  = 0x00000000;
+pub const FF1: ::c_int  = 0x00008000;
+pub const BS0: ::c_int  = 0x00000000;
+pub const BS1: ::c_int  = 0x00002000;
+pub const VT0: ::c_int  = 0x00000000;
+pub const VT1: ::c_int  = 0x00004000;
+pub const VERASE: usize = 2;
+pub const VWERASE: usize = 14;
+pub const VKILL: usize = 3;
+pub const VREPRINT: usize = 12;
+pub const VINTR: usize = 0;
+pub const VQUIT: usize = 1;
+pub const VSUSP: usize = 10;
+pub const VSTART: usize = 8;
+pub const VSTOP: usize = 9;
+pub const VLNEXT: usize = 15;
+pub const VDISCARD: usize = 13;
+pub const VTIME: usize = 5;
+pub const IGNBRK: ::tcflag_t = 0x00000001;
+pub const BRKINT: ::tcflag_t = 0x00000002;
+pub const IGNPAR: ::tcflag_t = 0x00000004;
+pub const PARMRK: ::tcflag_t = 0x00000008;
+pub const INPCK: ::tcflag_t = 0x00000010;
+pub const ISTRIP: ::tcflag_t = 0x00000020;
+pub const INLCR: ::tcflag_t = 0x00000040;
+pub const IGNCR: ::tcflag_t = 0x00000080;
+pub const ICRNL: ::tcflag_t = 0x00000100;
+pub const IXON: ::tcflag_t = 0x00000400;
+pub const IXOFF: ::tcflag_t = 0x00001000;
+pub const IXANY: ::tcflag_t = 0x00000800;
+pub const IMAXBEL: ::tcflag_t = 0x00002000;
+pub const OPOST: ::tcflag_t = 0x1;
+pub const ONLCR: ::tcflag_t = 0x4;
+pub const CSIZE: ::tcflag_t = 0x00000030;
+pub const CS5: ::tcflag_t = 0x00000000;
+pub const CS6: ::tcflag_t = 0x00000010;
+pub const CS7: ::tcflag_t = 0x00000020;
+pub const CS8: ::tcflag_t = 0x00000030;
+pub const CSTOPB: ::tcflag_t = 0x00000040;
+pub const CREAD: ::tcflag_t = 0x00000080;
+pub const PARENB: ::tcflag_t = 0x00000100;
+pub const PARODD: ::tcflag_t = 0x00000200;
+pub const HUPCL: ::tcflag_t = 0x00000400;
+pub const CLOCAL: ::tcflag_t = 0x00000800;
+pub const CRTSCTS: ::tcflag_t = 0x80000000;
+pub const ECHOKE: ::tcflag_t = 0x00000800;
+pub const ECHOE: ::tcflag_t = 0x00000010;
+pub const ECHOK: ::tcflag_t = 0x00000020;
+pub const ECHO: ::tcflag_t = 0x00000008;
+pub const ECHONL: ::tcflag_t = 0x00000040;
+pub const ECHOPRT: ::tcflag_t = 0x00000400;
+pub const ECHOCTL: ::tcflag_t = 0x00000200;
+pub const ISIG: ::tcflag_t = 0x00000001;
+pub const ICANON: ::tcflag_t = 0x00000002;
+pub const PENDIN: ::tcflag_t = 0x00004000;
+pub const NOFLSH: ::tcflag_t = 0x00000080;
+
+pub const CLONE_VM: ::c_int = 0x100;
+pub const CLONE_FS: ::c_int = 0x200;
+pub const CLONE_FILES: ::c_int = 0x400;
+pub const CLONE_SIGHAND: ::c_int = 0x800;
+pub const CLONE_PTRACE: ::c_int = 0x2000;
+pub const CLONE_VFORK: ::c_int = 0x4000;
+pub const CLONE_PARENT: ::c_int = 0x8000;
+pub const CLONE_THREAD: ::c_int = 0x10000;
+pub const CLONE_NEWNS: ::c_int = 0x20000;
+pub const CLONE_SYSVSEM: ::c_int = 0x40000;
+pub const CLONE_SETTLS: ::c_int = 0x80000;
+pub const CLONE_PARENT_SETTID: ::c_int = 0x100000;
+pub const CLONE_CHILD_CLEARTID: ::c_int = 0x200000;
+pub const CLONE_DETACHED: ::c_int = 0x400000;
+pub const CLONE_UNTRACED: ::c_int = 0x800000;
+pub const CLONE_CHILD_SETTID: ::c_int = 0x01000000;
+
 f! {
     pub fn FD_CLR(fd: ::c_int, set: *mut fd_set) -> () {
         let fd = fd as usize;
@@ -377,10 +579,40 @@ extern {
     pub fn memalign(align: ::size_t, size: ::size_t) -> *mut ::c_void;
     pub fn setgroups(ngroups: ::size_t,
                      ptr: *const ::gid_t) -> ::c_int;
+    pub fn sched_setscheduler(pid: ::pid_t, policy: ::c_int, param: *const sched_param) -> ::c_int;
+    pub fn sched_getscheduler(pid: ::pid_t) -> ::c_int;
+    pub fn sched_get_priority_max(policy: ::c_int) -> ::c_int;
+    pub fn sched_get_priority_min(policy: ::c_int) -> ::c_int;
+    pub fn epoll_create(size: ::c_int) -> ::c_int;
+    pub fn epoll_ctl(epfd: ::c_int,
+                     op: ::c_int,
+                     fd: ::c_int,
+                     event: *mut epoll_event) -> ::c_int;
+    pub fn epoll_wait(epfd: ::c_int,
+                      events: *mut epoll_event,
+                      maxevents: ::c_int,
+                      timeout: ::c_int) -> ::c_int;
+    pub fn pipe2(fds: *mut ::c_int, flags: ::c_int) -> ::c_int;
+    pub fn mount(src: *const ::c_char,
+                 target: *const ::c_char,
+                 fstype: *const ::c_char,
+                 flags: ::c_ulong,
+                 data: *const ::c_void) -> ::c_int;
+    pub fn umount(target: *const ::c_char) -> ::c_int;
+    pub fn umount2(target: *const ::c_char, flags: ::c_int) -> ::c_int;
+    pub fn clone(cb: extern fn(*mut ::c_void) -> ::c_int,
+                 child_stack: *mut ::c_void,
+                 flags: ::c_int,
+                 arg: *mut ::c_void, ...) -> ::c_int;
+    pub fn statfs(path: *const ::c_char, buf: *mut statfs) -> ::c_int;
+    pub fn fstatfs(fd: ::c_int, buf: *mut statfs) -> ::c_int;
+    pub fn memrchr(cx: *const ::c_void, c: ::c_int, n: ::size_t) -> *mut ::c_void;
+    pub fn syscall(num: ::c_long, ...) -> ::c_long;
 }
 
 cfg_if! {
-    if #[cfg(target_os = "linux")] {
+    if #[cfg(any(target_os = "linux",
+                 target_os = "emscripten"))] {
         mod linux;
         pub use self::linux::*;
     } else if #[cfg(target_os = "android")] {
index 404bbd902bfd86af95e47ae86932e59e44af93b4..dbd553acd68fc9f361793f3f65b10948921ec75a 100644 (file)
 //! they're turned off (just a load and an integer comparison). This also means that
 //! if logging is disabled, none of the components of the log will be executed.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "log"]
 #![unstable(feature = "rustc_private",
             reason = "use the crates.io `log` library instead",
             issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
index e2c157f98a6a4d2b11ed1e5fc9fe90ab1c9ec9bc..cd099c69005f3a1ece79fad054ef70c48b7bdfd4 100644 (file)
@@ -208,7 +208,6 @@ impl Rand for ChaChaRng {
 mod tests {
     use std::prelude::v1::*;
 
-    use core::iter::order;
     use {Rng, SeedableRng};
     use super::ChaChaRng;
 
@@ -217,8 +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!(order::equals(ra.gen_ascii_chars().take(100),
-                              rb.gen_ascii_chars().take(100)));
+        assert!(ra.gen_ascii_chars().take(100)
+                  .eq(rb.gen_ascii_chars().take(100)));
     }
 
     #[test]
@@ -226,8 +225,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!(order::equals(ra.gen_ascii_chars().take(100),
-                              rb.gen_ascii_chars().take(100)));
+        assert!(ra.gen_ascii_chars().take(100)
+                  .eq(rb.gen_ascii_chars().take(100)));
     }
 
     #[test]
index 1f56a82eba86eea72dfe576f32a2fe42254a4b41..28eff87bde3b762246e11abec3c6039d93c51b21 100644 (file)
@@ -77,14 +77,37 @@ impl IsaacRng {
 
         macro_rules! mix {
             () => {{
-                a=a^(b<<11); d=d+a; b=b+c;
-                b=b^(c>>2);  e=e+b; c=c+d;
-                c=c^(d<<8);  f=f+c; d=d+e;
-                d=d^(e>>16); g=g+d; e=e+f;
-                e=e^(f<<10); h=h+e; f=f+g;
-                f=f^(g>>4);  a=a+f; g=g+h;
-                g=g^(h<<8);  b=b+g; h=h+a;
-                h=h^(a>>9);  c=c+h; a=a+b;
+                a = a ^ (b << 11);
+                d = d + a;
+                b = b + c;
+
+                b = b ^ (c >> 2);
+                e = e + b;
+                c = c + d;
+
+                c = c ^ (d << 8);
+                f = f + c;
+                d = d + e;
+
+                d = d ^ (e >> 16);
+                g = g + d;
+                e = e + f;
+
+                e = e ^ (f << 10);
+                h = h + e;
+                f = f + g;
+
+                f = f ^ (g >> 4);
+                a = a + f;
+                g = g + h;
+
+                g = g ^ (h << 8);
+                b = b + g;
+                h = h + a;
+
+                h = h ^ (a >> 9);
+                c = c + h;
+                a = a + b;
             }}
         }
 
@@ -337,14 +360,37 @@ impl Isaac64Rng {
 
         macro_rules! mix {
             () => {{
-                a=a-e; f=f^(h>>9);  h=h+a;
-                b=b-f; g=g^(a<<9);  a=a+b;
-                c=c-g; h=h^(b>>23); b=b+c;
-                d=d-h; a=a^(c<<15); c=c+d;
-                e=e-a; b=b^(d>>14); d=d+e;
-                f=f-b; c=c^(e<<20); e=e+f;
-                g=g-c; d=d^(f>>17); f=f+g;
-                h=h-d; e=e^(g<<14); g=g+h;
+                a = a - e;
+                f = f ^ (h >> 9);
+                h = h + a;
+
+                b = b - f;
+                g = g ^ (a << 9);
+                a = a + b;
+
+                c = c - g;
+                h = h ^ (b >> 23);
+                b = b + c;
+
+                d = d - h;
+                a = a ^ (c << 15);
+                c = c + d;
+
+                e = e - a;
+                b = b ^ (d >> 14);
+                d = d + e;
+
+                f = f - b;
+                c = c ^ (e << 20);
+                e = e + f;
+
+                g = g - c;
+                d = d ^ (f >> 17);
+                f = f + g;
+
+                h = h - d;
+                e = e ^ (g << 14);
+                g = g + h;
             }}
         }
 
@@ -544,7 +590,6 @@ impl Rand for Isaac64Rng {
 mod tests {
     use std::prelude::v1::*;
 
-    use core::iter::order;
     use {Rng, SeedableRng};
     use super::{IsaacRng, Isaac64Rng};
 
@@ -553,16 +598,16 @@ mod tests {
         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!(order::equals(ra.gen_ascii_chars().take(100),
-                              rb.gen_ascii_chars().take(100)));
+        assert!(ra.gen_ascii_chars().take(100)
+                  .eq(rb.gen_ascii_chars().take(100)));
     }
     #[test]
     fn test_rng_64_rand_seeded() {
         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!(order::equals(ra.gen_ascii_chars().take(100),
-                              rb.gen_ascii_chars().take(100)));
+        assert!(ra.gen_ascii_chars().take(100)
+                  .eq(rb.gen_ascii_chars().take(100)));
     }
 
     #[test]
@@ -570,16 +615,16 @@ 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!(order::equals(ra.gen_ascii_chars().take(100),
-                              rb.gen_ascii_chars().take(100)));
+        assert!(ra.gen_ascii_chars().take(100)
+                  .eq(rb.gen_ascii_chars().take(100)));
     }
     #[test]
     fn test_rng_64_seeded() {
         let seed: &[_] = &[1, 23, 456, 7890, 12345];
         let mut ra: Isaac64Rng = SeedableRng::from_seed(seed);
         let mut rb: Isaac64Rng = SeedableRng::from_seed(seed);
-        assert!(order::equals(ra.gen_ascii_chars().take(100),
-                              rb.gen_ascii_chars().take(100)));
+        assert!(ra.gen_ascii_chars().take(100)
+                  .eq(rb.gen_ascii_chars().take(100)));
     }
 
     #[test]
index 013efb129d850268dbe8f66e20be8bb13ca4c532..06f4c8dfd20a82b65dff234a0dc8c09302274a34 100644 (file)
@@ -16,8 +16,6 @@
 //! is not recommended to use this library directly, but rather the official
 //! interface through `std::rand`.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rand"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
@@ -26,7 +24,6 @@
        html_playground_url = "https://play.rust-lang.org/",
        test(attr(deny(warnings))))]
 #![no_std]
-#![cfg_attr(stage0, staged_api)]
 #![unstable(feature = "rand",
             reason = "use `rand` from crates.io",
             issue = "27703")]
@@ -37,7 +34,6 @@
 #![feature(step_by)]
 #![feature(custom_attribute)]
 #![allow(unused_attributes)]
-#![cfg_attr(stage0, feature(no_std))]
 
 #![cfg_attr(test, feature(test, rand, rustc_private, iter_order_deprecated))]
 
index 8ef94eb16f25db4434c0e73c53f5bb3c626db5e6..db5e0213726d90951538ed28540ad5deed47a9fe 100644 (file)
@@ -122,7 +122,6 @@ impl Default for ReseedWithDefault {
 mod tests {
     use std::prelude::v1::*;
 
-    use core::iter::order;
     use super::{ReseedingRng, ReseedWithDefault};
     use {SeedableRng, Rng};
 
@@ -167,8 +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!(order::equals(ra.gen_ascii_chars().take(100),
-                              rb.gen_ascii_chars().take(100)));
+        assert!(ra.gen_ascii_chars().take(100)
+                  .eq(rb.gen_ascii_chars().take(100)));
     }
 
     #[test]
diff --git a/src/librbml/leb128.rs b/src/librbml/leb128.rs
new file mode 100644 (file)
index 0000000..0c5356c
--- /dev/null
@@ -0,0 +1,151 @@
+// 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.
+
+#[inline]
+pub fn write_to_vec(vec: &mut Vec<u8>, position: &mut usize, byte: u8) {
+    if *position == vec.len() {
+        vec.push(byte);
+    } else {
+        vec[*position] = byte;
+    }
+
+    *position += 1;
+}
+
+pub fn write_unsigned_leb128(out: &mut Vec<u8>, start_position: usize, mut value: u64) -> usize {
+    let mut position = start_position;
+    loop {
+        let mut byte = (value & 0x7F) as u8;
+        value >>= 7;
+        if value != 0 {
+            byte |= 0x80;
+        }
+
+        write_to_vec(out, &mut position, byte);
+
+        if value == 0 {
+            break;
+        }
+    }
+
+    return position - start_position;
+}
+
+pub fn read_unsigned_leb128(data: &[u8], start_position: usize) -> (u64, usize) {
+    let mut result = 0;
+    let mut shift = 0;
+    let mut position = start_position;
+    loop {
+        let byte = data[position];
+        position += 1;
+        result |= ((byte & 0x7F) as u64) << shift;
+        if (byte & 0x80) == 0 {
+            break;
+        }
+        shift += 7;
+    }
+
+    (result, position - start_position)
+}
+
+
+pub fn write_signed_leb128(out: &mut Vec<u8>, start_position: usize, mut value: i64) -> usize {
+    let mut position = start_position;
+
+    loop {
+        let mut byte = (value as u8) & 0x7f;
+        value >>= 7;
+        let more = !((((value == 0) && ((byte & 0x40) == 0)) ||
+                      ((value == -1) && ((byte & 0x40) != 0))));
+        if more {
+            byte |= 0x80; // Mark this byte to show that more bytes will follow.
+        }
+
+        write_to_vec(out, &mut position, byte);
+
+        if !more {
+            break;
+        }
+    }
+
+    return position - start_position;
+}
+
+pub fn read_signed_leb128(data: &[u8], start_position: usize) -> (i64, usize) {
+    let mut result = 0;
+    let mut shift = 0;
+    let mut position = start_position;
+    let mut byte;
+
+    loop {
+        byte = data[position];
+        position += 1;
+        result |= ((byte & 0x7F) as i64) << shift;
+        shift += 7;
+
+        if (byte & 0x80) == 0 {
+            break;
+        }
+    }
+
+    if (shift < 64) && ((byte & 0x40) != 0) {
+        // sign extend
+        result |= -(1i64 << shift);
+    }
+
+    (result, position - start_position)
+}
+
+#[test]
+fn test_unsigned_leb128() {
+    let mut stream = Vec::with_capacity(10000);
+
+    for x in 0..62 {
+        let pos = stream.len();
+        let bytes_written = write_unsigned_leb128(&mut stream, pos, 3 << x);
+        assert_eq!(stream.len(), pos + bytes_written);
+    }
+
+    let mut position = 0;
+    for x in 0..62 {
+        let expected = 3 << x;
+        let (actual, bytes_read) = read_unsigned_leb128(&stream, position);
+        assert_eq!(expected, actual);
+        position += bytes_read;
+    }
+    assert_eq!(stream.len(), position);
+}
+
+#[test]
+fn test_signed_leb128() {
+    let mut values = Vec::new();
+
+    let mut i = -500;
+    while i < 500 {
+        values.push(i * 123457i64);
+        i += 1;
+    }
+
+    let mut stream = Vec::new();
+
+    for &x in &values {
+        let pos = stream.len();
+        let bytes_written = write_signed_leb128(&mut stream, pos, x);
+        assert_eq!(stream.len(), pos + bytes_written);
+    }
+
+    let mut pos = 0;
+    for &x in &values {
+        let (value, bytes_read) = read_signed_leb128(&mut stream, pos);
+        pos += bytes_read;
+        assert_eq!(x, value);
+    }
+    assert_eq!(pos, stream.len());
+}
index 9cb160b7decfff2334b7ce396e6b0498c23d1d6d..913314c4899a2260c69eb34c8cd5bb3c07440fb0 100644 (file)
 //!
 //! First 0x20 tags are reserved by RBML; custom tags start at 0x20.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rbml"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
 
 #![feature(rustc_private)]
 #![feature(staged_api)]
-#![feature(clone_from_slice)]
 
 #![cfg_attr(test, feature(test))]
 
 extern crate serialize;
-#[macro_use] extern crate log;
 
-#[cfg(test)] extern crate test;
+#[cfg(test)]
+extern crate serialize as rustc_serialize; // Used by RustcEncodable
+
+#[macro_use]
+extern crate log;
+
+#[cfg(test)]
+extern crate test;
+
+pub mod opaque;
+pub mod leb128;
 
 pub use self::EbmlEncoderTag::*;
 pub use self::Error::*;
@@ -151,7 +156,11 @@ pub struct Doc<'a> {
 
 impl<'doc> Doc<'doc> {
     pub fn new(data: &'doc [u8]) -> Doc<'doc> {
-        Doc { data: data, start: 0, end: data.len() }
+        Doc {
+            data: data,
+            start: 0,
+            end: data.len(),
+        }
     }
 
     pub fn get<'a>(&'a self, tag: usize) -> Doc<'a> {
@@ -180,36 +189,35 @@ pub struct TaggedDoc<'a> {
 pub enum EbmlEncoderTag {
     // tags 00..1f are reserved for auto-serialization.
     // first NUM_IMPLICIT_TAGS tags are implicitly sized and lengths are not encoded.
-
-    EsU8       = 0x00, // + 1 byte
-    EsU16      = 0x01, // + 2 bytes
-    EsU32      = 0x02, // + 4 bytes
-    EsU64      = 0x03, // + 8 bytes
-    EsI8       = 0x04, // + 1 byte
-    EsI16      = 0x05, // + 2 bytes
-    EsI32      = 0x06, // + 4 bytes
-    EsI64      = 0x07, // + 8 bytes
-    EsBool     = 0x08, // + 1 byte
-    EsChar     = 0x09, // + 4 bytes
-    EsF32      = 0x0a, // + 4 bytes
-    EsF64      = 0x0b, // + 8 bytes
-    EsSub8     = 0x0c, // + 1 byte
-    EsSub32    = 0x0d, // + 4 bytes
+    EsU8 = 0x00, // + 1 byte
+    EsU16 = 0x01, // + 2 bytes
+    EsU32 = 0x02, // + 4 bytes
+    EsU64 = 0x03, // + 8 bytes
+    EsI8 = 0x04, // + 1 byte
+    EsI16 = 0x05, // + 2 bytes
+    EsI32 = 0x06, // + 4 bytes
+    EsI64 = 0x07, // + 8 bytes
+    EsBool = 0x08, // + 1 byte
+    EsChar = 0x09, // + 4 bytes
+    EsF32 = 0x0a, // + 4 bytes
+    EsF64 = 0x0b, // + 8 bytes
+    EsSub8 = 0x0c, // + 1 byte
+    EsSub32 = 0x0d, // + 4 bytes
     // 0x0e and 0x0f are reserved
-
-    EsStr      = 0x10,
-    EsEnum     = 0x11, // encodes the variant id as the first EsSub*
-    EsVec      = 0x12, // encodes the # of elements as the first EsSub*
-    EsVecElt   = 0x13,
-    EsMap      = 0x14, // encodes the # of pairs as the first EsSub*
-    EsMapKey   = 0x15,
-    EsMapVal   = 0x16,
-    EsOpaque   = 0x17,
+    EsStr = 0x10,
+    EsEnum = 0x11, // encodes the variant id as the first EsSub*
+    EsVec = 0x12, // encodes the # of elements as the first EsSub*
+    EsVecElt = 0x13,
+    EsMap = 0x14, // encodes the # of pairs as the first EsSub*
+    EsMapKey = 0x15,
+    EsMapVal = 0x16,
+    EsOpaque = 0x17,
 }
 
 const NUM_TAGS: usize = 0x1000;
 const NUM_IMPLICIT_TAGS: usize = 0x0e;
 
+#[cfg_attr(rustfmt, rustfmt_skip)]
 static TAG_IMPLICIT_LEN: [i8; NUM_IMPLICIT_TAGS] = [
     1, 2, 4, 8, // EsU*
     1, 2, 4, 8, // ESI*
@@ -225,7 +233,7 @@ pub enum Error {
     InvalidTag(usize),
     Expected(String),
     IoError(std::io::Error),
-    ApplicationError(String)
+    ApplicationError(String),
 }
 
 impl fmt::Display for Error {
@@ -244,11 +252,11 @@ pub mod reader {
 
     use serialize;
 
-    use super::{ ApplicationError, EsVec, EsMap, EsEnum, EsSub8, EsSub32,
-        EsVecElt, EsMapKey, EsU64, EsU32, EsU16, EsU8, EsI64,
-        EsI32, EsI16, EsI8, EsBool, EsF64, EsF32, EsChar, EsStr, EsMapVal,
-        EsOpaque, EbmlEncoderTag, Doc, TaggedDoc,
-        Error, IntTooBig, InvalidTag, Expected, NUM_IMPLICIT_TAGS, TAG_IMPLICIT_LEN };
+    use super::opaque;
+    use super::{ApplicationError, EsVec, EsMap, EsEnum, EsSub8, EsSub32, EsVecElt, EsMapKey,
+                EsU64, EsU32, EsU16, EsU8, EsI64, EsI32, EsI16, EsI8, EsBool, EsF64, EsF32,
+                EsChar, EsStr, EsMapVal, EsOpaque, EbmlEncoderTag, Doc, TaggedDoc, Error,
+                IntTooBig, InvalidTag, Expected, NUM_IMPLICIT_TAGS, TAG_IMPLICIT_LEN};
 
     pub type DecodeResult<T> = Result<T, Error>;
     // rbml reading
@@ -268,15 +276,21 @@ pub mod reader {
     #[derive(Copy, Clone)]
     pub struct Res {
         pub val: usize,
-        pub next: usize
+        pub next: usize,
     }
 
     pub fn tag_at(data: &[u8], start: usize) -> DecodeResult<Res> {
         let v = data[start] as usize;
         if v < 0xf0 {
-            Ok(Res { val: v, next: start + 1 })
+            Ok(Res {
+                val: v,
+                next: start + 1,
+            })
         } else if v > 0xf0 {
-            Ok(Res { val: ((v & 0xf) << 8) | data[start + 1] as usize, next: start + 2 })
+            Ok(Res {
+                val: ((v & 0xf) << 8) | data[start + 1] as usize,
+                next: start + 2,
+            })
         } else {
             // every tag starting with byte 0xf0 is an overlong form, which is prohibited.
             Err(InvalidTag(v))
@@ -287,25 +301,31 @@ pub mod reader {
     fn vuint_at_slow(data: &[u8], start: usize) -> DecodeResult<Res> {
         let a = data[start];
         if a & 0x80 != 0 {
-            return Ok(Res {val: (a & 0x7f) as usize, next: start + 1});
+            return Ok(Res {
+                val: (a & 0x7f) as usize,
+                next: start + 1,
+            });
         }
         if a & 0x40 != 0 {
-            return Ok(Res {val: ((a & 0x3f) as usize) << 8 |
-                        (data[start + 1] as usize),
-                    next: start + 2});
+            return Ok(Res {
+                val: ((a & 0x3f) as usize) << 8 | (data[start + 1] as usize),
+                next: start + 2,
+            });
         }
         if a & 0x20 != 0 {
-            return Ok(Res {val: ((a & 0x1f) as usize) << 16 |
-                        (data[start + 1] as usize) << 8 |
-                        (data[start + 2] as usize),
-                    next: start + 3});
+            return Ok(Res {
+                val: ((a & 0x1f) as usize) << 16 | (data[start + 1] as usize) << 8 |
+                     (data[start + 2] as usize),
+                next: start + 3,
+            });
         }
         if a & 0x10 != 0 {
-            return Ok(Res {val: ((a & 0x0f) as usize) << 24 |
-                        (data[start + 1] as usize) << 16 |
-                        (data[start + 2] as usize) << 8 |
-                        (data[start + 3] as usize),
-                    next: start + 4});
+            return Ok(Res {
+                val: ((a & 0x0f) as usize) << 24 | (data[start + 1] as usize) << 16 |
+                     (data[start + 2] as usize) << 8 |
+                     (data[start + 3] as usize),
+                next: start + 4,
+            });
         }
         Err(IntTooBig(a as usize))
     }
@@ -334,13 +354,22 @@ pub mod reader {
         // most significant bit is set etc. we can replace up to three
         // "and+branch" with a single table lookup which gives us a measured
         // speedup of around 2x on x86_64.
-        static SHIFT_MASK_TABLE: [(usize, u32); 16] = [
-            (0, 0x0), (0, 0x0fffffff),
-            (8, 0x1fffff), (8, 0x1fffff),
-            (16, 0x3fff), (16, 0x3fff), (16, 0x3fff), (16, 0x3fff),
-            (24, 0x7f), (24, 0x7f), (24, 0x7f), (24, 0x7f),
-            (24, 0x7f), (24, 0x7f), (24, 0x7f), (24, 0x7f)
-        ];
+        static SHIFT_MASK_TABLE: [(usize, u32); 16] = [(0, 0x0),
+                                                       (0, 0x0fffffff),
+                                                       (8, 0x1fffff),
+                                                       (8, 0x1fffff),
+                                                       (16, 0x3fff),
+                                                       (16, 0x3fff),
+                                                       (16, 0x3fff),
+                                                       (16, 0x3fff),
+                                                       (24, 0x7f),
+                                                       (24, 0x7f),
+                                                       (24, 0x7f),
+                                                       (24, 0x7f),
+                                                       (24, 0x7f),
+                                                       (24, 0x7f),
+                                                       (24, 0x7f),
+                                                       (24, 0x7f)];
 
         unsafe {
             let ptr = data.as_ptr().offset(start as isize) as *const u32;
@@ -357,7 +386,10 @@ pub mod reader {
 
     pub fn tag_len_at(data: &[u8], tag: Res) -> DecodeResult<Res> {
         if tag.val < NUM_IMPLICIT_TAGS && TAG_IMPLICIT_LEN[tag.val] >= 0 {
-            Ok(Res { val: TAG_IMPLICIT_LEN[tag.val] as usize, next: tag.next })
+            Ok(Res {
+                val: TAG_IMPLICIT_LEN[tag.val] as usize,
+                next: tag.next,
+            })
         } else {
             vuint_at(data, tag.next)
         }
@@ -369,7 +401,11 @@ pub mod reader {
         let end = elt_size.next + elt_size.val;
         Ok(TaggedDoc {
             tag: elt_tag.val,
-            doc: Doc { data: data, start: elt_size.next, end: end }
+            doc: Doc {
+                data: data,
+                start: elt_size.next,
+                end: end,
+            },
         })
     }
 
@@ -380,8 +416,11 @@ pub mod reader {
             let elt_size = try_or!(tag_len_at(d.data, elt_tag), None);
             pos = elt_size.next + elt_size.val;
             if elt_tag.val == tg {
-                return Some(Doc { data: d.data, start: elt_size.next,
-                                  end: pos });
+                return Some(Doc {
+                    data: d.data,
+                    start: elt_size.next,
+                    end: pos,
+                });
             }
         }
         None
@@ -398,9 +437,7 @@ pub mod reader {
     }
 
     pub fn docs<'a>(d: Doc<'a>) -> DocsIterator<'a> {
-        DocsIterator {
-            d: d
-        }
+        DocsIterator { d: d }
     }
 
     pub struct DocsIterator<'a> {
@@ -461,8 +498,8 @@ pub mod reader {
         }
     }
 
-    pub fn with_doc_data<T, F>(d: Doc, f: F) -> T where
-        F: FnOnce(&[u8]) -> T,
+    pub fn with_doc_data<T, F>(d: Doc, f: F) -> T
+        where F: FnOnce(&[u8]) -> T
     {
         f(&d.data[d.start..d.end])
     }
@@ -481,30 +518,48 @@ pub mod reader {
             // of the page and segfault.
 
             let mut b = [0; 8];
-            b.clone_from_slice(&d.data[d.end-8..d.end]);
+            b.clone_from_slice(&d.data[d.end - 8..d.end]);
             let data = unsafe { (*(b.as_ptr() as *const u64)).to_be() };
             let len = d.end - d.start;
             if len < 8 {
-                data & ((1<<(len*8))-1)
+                data & ((1 << (len * 8)) - 1)
             } else {
                 data
             }
         } else {
             let mut result = 0;
             for b in &d.data[d.start..d.end] {
-                result = (result<<8) + (*b as u64);
+                result = (result << 8) + (*b as u64);
             }
             result
         }
     }
 
-    #[inline] pub fn doc_as_u16(d: Doc) -> u16 { doc_as_u64(d) as u16 }
-    #[inline] pub fn doc_as_u32(d: Doc) -> u32 { doc_as_u64(d) as u32 }
+    #[inline]
+    pub fn doc_as_u16(d: Doc) -> u16 {
+        doc_as_u64(d) as u16
+    }
+    #[inline]
+    pub fn doc_as_u32(d: Doc) -> u32 {
+        doc_as_u64(d) as u32
+    }
 
-    #[inline] pub fn doc_as_i8(d: Doc) -> i8 { doc_as_u8(d) as i8 }
-    #[inline] pub fn doc_as_i16(d: Doc) -> i16 { doc_as_u16(d) as i16 }
-    #[inline] pub fn doc_as_i32(d: Doc) -> i32 { doc_as_u32(d) as i32 }
-    #[inline] pub fn doc_as_i64(d: Doc) -> i64 { doc_as_u64(d) as i64 }
+    #[inline]
+    pub fn doc_as_i8(d: Doc) -> i8 {
+        doc_as_u8(d) as i8
+    }
+    #[inline]
+    pub fn doc_as_i16(d: Doc) -> i16 {
+        doc_as_u16(d) as i16
+    }
+    #[inline]
+    pub fn doc_as_i32(d: Doc) -> i32 {
+        doc_as_u32(d) as i32
+    }
+    #[inline]
+    pub fn doc_as_i64(d: Doc) -> i64 {
+        doc_as_u64(d) as i64
+    }
 
     pub struct Decoder<'a> {
         parent: Doc<'a>,
@@ -515,18 +570,16 @@ pub mod reader {
         pub fn new(d: Doc<'doc>) -> Decoder<'doc> {
             Decoder {
                 parent: d,
-                pos: d.start
+                pos: d.start,
             }
         }
 
         fn next_doc(&mut self, exp_tag: EbmlEncoderTag) -> DecodeResult<Doc<'doc>> {
             debug!(". next_doc(exp_tag={:?})", exp_tag);
             if self.pos >= self.parent.end {
-                return Err(Expected(format!("no more documents in \
-                                             current node!")));
+                return Err(Expected(format!("no more documents in current node!")));
             }
-            let TaggedDoc { tag: r_tag, doc: r_doc } =
-                try!(doc_at(self.parent.data, self.pos));
+            let TaggedDoc { tag: r_tag, doc: r_doc } = try!(doc_at(self.parent.data, self.pos));
             debug!("self.parent={:?}-{:?} self.pos={:?} r_tag={:?} r_doc={:?}-{:?}",
                    self.parent.start,
                    self.parent.end,
@@ -535,20 +588,22 @@ pub mod reader {
                    r_doc.start,
                    r_doc.end);
             if r_tag != (exp_tag as usize) {
-                return Err(Expected(format!("expected EBML doc with tag {:?} but \
-                                             found tag {:?}", exp_tag, r_tag)));
+                return Err(Expected(format!("expected EBML doc with tag {:?} but found tag {:?}",
+                                            exp_tag,
+                                            r_tag)));
             }
             if r_doc.end > self.parent.end {
-                return Err(Expected(format!("invalid EBML, child extends to \
-                                             {:#x}, parent to {:#x}",
-                                            r_doc.end, self.parent.end)));
+                return Err(Expected(format!("invalid EBML, child extends to {:#x}, parent to \
+                                             {:#x}",
+                                            r_doc.end,
+                                            self.parent.end)));
             }
             self.pos = r_doc.end;
             Ok(r_doc)
         }
 
-        fn push_doc<T, F>(&mut self, exp_tag: EbmlEncoderTag, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+        fn push_doc<T, F>(&mut self, exp_tag: EbmlEncoderTag, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
             let d = try!(self.next_doc(exp_tag));
             let old_parent = self.parent;
@@ -567,20 +622,23 @@ pub mod reader {
                 return Ok(0);
             }
 
-            let TaggedDoc { tag: r_tag, doc: r_doc } =
-                try!(doc_at(self.parent.data, self.pos));
+            let TaggedDoc { tag: r_tag, doc: r_doc } = try!(doc_at(self.parent.data, self.pos));
             let r = if r_tag == (EsSub8 as usize) {
                 doc_as_u8(r_doc) as usize
             } else if r_tag == (EsSub32 as usize) {
                 doc_as_u32(r_doc) as usize
             } else {
-                return Err(Expected(format!("expected EBML doc with tag {:?} or {:?} but \
-                                             found tag {:?}", EsSub8, EsSub32, r_tag)));
+                return Err(Expected(format!("expected EBML doc with tag {:?} or {:?} but found \
+                                             tag {:?}",
+                                            EsSub8,
+                                            EsSub32,
+                                            r_tag)));
             };
             if r_doc.end > self.parent.end {
-                return Err(Expected(format!("invalid EBML, child extends to \
-                                             {:#x}, parent to {:#x}",
-                                            r_doc.end, self.parent.end)));
+                return Err(Expected(format!("invalid EBML, child extends to {:#x}, parent to \
+                                             {:#x}",
+                                            r_doc.end,
+                                            self.parent.end)));
             }
             self.pos = r_doc.end;
             debug!("_next_sub result={:?}", r);
@@ -593,14 +651,13 @@ pub mod reader {
         // all tags between them should be valid, in the order of u8, u16, u32 and u64.
         fn _next_int(&mut self,
                      first_tag: EbmlEncoderTag,
-                     last_tag: EbmlEncoderTag) -> DecodeResult<u64> {
+                     last_tag: EbmlEncoderTag)
+                     -> DecodeResult<u64> {
             if self.pos >= self.parent.end {
-                return Err(Expected(format!("no more documents in \
-                                             current node!")));
+                return Err(Expected(format!("no more documents in current node!")));
             }
 
-            let TaggedDoc { tag: r_tag, doc: r_doc } =
-                try!(doc_at(self.parent.data, self.pos));
+            let TaggedDoc { tag: r_tag, doc: r_doc } = try!(doc_at(self.parent.data, self.pos));
             let r = if first_tag as usize <= r_tag && r_tag <= last_tag as usize {
                 match r_tag - first_tag as usize {
                     0 => doc_as_u8(r_doc) as u64,
@@ -611,43 +668,62 @@ pub mod reader {
                 }
             } else {
                 return Err(Expected(format!("expected EBML doc with tag {:?} through {:?} but \
-                                             found tag {:?}", first_tag, last_tag, r_tag)));
+                                             found tag {:?}",
+                                            first_tag,
+                                            last_tag,
+                                            r_tag)));
             };
             if r_doc.end > self.parent.end {
-                return Err(Expected(format!("invalid EBML, child extends to \
-                                             {:#x}, parent to {:#x}",
-                                            r_doc.end, self.parent.end)));
+                return Err(Expected(format!("invalid EBML, child extends to {:#x}, parent to \
+                                             {:#x}",
+                                            r_doc.end,
+                                            self.parent.end)));
             }
             self.pos = r_doc.end;
             debug!("_next_int({:?}, {:?}) result={:?}", first_tag, last_tag, r);
             Ok(r)
         }
 
-        pub fn read_opaque<R, F>(&mut self, op: F) -> DecodeResult<R> where
-            F: FnOnce(&mut Decoder, Doc) -> DecodeResult<R>,
+        pub fn read_opaque<R, F>(&mut self, op: F) -> DecodeResult<R>
+            where F: FnOnce(&mut opaque::Decoder, Doc) -> DecodeResult<R>
         {
             let doc = try!(self.next_doc(EsOpaque));
 
-            let (old_parent, old_pos) = (self.parent, self.pos);
-            self.parent = doc;
-            self.pos = doc.start;
-
-            let result = try!(op(self, doc));
+            let result = {
+                let mut opaque_decoder = opaque::Decoder::new(doc.data, doc.start);
+                try!(op(&mut opaque_decoder, doc))
+            };
 
-            self.parent = old_parent;
-            self.pos = old_pos;
             Ok(result)
         }
+
+        pub fn position(&self) -> usize {
+            self.pos
+        }
+
+        pub fn advance(&mut self, bytes: usize) {
+            self.pos += bytes;
+        }
     }
 
     impl<'doc> serialize::Decoder for Decoder<'doc> {
         type Error = Error;
-        fn read_nil(&mut self) -> DecodeResult<()> { Ok(()) }
+        fn read_nil(&mut self) -> DecodeResult<()> {
+            Ok(())
+        }
 
-        fn read_u64(&mut self) -> DecodeResult<u64> { self._next_int(EsU8, EsU64) }
-        fn read_u32(&mut self) -> DecodeResult<u32> { Ok(try!(self._next_int(EsU8, EsU32)) as u32) }
-        fn read_u16(&mut self) -> DecodeResult<u16> { Ok(try!(self._next_int(EsU8, EsU16)) as u16) }
-        fn read_u8(&mut self) -> DecodeResult<u8> { Ok(doc_as_u8(try!(self.next_doc(EsU8)))) }
+        fn read_u64(&mut self) -> DecodeResult<u64> {
+            self._next_int(EsU8, EsU64)
+        }
+        fn read_u32(&mut self) -> DecodeResult<u32> {
+            Ok(try!(self._next_int(EsU8, EsU32)) as u32)
+        }
+        fn read_u16(&mut self) -> DecodeResult<u16> {
+            Ok(try!(self._next_int(EsU8, EsU16)) as u16)
+        }
+        fn read_u8(&mut self) -> DecodeResult<u8> {
+            Ok(doc_as_u8(try!(self.next_doc(EsU8))))
+        }
         fn read_uint(&mut self) -> DecodeResult<usize> {
             let v = try!(self._next_int(EsU8, EsU64));
             if v > (::std::usize::MAX as u64) {
@@ -657,10 +733,18 @@ pub mod reader {
             }
         }
 
-        fn read_i64(&mut self) -> DecodeResult<i64> { Ok(try!(self._next_int(EsI8, EsI64)) as i64) }
-        fn read_i32(&mut self) -> DecodeResult<i32> { Ok(try!(self._next_int(EsI8, EsI32)) as i32) }
-        fn read_i16(&mut self) -> DecodeResult<i16> { Ok(try!(self._next_int(EsI8, EsI16)) as i16) }
-        fn read_i8(&mut self) -> DecodeResult<i8> { Ok(doc_as_u8(try!(self.next_doc(EsI8))) as i8) }
+        fn read_i64(&mut self) -> DecodeResult<i64> {
+            Ok(try!(self._next_int(EsI8, EsI64)) as i64)
+        }
+        fn read_i32(&mut self) -> DecodeResult<i32> {
+            Ok(try!(self._next_int(EsI8, EsI32)) as i32)
+        }
+        fn read_i16(&mut self) -> DecodeResult<i16> {
+            Ok(try!(self._next_int(EsI8, EsI16)) as i16)
+        }
+        fn read_i8(&mut self) -> DecodeResult<i8> {
+            Ok(doc_as_u8(try!(self.next_doc(EsI8))) as i8)
+        }
         fn read_int(&mut self) -> DecodeResult<isize> {
             let v = try!(self._next_int(EsI8, EsI64)) as i64;
             if v > (isize::MAX as i64) || v < (isize::MIN as i64) {
@@ -691,8 +775,8 @@ pub mod reader {
         }
 
         // Compound types:
-        fn read_enum<T, F>(&mut self, name: &str, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+        fn read_enum<T, F>(&mut self, name: &str, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
             debug!("read_enum({})", name);
 
@@ -709,9 +793,8 @@ pub mod reader {
             Ok(result)
         }
 
-        fn read_enum_variant<T, F>(&mut self, _: &[&str],
-                                   mut f: F) -> DecodeResult<T>
-            where F: FnMut(&mut Decoder<'doc>, usize) -> DecodeResult<T>,
+        fn read_enum_variant<T, F>(&mut self, _: &[&str], mut f: F) -> DecodeResult<T>
+            where F: FnMut(&mut Decoder<'doc>, usize) -> DecodeResult<T>
         {
             debug!("read_enum_variant()");
             let idx = try!(self._next_sub());
@@ -720,16 +803,15 @@ pub mod reader {
             f(self, idx)
         }
 
-        fn read_enum_variant_arg<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+        fn read_enum_variant_arg<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
             debug!("read_enum_variant_arg(idx={})", idx);
             f(self)
         }
 
-        fn read_enum_struct_variant<T, F>(&mut self, _: &[&str],
-                                          mut f: F) -> DecodeResult<T>
-            where F: FnMut(&mut Decoder<'doc>, usize) -> DecodeResult<T>,
+        fn read_enum_struct_variant<T, F>(&mut self, _: &[&str], mut f: F) -> DecodeResult<T>
+            where F: FnMut(&mut Decoder<'doc>, usize) -> DecodeResult<T>
         {
             debug!("read_enum_struct_variant()");
             let idx = try!(self._next_sub());
@@ -742,67 +824,66 @@ pub mod reader {
                                                 name: &str,
                                                 idx: usize,
                                                 f: F)
-                                                -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+                                                -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
-                debug!("read_enum_struct_variant_arg(name={}, idx={})", name, idx);
+            debug!("read_enum_struct_variant_arg(name={}, idx={})", name, idx);
             f(self)
         }
 
-        fn read_struct<T, F>(&mut self, name: &str, _: usize, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+        fn read_struct<T, F>(&mut self, name: &str, _: usize, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
             debug!("read_struct(name={})", name);
             f(self)
         }
 
-        fn read_struct_field<T, F>(&mut self, name: &str, idx: usize, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+        fn read_struct_field<T, F>(&mut self, name: &str, idx: usize, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
             debug!("read_struct_field(name={}, idx={})", name, idx);
             f(self)
         }
 
-        fn read_tuple<T, F>(&mut self, tuple_len: usize, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+        fn read_tuple<T, F>(&mut self, tuple_len: usize, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
             debug!("read_tuple()");
             self.read_seq(move |d, len| {
                 if len == tuple_len {
                     f(d)
                 } else {
-                    Err(Expected(format!("Expected tuple of length `{}`, \
-                                          found tuple of length `{}`", tuple_len, len)))
+                    Err(Expected(format!("Expected tuple of length `{}`, found tuple of length \
+                                          `{}`",
+                                         tuple_len,
+                                         len)))
                 }
             })
         }
 
-        fn read_tuple_arg<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+        fn read_tuple_arg<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
             debug!("read_tuple_arg(idx={})", idx);
             self.read_seq_elt(idx, f)
         }
 
-        fn read_tuple_struct<T, F>(&mut self, name: &str, len: usize, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+        fn read_tuple_struct<T, F>(&mut self, name: &str, len: usize, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
             debug!("read_tuple_struct(name={})", name);
             self.read_tuple(len, f)
         }
 
-        fn read_tuple_struct_arg<T, F>(&mut self,
-                                       idx: usize,
-                                       f: F)
-                                       -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+        fn read_tuple_struct_arg<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
             debug!("read_tuple_struct_arg(idx={})", idx);
             self.read_tuple_arg(idx, f)
         }
 
-        fn read_option<T, F>(&mut self, mut f: F) -> DecodeResult<T> where
-            F: FnMut(&mut Decoder<'doc>, bool) -> DecodeResult<T>,
+        fn read_option<T, F>(&mut self, mut f: F) -> DecodeResult<T>
+            where F: FnMut(&mut Decoder<'doc>, bool) -> DecodeResult<T>
         {
             debug!("read_option()");
             self.read_enum("Option", move |this| {
@@ -810,16 +891,14 @@ pub mod reader {
                     match idx {
                         0 => f(this, false),
                         1 => f(this, true),
-                        _ => {
-                            Err(Expected(format!("Expected None or Some")))
-                        }
+                        _ => Err(Expected(format!("Expected None or Some"))),
                     }
                 })
             })
         }
 
-        fn read_seq<T, F>(&mut self, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>, usize) -> DecodeResult<T>,
+        fn read_seq<T, F>(&mut self, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>, usize) -> DecodeResult<T>
         {
             debug!("read_seq()");
             self.push_doc(EsVec, move |d| {
@@ -829,15 +908,15 @@ pub mod reader {
             })
         }
 
-        fn read_seq_elt<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+        fn read_seq_elt<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
             debug!("read_seq_elt(idx={})", idx);
             self.push_doc(EsVecElt, f)
         }
 
-        fn read_map<T, F>(&mut self, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>, usize) -> DecodeResult<T>,
+        fn read_map<T, F>(&mut self, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>, usize) -> DecodeResult<T>
         {
             debug!("read_map()");
             self.push_doc(EsMap, move |d| {
@@ -847,15 +926,15 @@ pub mod reader {
             })
         }
 
-        fn read_map_elt_key<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+        fn read_map_elt_key<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
             debug!("read_map_elt_key(idx={})", idx);
             self.push_doc(EsMapKey, f)
         }
 
-        fn read_map_elt_val<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T> where
-            F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>,
+        fn read_map_elt_val<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T>
+            where F: FnOnce(&mut Decoder<'doc>) -> DecodeResult<T>
         {
             debug!("read_map_elt_val(idx={})", idx);
             self.push_doc(EsMapVal, f)
@@ -872,10 +951,10 @@ pub mod writer {
     use std::io::prelude::*;
     use std::io::{self, SeekFrom, Cursor};
 
-    use super::{ EsVec, EsMap, EsEnum, EsSub8, EsSub32, EsVecElt, EsMapKey,
-        EsU64, EsU32, EsU16, EsU8, EsI64, EsI32, EsI16, EsI8,
-        EsBool, EsF64, EsF32, EsChar, EsStr, EsMapVal,
-        EsOpaque, NUM_IMPLICIT_TAGS, NUM_TAGS };
+    use super::opaque;
+    use super::{EsVec, EsMap, EsEnum, EsSub8, EsSub32, EsVecElt, EsMapKey, EsU64, EsU32, EsU16,
+                EsU8, EsI64, EsI32, EsI16, EsI8, EsBool, EsF64, EsF32, EsChar, EsStr, EsMapVal,
+                EsOpaque, NUM_IMPLICIT_TAGS, NUM_TAGS};
 
     use serialize;
 
@@ -895,8 +974,7 @@ pub mod writer {
         } else if 0x100 <= n && n < NUM_TAGS {
             w.write_all(&[0xf0 | (n >> 8) as u8, n as u8])
         } else {
-            Err(io::Error::new(io::ErrorKind::Other,
-                               &format!("invalid tag: {}", n)[..]))
+            Err(io::Error::new(io::ErrorKind::Other, &format!("invalid tag: {}", n)[..]))
         }
     }
 
@@ -904,29 +982,33 @@ pub mod writer {
         match size {
             1 => w.write_all(&[0x80 | (n as u8)]),
             2 => w.write_all(&[0x40 | ((n >> 8) as u8), n as u8]),
-            3 => w.write_all(&[0x20 | ((n >> 16) as u8), (n >> 8) as u8,
-                            n as u8]),
-            4 => w.write_all(&[0x10 | ((n >> 24) as u8), (n >> 16) as u8,
-                            (n >> 8) as u8, n as u8]),
-            _ => Err(io::Error::new(io::ErrorKind::Other,
-                                    &format!("isize too big: {}", n)[..]))
+            3 => w.write_all(&[0x20 | ((n >> 16) as u8), (n >> 8) as u8, n as u8]),
+            4 => w.write_all(&[0x10 | ((n >> 24) as u8), (n >> 16) as u8, (n >> 8) as u8, n as u8]),
+            _ => Err(io::Error::new(io::ErrorKind::Other, &format!("isize too big: {}", n)[..])),
         }
     }
 
     pub fn write_vuint<W: Write>(w: &mut W, n: usize) -> EncodeResult {
-        if n < 0x7f { return write_sized_vuint(w, n, 1); }
-        if n < 0x4000 { return write_sized_vuint(w, n, 2); }
-        if n < 0x200000 { return write_sized_vuint(w, n, 3); }
-        if n < 0x10000000 { return write_sized_vuint(w, n, 4); }
-        Err(io::Error::new(io::ErrorKind::Other,
-                           &format!("isize too big: {}", n)[..]))
+        if n < 0x7f {
+            return write_sized_vuint(w, n, 1);
+        }
+        if n < 0x4000 {
+            return write_sized_vuint(w, n, 2);
+        }
+        if n < 0x200000 {
+            return write_sized_vuint(w, n, 3);
+        }
+        if n < 0x10000000 {
+            return write_sized_vuint(w, n, 4);
+        }
+        Err(io::Error::new(io::ErrorKind::Other, &format!("isize too big: {}", n)[..]))
     }
 
     impl<'a> Encoder<'a> {
         pub fn new(w: &'a mut Cursor<Vec<u8>>) -> Encoder<'a> {
             Encoder {
                 writer: w,
-                size_positions: vec!(),
+                size_positions: vec![],
                 relax_limit: 0,
             }
         }
@@ -959,8 +1041,8 @@ pub mod writer {
                 let mut buf = [0u8; RELAX_MAX_SIZE];
                 {
                     let last_size_pos = last_size_pos as usize;
-                    let data = &self.writer.get_ref()[last_size_pos+4..cur_pos as usize];
-                    buf.clone_from_slice(data);
+                    let data = &self.writer.get_ref()[last_size_pos + 4..cur_pos as usize];
+                    buf[..size].clone_from_slice(data);
                 }
 
                 // overwrite the size and data and continue
@@ -976,8 +1058,8 @@ pub mod writer {
             Ok(())
         }
 
-        pub fn wr_tag<F>(&mut self, tag_id: usize, blk: F) -> EncodeResult where
-            F: FnOnce() -> EncodeResult,
+        pub fn wr_tag<F>(&mut self, tag_id: usize, blk: F) -> EncodeResult
+            where F: FnOnce() -> EncodeResult
         {
             try!(self.start_tag(tag_id));
             try!(blk());
@@ -995,12 +1077,12 @@ pub mod writer {
             let bytes: [u8; 8] = unsafe { mem::transmute(v.to_be()) };
             // tagged integers are emitted in big-endian, with no
             // leading zeros.
-            let leading_zero_bytes = v.leading_zeros()/8;
+            let leading_zero_bytes = v.leading_zeros() / 8;
             self.wr_tagged_bytes(tag_id, &bytes[leading_zero_bytes as usize..])
         }
 
         #[inline]
-        pub fn wr_tagged_u32(&mut self, tag_id: usize, v: u32)  -> EncodeResult {
+        pub fn wr_tagged_u32(&mut self, tag_id: usize, v: u32) -> EncodeResult {
             self.wr_tagged_u64(tag_id, v as u64)
         }
 
@@ -1049,7 +1131,7 @@ pub mod writer {
             self.wr_tagged_raw_bytes(tag_id, &bytes)
         }
 
-        fn wr_tagged_raw_u32(&mut self, tag_id: usize, v: u32)  -> EncodeResult{
+        fn wr_tagged_raw_u32(&mut self, tag_id: usize, v: u32) -> EncodeResult {
             let bytes: [u8; 4] = unsafe { mem::transmute(v.to_be()) };
             self.wr_tagged_raw_bytes(tag_id, &bytes)
         }
@@ -1109,16 +1191,21 @@ pub mod writer {
                 self.wr_tagged_raw_u32(EsSub32 as usize, v as u32)
             } else {
                 Err(io::Error::new(io::ErrorKind::Other,
-                                   &format!("length or variant id too big: {}",
-                                            v)[..]))
+                                   &format!("length or variant id too big: {}", v)[..]))
             }
         }
 
-        pub fn emit_opaque<F>(&mut self, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder) -> EncodeResult,
+        pub fn emit_opaque<F>(&mut self, f: F) -> EncodeResult
+            where F: FnOnce(&mut opaque::Encoder) -> EncodeResult
         {
             try!(self.start_tag(EsOpaque as usize));
-            try!(f(self));
+
+            {
+                let mut opaque_encoder = opaque::Encoder::new(self.writer);
+                try!(f(&mut opaque_encoder));
+            }
+
+            self.mark_stable_position();
             self.end_tag()
         }
     }
@@ -1206,27 +1293,23 @@ pub mod writer {
             self.wr_tagged_str(EsStr as usize, v)
         }
 
-        fn emit_enum<F>(&mut self, _name: &str, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_enum<F>(&mut self, _name: &str, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             try!(self.start_tag(EsEnum as usize));
             try!(f(self));
             self.end_tag()
         }
 
-        fn emit_enum_variant<F>(&mut self,
-                                _: &str,
-                                v_id: usize,
-                                _: usize,
-                                f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_enum_variant<F>(&mut self, _: &str, v_id: usize, _: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             try!(self._emit_tagged_sub(v_id));
             f(self)
         }
 
-        fn emit_enum_variant_arg<F>(&mut self, _: usize, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_enum_variant_arg<F>(&mut self, _: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             f(self)
         }
@@ -1235,72 +1318,70 @@ pub mod writer {
                                        v_name: &str,
                                        v_id: usize,
                                        cnt: usize,
-                                       f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+                                       f: F)
+                                       -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             self.emit_enum_variant(v_name, v_id, cnt, f)
         }
 
-        fn emit_enum_struct_variant_field<F>(&mut self,
-                                             _: &str,
-                                             idx: usize,
-                                             f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_enum_struct_variant_field<F>(&mut self, _: &str, idx: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             self.emit_enum_variant_arg(idx, f)
         }
 
-        fn emit_struct<F>(&mut self, _: &str, _len: usize, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_struct<F>(&mut self, _: &str, _len: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             f(self)
         }
 
-        fn emit_struct_field<F>(&mut self, _name: &str, _: usize, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_struct_field<F>(&mut self, _name: &str, _: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             f(self)
         }
 
-        fn emit_tuple<F>(&mut self, len: usize, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_tuple<F>(&mut self, len: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             self.emit_seq(len, f)
         }
-        fn emit_tuple_arg<F>(&mut self, idx: usize, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_tuple_arg<F>(&mut self, idx: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             self.emit_seq_elt(idx, f)
         }
 
-        fn emit_tuple_struct<F>(&mut self, _: &str, len: usize, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_tuple_struct<F>(&mut self, _: &str, len: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             self.emit_seq(len, f)
         }
-        fn emit_tuple_struct_arg<F>(&mut self, idx: usize, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_tuple_struct_arg<F>(&mut self, idx: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             self.emit_seq_elt(idx, f)
         }
 
-        fn emit_option<F>(&mut self, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_option<F>(&mut self, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             self.emit_enum("Option", f)
         }
         fn emit_option_none(&mut self) -> EncodeResult {
             self.emit_enum_variant("None", 0, 0, |_| Ok(()))
         }
-        fn emit_option_some<F>(&mut self, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_option_some<F>(&mut self, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
 
             self.emit_enum_variant("Some", 1, 1, f)
         }
 
-        fn emit_seq<F>(&mut self, len: usize, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_seq<F>(&mut self, len: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             if len == 0 {
                 // empty vector optimization
@@ -1313,8 +1394,8 @@ pub mod writer {
             self.end_tag()
         }
 
-        fn emit_seq_elt<F>(&mut self, _idx: usize, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_seq_elt<F>(&mut self, _idx: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
 
             try!(self.start_tag(EsVecElt as usize));
@@ -1322,8 +1403,8 @@ pub mod writer {
             self.end_tag()
         }
 
-        fn emit_map<F>(&mut self, len: usize, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_map<F>(&mut self, len: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             if len == 0 {
                 // empty map optimization
@@ -1336,8 +1417,8 @@ pub mod writer {
             self.end_tag()
         }
 
-        fn emit_map_elt_key<F>(&mut self, _idx: usize, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_map_elt_key<F>(&mut self, _idx: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
 
             try!(self.start_tag(EsMapKey as usize));
@@ -1345,8 +1426,8 @@ pub mod writer {
             self.end_tag()
         }
 
-        fn emit_map_elt_val<F>(&mut self, _idx: usize, f: F) -> EncodeResult where
-            F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
+        fn emit_map_elt_val<F>(&mut self, _idx: usize, f: F) -> EncodeResult
+            where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
         {
             try!(self.start_tag(EsMapVal as usize));
             try!(f(self));
@@ -1444,12 +1525,14 @@ mod bench {
 
     #[bench]
     pub fn vuint_at_A_aligned(b: &mut Bencher) {
-        let data = (0..4*100).map(|i| {
-            match i % 2 {
-              0 => 0x80,
-              _ => i as u8,
-            }
-        }).collect::<Vec<_>>();
+        let data = (0..4 * 100)
+                       .map(|i| {
+                           match i % 2 {
+                               0 => 0x80,
+                               _ => i as u8,
+                           }
+                       })
+                       .collect::<Vec<_>>();
         let mut sum = 0;
         b.iter(|| {
             let mut i = 0;
@@ -1462,12 +1545,14 @@ mod bench {
 
     #[bench]
     pub fn vuint_at_A_unaligned(b: &mut Bencher) {
-        let data = (0..4*100+1).map(|i| {
-            match i % 2 {
-              1 => 0x80,
-              _ => i as u8
-            }
-        }).collect::<Vec<_>>();
+        let data = (0..4 * 100 + 1)
+                       .map(|i| {
+                           match i % 2 {
+                               1 => 0x80,
+                               _ => i as u8,
+                           }
+                       })
+                       .collect::<Vec<_>>();
         let mut sum = 0;
         b.iter(|| {
             let mut i = 1;
@@ -1480,13 +1565,15 @@ mod bench {
 
     #[bench]
     pub fn vuint_at_D_aligned(b: &mut Bencher) {
-        let data = (0..4*100).map(|i| {
-            match i % 4 {
-              0 => 0x10,
-              3 => i as u8,
-              _ => 0
-            }
-        }).collect::<Vec<_>>();
+        let data = (0..4 * 100)
+                       .map(|i| {
+                           match i % 4 {
+                               0 => 0x10,
+                               3 => i as u8,
+                               _ => 0,
+                           }
+                       })
+                       .collect::<Vec<_>>();
         let mut sum = 0;
         b.iter(|| {
             let mut i = 0;
@@ -1499,13 +1586,15 @@ mod bench {
 
     #[bench]
     pub fn vuint_at_D_unaligned(b: &mut Bencher) {
-        let data = (0..4*100+1).map(|i| {
-            match i % 4 {
-              1 => 0x10,
-              0 => i as u8,
-              _ => 0
-            }
-        }).collect::<Vec<_>>();
+        let data = (0..4 * 100 + 1)
+                       .map(|i| {
+                           match i % 4 {
+                               1 => 0x10,
+                               0 => i as u8,
+                               _ => 0,
+                           }
+                       })
+                       .collect::<Vec<_>>();
         let mut sum = 0;
         b.iter(|| {
             let mut i = 1;
diff --git a/src/librbml/opaque.rs b/src/librbml/opaque.rs
new file mode 100644 (file)
index 0000000..531a25d
--- /dev/null
@@ -0,0 +1,805 @@
+// 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 Error as DecodeError;
+use writer::EncodeResult;
+use leb128::{read_signed_leb128, read_unsigned_leb128, write_signed_leb128, write_unsigned_leb128};
+use std::io::{self, Write};
+use serialize;
+
+// -----------------------------------------------------------------------------
+// Encoder
+// -----------------------------------------------------------------------------
+
+pub struct Encoder<'a> {
+    pub cursor: &'a mut io::Cursor<Vec<u8>>,
+}
+
+impl<'a> Encoder<'a> {
+    pub fn new(cursor: &'a mut io::Cursor<Vec<u8>>) -> Encoder<'a> {
+        Encoder { cursor: cursor }
+    }
+}
+
+
+macro_rules! write_uleb128 {
+    ($enc:expr, $value:expr) => {{
+        let pos = $enc.cursor.position() as usize;
+        let bytes_written = write_unsigned_leb128($enc.cursor.get_mut(), pos, $value as u64);
+        $enc.cursor.set_position((pos + bytes_written) as u64);
+        Ok(())
+    }}
+}
+
+macro_rules! write_sleb128 {
+    ($enc:expr, $value:expr) => {{
+        let pos = $enc.cursor.position() as usize;
+        let bytes_written = write_signed_leb128($enc.cursor.get_mut(), pos, $value as i64);
+        $enc.cursor.set_position((pos + bytes_written) as u64);
+        Ok(())
+    }}
+}
+
+impl<'a> serialize::Encoder for Encoder<'a> {
+    type Error = io::Error;
+
+    fn emit_nil(&mut self) -> EncodeResult {
+        Ok(())
+    }
+
+    fn emit_uint(&mut self, v: usize) -> EncodeResult {
+        write_uleb128!(self, v)
+    }
+
+    fn emit_u64(&mut self, v: u64) -> EncodeResult {
+        write_uleb128!(self, v)
+    }
+
+    fn emit_u32(&mut self, v: u32) -> EncodeResult {
+        write_uleb128!(self, v)
+    }
+
+    fn emit_u16(&mut self, v: u16) -> EncodeResult {
+        write_uleb128!(self, v)
+    }
+
+    fn emit_u8(&mut self, v: u8) -> EncodeResult {
+        let _ = self.cursor.write_all(&[v]);
+        Ok(())
+    }
+
+    fn emit_int(&mut self, v: isize) -> EncodeResult {
+        write_sleb128!(self, v)
+    }
+
+    fn emit_i64(&mut self, v: i64) -> EncodeResult {
+        write_sleb128!(self, v)
+    }
+
+    fn emit_i32(&mut self, v: i32) -> EncodeResult {
+        write_sleb128!(self, v)
+    }
+
+    fn emit_i16(&mut self, v: i16) -> EncodeResult {
+        write_sleb128!(self, v)
+    }
+
+    fn emit_i8(&mut self, v: i8) -> EncodeResult {
+        let as_u8: u8 = unsafe { ::std::mem::transmute(v) };
+        let _ = self.cursor.write_all(&[as_u8]);
+        Ok(())
+    }
+
+    fn emit_bool(&mut self, v: bool) -> EncodeResult {
+        self.emit_u8(if v {
+            1
+        } else {
+            0
+        })
+    }
+
+    fn emit_f64(&mut self, v: f64) -> EncodeResult {
+        let as_u64: u64 = unsafe { ::std::mem::transmute(v) };
+        self.emit_u64(as_u64)
+    }
+
+    fn emit_f32(&mut self, v: f32) -> EncodeResult {
+        let as_u32: u32 = unsafe { ::std::mem::transmute(v) };
+        self.emit_u32(as_u32)
+    }
+
+    fn emit_char(&mut self, v: char) -> EncodeResult {
+        self.emit_u32(v as u32)
+    }
+
+    fn emit_str(&mut self, v: &str) -> EncodeResult {
+        try!(self.emit_uint(v.len()));
+        let _ = self.cursor.write_all(v.as_bytes());
+        Ok(())
+    }
+
+    fn emit_enum<F>(&mut self, _name: &str, f: F) -> EncodeResult
+        where F: FnOnce(&mut Self) -> EncodeResult
+    {
+        f(self)
+    }
+
+    fn emit_enum_variant<F>(&mut self,
+                            _v_name: &str,
+                            v_id: usize,
+                            _len: usize,
+                            f: F)
+                            -> EncodeResult
+        where F: FnOnce(&mut Self) -> EncodeResult
+    {
+        try!(self.emit_uint(v_id));
+        f(self)
+    }
+
+    fn emit_enum_variant_arg<F>(&mut self, _: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        f(self)
+    }
+
+    fn emit_enum_struct_variant<F>(&mut self,
+                                   v_name: &str,
+                                   v_id: usize,
+                                   cnt: usize,
+                                   f: F)
+                                   -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        self.emit_enum_variant(v_name, v_id, cnt, f)
+    }
+
+    fn emit_enum_struct_variant_field<F>(&mut self, _: &str, idx: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        self.emit_enum_variant_arg(idx, f)
+    }
+
+    fn emit_struct<F>(&mut self, _: &str, _len: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        f(self)
+    }
+
+    fn emit_struct_field<F>(&mut self, _name: &str, _: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        f(self)
+    }
+
+    fn emit_tuple<F>(&mut self, len: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        self.emit_seq(len, f)
+    }
+
+    fn emit_tuple_arg<F>(&mut self, idx: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        self.emit_seq_elt(idx, f)
+    }
+
+    fn emit_tuple_struct<F>(&mut self, _: &str, len: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        self.emit_seq(len, f)
+    }
+
+    fn emit_tuple_struct_arg<F>(&mut self, idx: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        self.emit_seq_elt(idx, f)
+    }
+
+    fn emit_option<F>(&mut self, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        self.emit_enum("Option", f)
+    }
+
+    fn emit_option_none(&mut self) -> EncodeResult {
+        self.emit_enum_variant("None", 0, 0, |_| Ok(()))
+    }
+
+    fn emit_option_some<F>(&mut self, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        self.emit_enum_variant("Some", 1, 1, f)
+    }
+
+    fn emit_seq<F>(&mut self, len: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        try!(self.emit_uint(len));
+        f(self)
+    }
+
+    fn emit_seq_elt<F>(&mut self, _idx: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        f(self)
+    }
+
+    fn emit_map<F>(&mut self, len: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        try!(self.emit_uint(len));
+        f(self)
+    }
+
+    fn emit_map_elt_key<F>(&mut self, _idx: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        f(self)
+    }
+
+    fn emit_map_elt_val<F>(&mut self, _idx: usize, f: F) -> EncodeResult
+        where F: FnOnce(&mut Encoder<'a>) -> EncodeResult
+    {
+        f(self)
+    }
+}
+
+impl<'a> Encoder<'a> {
+    pub fn position(&self) -> usize {
+        self.cursor.position() as usize
+    }
+
+    pub fn from_rbml<'b: 'c, 'c>(rbml: &'c mut ::writer::Encoder<'b>) -> Encoder<'c> {
+        Encoder { cursor: rbml.writer }
+    }
+}
+
+// -----------------------------------------------------------------------------
+// Decoder
+// -----------------------------------------------------------------------------
+
+pub struct Decoder<'a> {
+    pub data: &'a [u8],
+    position: usize,
+}
+
+impl<'a> Decoder<'a> {
+    pub fn new(data: &'a [u8], position: usize) -> Decoder<'a> {
+        Decoder {
+            data: data,
+            position: position,
+        }
+    }
+
+    pub fn position(&self) -> usize {
+        self.position
+    }
+
+    pub fn advance(&mut self, bytes: usize) {
+        self.position += bytes;
+    }
+}
+
+macro_rules! read_uleb128 {
+    ($dec:expr, $t:ty) => ({
+        let (value, bytes_read) = read_unsigned_leb128($dec.data, $dec.position);
+        $dec.position += bytes_read;
+        Ok(value as $t)
+    })
+}
+
+macro_rules! read_sleb128 {
+    ($dec:expr, $t:ty) => ({
+        let (value, bytes_read) = read_signed_leb128($dec.data, $dec.position);
+        $dec.position += bytes_read;
+        Ok(value as $t)
+    })
+}
+
+
+impl<'a> serialize::Decoder for Decoder<'a> {
+    type Error = DecodeError;
+
+    fn read_nil(&mut self) -> Result<(), Self::Error> {
+        Ok(())
+    }
+
+    fn read_u64(&mut self) -> Result<u64, Self::Error> {
+        read_uleb128!(self, u64)
+    }
+
+    fn read_u32(&mut self) -> Result<u32, Self::Error> {
+        read_uleb128!(self, u32)
+    }
+
+    fn read_u16(&mut self) -> Result<u16, Self::Error> {
+        read_uleb128!(self, u16)
+    }
+
+    fn read_u8(&mut self) -> Result<u8, Self::Error> {
+        let value = self.data[self.position];
+        self.position += 1;
+        Ok(value)
+    }
+
+    fn read_uint(&mut self) -> Result<usize, Self::Error> {
+        read_uleb128!(self, usize)
+    }
+
+    fn read_i64(&mut self) -> Result<i64, Self::Error> {
+        read_sleb128!(self, i64)
+    }
+
+    fn read_i32(&mut self) -> Result<i32, Self::Error> {
+        read_sleb128!(self, i32)
+    }
+
+    fn read_i16(&mut self) -> Result<i16, Self::Error> {
+        read_sleb128!(self, i16)
+    }
+
+    fn read_i8(&mut self) -> Result<i8, Self::Error> {
+        let as_u8 = self.data[self.position];
+        self.position += 1;
+        unsafe { Ok(::std::mem::transmute(as_u8)) }
+    }
+
+    fn read_int(&mut self) -> Result<isize, Self::Error> {
+        read_sleb128!(self, isize)
+    }
+
+    fn read_bool(&mut self) -> Result<bool, Self::Error> {
+        let value = try!(self.read_u8());
+        Ok(value != 0)
+    }
+
+    fn read_f64(&mut self) -> Result<f64, Self::Error> {
+        let bits = try!(self.read_u64());
+        Ok(unsafe { ::std::mem::transmute(bits) })
+    }
+
+    fn read_f32(&mut self) -> Result<f32, Self::Error> {
+        let bits = try!(self.read_u32());
+        Ok(unsafe { ::std::mem::transmute(bits) })
+    }
+
+    fn read_char(&mut self) -> Result<char, Self::Error> {
+        let bits = try!(self.read_u32());
+        Ok(::std::char::from_u32(bits).unwrap())
+    }
+
+    fn read_str(&mut self) -> Result<String, Self::Error> {
+        let len = try!(self.read_uint());
+        let s = ::std::str::from_utf8(&self.data[self.position..self.position + len]).unwrap();
+        self.position += len;
+        Ok(s.to_string())
+    }
+
+    fn read_enum<T, F>(&mut self, _name: &str, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>) -> Result<T, Self::Error>
+    {
+        f(self)
+    }
+
+    fn read_enum_variant<T, F>(&mut self, _: &[&str], mut f: F) -> Result<T, Self::Error>
+        where F: FnMut(&mut Decoder<'a>, usize) -> Result<T, Self::Error>
+    {
+        let disr = try!(self.read_uint());
+        f(self, disr)
+    }
+
+    fn read_enum_variant_arg<T, F>(&mut self, _idx: usize, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>) -> Result<T, Self::Error>
+    {
+        f(self)
+    }
+
+    fn read_enum_struct_variant<T, F>(&mut self, _: &[&str], mut f: F) -> Result<T, Self::Error>
+        where F: FnMut(&mut Decoder<'a>, usize) -> Result<T, Self::Error>
+    {
+        let disr = try!(self.read_uint());
+        f(self, disr)
+    }
+
+    fn read_enum_struct_variant_field<T, F>(&mut self,
+                                            _name: &str,
+                                            _idx: usize,
+                                            f: F)
+                                            -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>) -> Result<T, Self::Error>
+    {
+        f(self)
+    }
+
+    fn read_struct<T, F>(&mut self, _name: &str, _: usize, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>) -> Result<T, Self::Error>
+    {
+        f(self)
+    }
+
+    fn read_struct_field<T, F>(&mut self, _name: &str, _idx: usize, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>) -> Result<T, Self::Error>
+    {
+        f(self)
+    }
+
+    fn read_tuple<T, F>(&mut self, tuple_len: usize, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>) -> Result<T, Self::Error>
+    {
+        self.read_seq(move |d, len| {
+            if len == tuple_len {
+                f(d)
+            } else {
+                let err = format!("Invalid tuple length. Expected {}, found {}",
+                                  tuple_len,
+                                  len);
+                Err(DecodeError::Expected(err))
+            }
+        })
+    }
+
+    fn read_tuple_arg<T, F>(&mut self, idx: usize, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>) -> Result<T, Self::Error>
+    {
+        self.read_seq_elt(idx, f)
+    }
+
+    fn read_tuple_struct<T, F>(&mut self, _name: &str, len: usize, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>) -> Result<T, Self::Error>
+    {
+        self.read_tuple(len, f)
+    }
+
+    fn read_tuple_struct_arg<T, F>(&mut self, idx: usize, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>) -> Result<T, Self::Error>
+    {
+        self.read_tuple_arg(idx, f)
+    }
+
+    fn read_option<T, F>(&mut self, mut f: F) -> Result<T, Self::Error>
+        where F: FnMut(&mut Decoder<'a>, bool) -> Result<T, Self::Error>
+    {
+        self.read_enum("Option", move |this| {
+            this.read_enum_variant(&["None", "Some"], move |this, idx| {
+                match idx {
+                    0 => f(this, false),
+                    1 => f(this, true),
+                    _ => {
+                        let msg = format!("Invalid Option index: {}", idx);
+                        Err(DecodeError::Expected(msg))
+                    }
+                }
+            })
+        })
+    }
+
+    fn read_seq<T, F>(&mut self, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>, usize) -> Result<T, Self::Error>
+    {
+        let len = try!(self.read_uint());
+        f(self, len)
+    }
+
+    fn read_seq_elt<T, F>(&mut self, _idx: usize, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>) -> Result<T, Self::Error>
+    {
+        f(self)
+    }
+
+    fn read_map<T, F>(&mut self, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>, usize) -> Result<T, Self::Error>
+    {
+        let len = try!(self.read_uint());
+        f(self, len)
+    }
+
+    fn read_map_elt_key<T, F>(&mut self, _idx: usize, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>) -> Result<T, Self::Error>
+    {
+        f(self)
+    }
+
+    fn read_map_elt_val<T, F>(&mut self, _idx: usize, f: F) -> Result<T, Self::Error>
+        where F: FnOnce(&mut Decoder<'a>) -> Result<T, Self::Error>
+    {
+        f(self)
+    }
+
+    fn error(&mut self, err: &str) -> Self::Error {
+        DecodeError::ApplicationError(err.to_string())
+    }
+}
+
+
+#[cfg(test)]
+mod tests {
+    use serialize::{Encodable, Decodable};
+    use std::io::Cursor;
+    use std::fmt::Debug;
+    use super::{Encoder, Decoder};
+
+    #[derive(PartialEq, Clone, Debug, RustcEncodable, RustcDecodable)]
+    struct Struct {
+        a: (),
+        b: u8,
+        c: u16,
+        d: u32,
+        e: u64,
+        f: usize,
+
+        g: i8,
+        h: i16,
+        i: i32,
+        j: i64,
+        k: isize,
+
+        l: char,
+        m: String,
+        n: f32,
+        o: f64,
+        p: bool,
+        q: Option<u32>,
+    }
+
+
+    fn check_round_trip<T: Encodable + Decodable + PartialEq + Debug>(values: Vec<T>) {
+        let mut cursor = Cursor::new(Vec::new());
+
+        for value in &values {
+            let mut encoder = Encoder::new(&mut cursor);
+            Encodable::encode(&value, &mut encoder).unwrap();
+        }
+
+        let data = cursor.into_inner();
+        let mut decoder = Decoder::new(&data[..], 0);
+
+        for value in values {
+            let decoded = Decodable::decode(&mut decoder).unwrap();
+            assert_eq!(value, decoded);
+        }
+    }
+
+    #[test]
+    fn test_unit() {
+        check_round_trip(vec![(), (), (), ()]);
+    }
+
+    #[test]
+    fn test_u8() {
+        let mut vec = vec![];
+        for i in ::std::u8::MIN..::std::u8::MAX {
+            vec.push(i);
+        }
+        check_round_trip(vec);
+    }
+
+    #[test]
+    fn test_u16() {
+        for i in ::std::u16::MIN..::std::u16::MAX {
+            check_round_trip(vec![1, 2, 3, i, i, i]);
+        }
+    }
+
+    #[test]
+    fn test_u32() {
+        check_round_trip(vec![1, 2, 3, ::std::u32::MIN, 0, 1, ::std::u32::MAX, 2, 1]);
+    }
+
+    #[test]
+    fn test_u64() {
+        check_round_trip(vec![1, 2, 3, ::std::u64::MIN, 0, 1, ::std::u64::MAX, 2, 1]);
+    }
+
+    #[test]
+    fn test_usize() {
+        check_round_trip(vec![1, 2, 3, ::std::usize::MIN, 0, 1, ::std::usize::MAX, 2, 1]);
+    }
+
+    #[test]
+    fn test_i8() {
+        let mut vec = vec![];
+        for i in ::std::i8::MIN..::std::i8::MAX {
+            vec.push(i);
+        }
+        check_round_trip(vec);
+    }
+
+    #[test]
+    fn test_i16() {
+        for i in ::std::i16::MIN..::std::i16::MAX {
+            check_round_trip(vec![-1, 2, -3, i, i, i, 2]);
+        }
+    }
+
+    #[test]
+    fn test_i32() {
+        check_round_trip(vec![-1, 2, -3, ::std::i32::MIN, 0, 1, ::std::i32::MAX, 2, 1]);
+    }
+
+    #[test]
+    fn test_i64() {
+        check_round_trip(vec![-1, 2, -3, ::std::i64::MIN, 0, 1, ::std::i64::MAX, 2, 1]);
+    }
+
+    #[test]
+    fn test_isize() {
+        check_round_trip(vec![-1, 2, -3, ::std::isize::MIN, 0, 1, ::std::isize::MAX, 2, 1]);
+    }
+
+    #[test]
+    fn test_bool() {
+        check_round_trip(vec![false, true, true, false, false]);
+    }
+
+    #[test]
+    fn test_f32() {
+        let mut vec = vec![];
+        for i in -100..100 {
+            vec.push((i as f32) / 3.0);
+        }
+        check_round_trip(vec);
+    }
+
+    #[test]
+    fn test_f64() {
+        let mut vec = vec![];
+        for i in -100..100 {
+            vec.push((i as f64) / 3.0);
+        }
+        check_round_trip(vec);
+    }
+
+    #[test]
+    fn test_char() {
+        let vec = vec!['a', 'b', 'c', 'd', 'A', 'X', ' ', '#', 'Ö', 'Ä', 'µ', '€'];
+        check_round_trip(vec);
+    }
+
+    #[test]
+    fn test_string() {
+        let vec = vec!["abcbuÖeiovÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(),
+                       "abcbuÖganeiovÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(),
+                       "abcbuÖganeiovÄnameÜavmpßvmea€µsbpapmaebn".to_string(),
+                       "abcbuÖganeiovÄnameÜavmpßvmeabpnvapeapmaebn".to_string(),
+                       "abcbuÖganeiÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(),
+                       "abcbuÖganeiovÄnameÜavmpßvmea€µsbpmaebn".to_string(),
+                       "abcbuÖganeiovÄnameÜavmpßvmea€µnvapeapmaebn".to_string()];
+
+        check_round_trip(vec);
+    }
+
+    #[test]
+    fn test_option() {
+        check_round_trip(vec![Some(-1i8)]);
+        check_round_trip(vec![Some(-2i16)]);
+        check_round_trip(vec![Some(-3i32)]);
+        check_round_trip(vec![Some(-4i64)]);
+        check_round_trip(vec![Some(-5isize)]);
+
+        let none_i8: Option<i8> = None;
+        check_round_trip(vec![none_i8]);
+
+        let none_i16: Option<i16> = None;
+        check_round_trip(vec![none_i16]);
+
+        let none_i32: Option<i32> = None;
+        check_round_trip(vec![none_i32]);
+
+        let none_i64: Option<i64> = None;
+        check_round_trip(vec![none_i64]);
+
+        let none_isize: Option<isize> = None;
+        check_round_trip(vec![none_isize]);
+    }
+
+    #[test]
+    fn test_struct() {
+        check_round_trip(vec![Struct {
+                                  a: (),
+                                  b: 10,
+                                  c: 11,
+                                  d: 12,
+                                  e: 13,
+                                  f: 14,
+
+                                  g: 15,
+                                  h: 16,
+                                  i: 17,
+                                  j: 18,
+                                  k: 19,
+
+                                  l: 'x',
+                                  m: "abc".to_string(),
+                                  n: 20.5,
+                                  o: 21.5,
+                                  p: false,
+                                  q: None,
+                              }]);
+
+        check_round_trip(vec![Struct {
+                                  a: (),
+                                  b: 101,
+                                  c: 111,
+                                  d: 121,
+                                  e: 131,
+                                  f: 141,
+
+                                  g: -15,
+                                  h: -16,
+                                  i: -17,
+                                  j: -18,
+                                  k: -19,
+
+                                  l: 'y',
+                                  m: "def".to_string(),
+                                  n: -20.5,
+                                  o: -21.5,
+                                  p: true,
+                                  q: Some(1234567),
+                              }]);
+    }
+
+    #[derive(PartialEq, Clone, Debug, RustcEncodable, RustcDecodable)]
+    enum Enum {
+        Variant1,
+        Variant2(usize, f32),
+        Variant3 {
+            a: i32,
+            b: char,
+            c: bool,
+        },
+    }
+
+    #[test]
+    fn test_enum() {
+        check_round_trip(vec![Enum::Variant1,
+                              Enum::Variant2(1, 2.5),
+                              Enum::Variant3 {
+                                  a: 3,
+                                  b: 'b',
+                                  c: false,
+                              },
+                              Enum::Variant3 {
+                                  a: -4,
+                                  b: 'f',
+                                  c: true,
+                              }]);
+    }
+
+    #[test]
+    fn test_sequence() {
+        let mut vec = vec![];
+        for i in -100i64..100i64 {
+            vec.push(i * 100000);
+        }
+
+        check_round_trip(vec![vec]);
+    }
+
+    #[test]
+    fn test_hash_map() {
+        use std::collections::HashMap;
+        let mut map = HashMap::new();
+        for i in -100i64..100i64 {
+            map.insert(i * 100000, i * 10000);
+        }
+
+        check_round_trip(vec![map]);
+    }
+
+    #[test]
+    fn test_tuples() {
+        check_round_trip(vec![('x', (), false, 0.5f32)]);
+        check_round_trip(vec![(9i8, 10u16, 1.5f64)]);
+        check_round_trip(vec![(-12i16, 11u8, 12usize)]);
+        check_round_trip(vec![(1234567isize, 100000000000000u64, 99999999999999i64)]);
+        check_round_trip(vec![(String::new(), "some string".to_string())]);
+    }
+}
diff --git a/src/librustc/dep_graph/README.md b/src/librustc/dep_graph/README.md
new file mode 100644 (file)
index 0000000..21742d9
--- /dev/null
@@ -0,0 +1,390 @@
+# Dependency graph for incremental compilation
+
+This module contains the infrastructure for managing the incremental
+compilation dependency graph. This README aims to explain how it ought
+to be used. In this document, we'll first explain the overall
+strategy, and then share some tips for handling specific scenarios.
+
+The high-level idea is that we want to instrument the compiler to
+track which parts of the AST and other IR are read/written by what.
+This way, when we come back later, we can look at this graph and
+determine what work needs to be redone.
+
+### The dependency graph
+
+The nodes of the graph are defined by the enum `DepNode`. They represent
+one of three things:
+
+1. HIR nodes (like `Hir(DefId)`) represent the HIR input itself.
+2. Data nodes (like `ItemSignature(DefId)`) represent some computed
+   information about a particular item.
+3. Procedure notes (like `CoherenceCheckImpl(DefId)`) represent some
+   procedure that is executing. Usually this procedure is
+   performing some kind of check for errors. You can think of them as
+   computed values where the value being computed is `()` (and the
+   value may fail to be computed, if an error results).
+
+An edge `N1 -> N2` is added between two nodes if either:
+
+- the value of `N1` is used to compute `N2`;
+- `N1` is read by the procedure `N2`;
+- the procedure `N1` writes the value `N2`.
+
+The latter two conditions are equivalent to the first one if you think
+of procedures as values.
+
+### Basic tracking
+
+There is a very general strategy to ensure that you have a correct, if
+sometimes overconservative, dependency graph. The two main things you have
+to do are (a) identify shared state and (b) identify the current tasks.
+
+### Identifying shared state
+
+Identify "shared state" that will be written by one pass and read by
+another. In particular, we need to identify shared state that will be
+read "across items" -- that is, anything where changes in one item
+could invalidate work done for other items. So, for example:
+
+1. The signature for a function is "shared state".
+2. The computed type of some expression in the body of a function is
+   not shared state, because if it changes it does not itself
+   invalidate other functions (though it may be that it causes new
+   monomorphizations to occur, but that's handled independently).
+   
+Put another way: if the HIR for an item changes, we are going to
+recompile that item for sure. But we need the dep tracking map to tell
+us what *else* we have to recompile. Shared state is anything that is
+used to communicate results from one item to another.
+
+### Identifying the current task
+
+The dep graph always tracks a current task: this is basically the
+`DepNode` that the compiler is computing right now. Typically it would
+be a procedure node, but it can also be a data node (as noted above,
+the two are kind of equivalent).
+
+You set the current task by calling `dep_graph.in_task(node)`. For example:
+
+```rust
+let _task = tcx.dep_graph.in_task(DepNode::Privacy);
+```
+
+Now all the code until `_task` goes out of scope will be considered
+part of the "privacy task".
+
+The tasks are maintained in a stack, so it is perfectly fine to nest
+one task within another. Because pushing a task is considered to be
+computing a value, when you nest a task `N2` inside of a task `N1`, we
+automatically add an edge `N2 -> N1` (since `N1` presumably needed the
+result of `N2` to complete):
+
+```rust
+let _n1 = tcx.dep_graph.in_task(DepNode::N1);
+let _n2 = tcx.dep_graph.in_task(DepNode::N2);
+// this will result in an edge N1 -> n2
+```
+
+### Ignore tasks
+
+Although it is rarely needed, you can also push a special "ignore"
+task:
+
+```rust
+let _ignore = tc.dep_graph.in_ignore();
+```
+
+This will cause all read/write edges to be ignored until it goes out
+of scope or until something else is pushed. For example, we could
+suppress the edge between nested tasks like so:
+
+```rust
+let _n1 = tcx.dep_graph.in_task(DepNode::N1);
+let _ignore = tcx.dep_graph.in_ignore();
+let _n2 = tcx.dep_graph.in_task(DepNode::N2);
+// now no edge is added
+```
+
+### Tracking reads and writes
+
+We need to identify what shared state is read/written by the current
+task as it executes. The most fundamental way of doing that is to invoke
+the `read` and `write` methods on `DepGraph`:
+
+```rust
+// Adds an edge from DepNode::Hir(some_def_id) to the current task
+tcx.dep_graph.read(DepNode::Hir(some_def_id))
+
+// Adds an edge from the current task to DepNode::ItemSignature(some_def_id)
+tcx.dep_graph.write(DepNode::ItemSignature(some_def_id))
+```
+
+However, you should rarely need to invoke those methods directly.
+Instead, the idea is to *encapsulate* shared state into some API that
+will invoke `read` and `write` automatically. The most common way to
+do this is to use a `DepTrackingMap`, described in the next section,
+but any sort of abstraction barrier will do. In general, the strategy
+is that getting access to information implicitly adds an appropriate
+`read`. So, for example, when you use the
+`dep_graph::visit_all_items_in_krate` helper method, it will visit
+each item `X`, start a task `Foo(X)` for that item, and automatically
+add an edge `Hir(X) -> Foo(X)`. This edge is added because the code is
+being given access to the HIR node for `X`, and hence it is expected
+to read from it. Similarly, reading from the `tcache` map for item `X`
+(which is a `DepTrackingMap`, described below) automatically invokes
+`dep_graph.read(ItemSignature(X))`.
+
+To make this strategy work, a certain amount of indirection is
+required. For example, modules in the HIR do not have direct pointers
+to the items that they contain. Rather, they contain node-ids -- one
+can then ask the HIR map for the item with a given node-id. This gives
+us an opportunity to add an appropriate read edge.
+
+#### Explicit calls to read and write when starting a new subtask
+
+One time when you *may* need to call `read` and `write` directly is
+when you push a new task onto the stack, either by calling `in_task`
+as shown above or indirectly, such as with the `memoize` pattern
+described below. In that case, any data that the task has access to
+from the surrounding environment must be explicitly "read". For
+example, in `librustc_typeck`, the collection code visits all items
+and, among other things, starts a subtask producing its signature
+(what follows is simplified pseudocode, of course):
+
+```rust
+fn visit_item(item: &hir::Item) {
+    // Here, current subtask is "Collect(X)", and an edge Hir(X) -> Collect(X)
+    // has automatically been added by `visit_all_items_in_krate`.
+    let sig = signature_of_item(item);
+}
+
+fn signature_of_item(item: &hir::Item) {
+    let def_id = tcx.map.local_def_id(item.id);
+    let task = tcx.dep_graph.in_task(DepNode::ItemSignature(def_id));
+    tcx.dep_graph.read(DepNode::Hir(def_id)); // <-- the interesting line
+    ...
+}
+```
+
+Here you can see that, in `signature_of_item`, we started a subtask
+corresponding to producing the `ItemSignature`. This subtask will read from
+`item` -- but it gained access to `item` implicitly. This means that if it just
+reads from `item`, there would be missing edges in the graph:
+
+    Hir(X) --+ // added by the explicit call to `read`
+      |      |
+      |      +---> ItemSignature(X) -> Collect(X)
+      |                                 ^
+      |                                 |
+      +---------------------------------+ // added by `visit_all_items_in_krate`
+    
+In particular, the edge from `Hir(X)` to `ItemSignature(X)` is only
+present because we called `read` ourselves when entering the `ItemSignature(X)`
+task.
+
+So, the rule of thumb: when entering a new task yourself, register
+reads on any shared state that you inherit. (This actually comes up
+fairly infrequently though: the main place you need caution is around
+memoization.)
+
+#### Dependency tracking map
+
+`DepTrackingMap` is a particularly convenient way to correctly store
+shared state. A `DepTrackingMap` is a special hashmap that will add
+edges automatically when `get` and `insert` are called. The idea is
+that, when you get/insert a value for the key `K`, we will add an edge
+from/to the node `DepNode::Variant(K)` (for some variant specific to
+the map).
+
+Each `DepTrackingMap` is parameterized by a special type `M` that
+implements `DepTrackingMapConfig`; this trait defines the key and value
+types of the map, and also defines a fn for converting from the key to
+a `DepNode` label. You don't usually have to muck about with this by
+hand, there is a macro for creating it. You can see the complete set
+of `DepTrackingMap` definitions in `librustc/middle/ty/maps.rs`.
+
+As an example, let's look at the `adt_defs` map. The `adt_defs` map
+maps from the def-id of a struct/enum to its `AdtDef`. It is defined
+using this macro:
+
+```rust
+dep_map_ty! { AdtDefs: ItemSignature(DefId) -> ty::AdtDefMaster<'tcx> }
+//            ~~~~~~~  ~~~~~~~~~~~~~ ~~~~~     ~~~~~~~~~~~~~~~~~~~~~~
+//               |           |      Key type       Value type
+//               |    DepNode variant
+//      Name of map id type
+```
+
+this indicates that a map id type `AdtDefs` will be created. The key
+of the map will be a `DefId` and value will be
+`ty::AdtDefMaster<'tcx>`. The `DepNode` will be created by
+`DepNode::ItemSignature(K)` for a given key.
+
+Once that is done, you can just use the `DepTrackingMap` like any
+other map:
+
+```rust
+let mut map: DepTrackingMap<M> = DepTrackingMap::new(dep_graph);
+map.insert(key, value); // registers dep_graph.write
+map.get(key; // registers dep_graph.read
+```
+
+#### Memoization
+
+One particularly interesting case is memoization. If you have some
+shared state that you compute in a memoized fashion, the correct thing
+to do is to define a `RefCell<DepTrackingMap>` for it and use the
+`memoize` helper:
+
+```rust
+map.memoize(key, || /* compute value */)
+```
+
+This will create a graph that looks like
+
+    ... -> MapVariant(key) -> CurrentTask
+
+where `MapVariant` is the `DepNode` variant that the map is associated with,
+and `...` are whatever edges the `/* compute value */` closure creates.
+
+In particular, using the memoize helper is much better than writing
+the obvious code yourself:
+
+```
+if let Some(result) = map.get(key) {
+    return result;
+}
+let value = /* compute value */;
+map.insert(key, value);
+```
+
+If you write that code manually, the dependency graph you get will
+include artificial edges that are not necessary. For example, imagine that
+two tasks, A and B, both invoke the manual memoization code, but A happens
+to go first. The resulting graph will be:
+
+    ... -> A -> MapVariant(key) -> B
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~       // caused by A writing to MapVariant(key)
+                ~~~~~~~~~~~~~~~~~~~~  // caused by B reading from MapVariant(key)
+
+This graph is not *wrong*, but it encodes a path from A to B that
+should not exist.  In contrast, using the memoized helper, you get:
+
+    ... -> MapVariant(key) -> A
+                 |
+                 +----------> B
+                 
+which is much cleaner.                 
+
+**Be aware though that the closure is executed with `MapVariant(key)`
+pushed onto the stack as the current task!** That means that you must
+add explicit `read` calls for any shared state that it accesses
+implicitly from its environment. See the section on "explicit calls to
+read and write when starting a new subtask" above for more details.
+
+### How to decide where to introduce a new task
+
+Certainly, you need at least one task on the stack: any attempt to
+`read` or `write` shared state will panic if there is no current
+task. But where does it make sense to introduce subtasks? The basic
+rule is that a subtask makes sense for any discrete unit of work you
+may want to skip in the future. Adding a subtask separates out the
+reads/writes from *that particular subtask* versus the larger
+context. An example: you might have a 'meta' task for all of borrow
+checking, and then subtasks for borrow checking individual fns.  (Seen
+in this light, memoized computations are just a special case where we
+may want to avoid redoing the work even within the context of one
+compilation.)
+
+The other case where you might want a subtask is to help with refining
+the reads/writes for some later bit of work that needs to be memoized.
+For example, we create a subtask for type-checking the body of each
+fn.  However, in the initial version of incr. comp. at least, we do
+not expect to actually *SKIP* type-checking -- we only expect to skip
+trans. However, it's still useful to create subtasks for type-checking
+individual items, because, otherwise, if a fn sig changes, we won't
+know which callers are affected -- in fact, because the graph would be
+so coarse, we'd just have to retrans everything, since we can't
+distinguish which fns used which fn sigs.
+
+### Testing the dependency graph
+
+There are various ways to write tests against the dependency graph.
+The simplest mechanism are the
+`#[rustc_if_this_changed]` and `#[rustc_then_this_would_need]`
+annotations. These are used in compile-fail tests to test whether the
+expected set of paths exist in the dependency graph. As an example,
+see `src/test/compile-fail/dep-graph-caller-callee.rs`.
+
+The idea is that you can annotate a test like:
+
+```rust
+#[rustc_if_this_changed]
+fn foo() { }
+
+#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
+fn bar() { foo(); }
+
+#[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+fn baz() { }
+```
+
+This will check whether there is a path in the dependency graph from
+`Hir(foo)` to `TypeckItemBody(bar)`. An error is reported for each
+`#[rustc_then_this_would_need]` annotation that indicates whether a
+path exists. `//~ ERROR` annotations can then be used to test if a
+path is found (as demonstrated above).
+
+### Debugging the dependency graph
+
+The compiler is also capable of dumping the dependency graph for your
+debugging pleasure. To do so, pass the `-Z dump-dep-graph` flag. The
+graph will be dumped to `dep_graph.{txt,dot}` in the current
+directory.  You can override the filename with the `RUST_DEP_GRAPH`
+environment variable.
+
+Frequently, though, the full dep graph is quite overwhelming and not
+particularly helpful. Therefore, the compiler also allows you to filter
+the graph. You can filter in three ways:
+
+1. All edges originating in a particular set of nodes (usually a single node).
+2. All edges reaching a particular set of nodes.
+3. All edges that lie between given start and end nodes.
+
+To filter, use the `RUST_DEP_GRAPH_FILTER` environment variable, which should
+look like one of the following:
+
+```
+source_filter     // nodes originating from source_filter
+-> target_filter  // nodes that can reach target_filter
+source_filter -> target_filter // nodes in between source_filter and target_filter
+```
+
+`source_filter` and `target_filter` are a `&`-separated list of strings.
+A node is considered to match a filter if all of those strings appear in its
+label. So, for example:
+
+```
+RUST_DEP_GRAPH_FILTER='-> TypeckItemBody'
+```
+
+would select the predecessors of all `TypeckItemBody` nodes. Usually though you
+want the `TypeckItemBody` node for some particular fn, so you might write:
+
+```
+RUST_DEP_GRAPH_FILTER='-> TypeckItemBody & bar'
+```
+
+This will select only the `TypeckItemBody` nodes for fns with `bar` in their name.
+
+Perhaps you are finding that when you change `foo` you need to re-type-check `bar`,
+but you don't think you should have to. In that case, you might do:
+
+```
+RUST_DEP_GRAPH_FILTER='Hir&foo -> TypeckItemBody & bar'
+```
+
+This will dump out all the nodes that lead from `Hir(foo)` to
+`TypeckItemBody(bar)`, from which you can (hopefully) see the source
+of the erroneous edge.
+
diff --git a/src/librustc/dep_graph/dep_tracking_map.rs b/src/librustc/dep_graph/dep_tracking_map.rs
new file mode 100644 (file)
index 0000000..c49e64f
--- /dev/null
@@ -0,0 +1,137 @@
+// 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 rustc_data_structures::fnv::FnvHashMap;
+use std::cell::RefCell;
+use std::ops::Index;
+use std::hash::Hash;
+use std::marker::PhantomData;
+use util::common::MemoizationMap;
+
+use super::{DepNode, DepGraph};
+
+/// A DepTrackingMap offers a subset of the `Map` API and ensures that
+/// we make calls to `read` and `write` as appropriate. We key the
+/// maps with a unique type for brevity.
+pub struct DepTrackingMap<M: DepTrackingMapConfig> {
+    phantom: PhantomData<M>,
+    graph: DepGraph,
+    map: FnvHashMap<M::Key, M::Value>,
+}
+
+pub trait DepTrackingMapConfig {
+    type Key: Eq + Hash + Clone;
+    type Value: Clone;
+    fn to_dep_node(key: &Self::Key) -> DepNode;
+}
+
+impl<M: DepTrackingMapConfig> DepTrackingMap<M> {
+    pub fn new(graph: DepGraph) -> DepTrackingMap<M> {
+        DepTrackingMap {
+            phantom: PhantomData,
+            graph: graph,
+            map: FnvHashMap()
+        }
+    }
+
+    /// Registers a (synthetic) read from the key `k`. Usually this
+    /// is invoked automatically by `get`.
+    fn read(&self, k: &M::Key) {
+        let dep_node = M::to_dep_node(k);
+        self.graph.read(dep_node);
+    }
+
+    /// Registers a (synthetic) write to the key `k`. Usually this is
+    /// invoked automatically by `insert`.
+    fn write(&self, k: &M::Key) {
+        let dep_node = M::to_dep_node(k);
+        self.graph.write(dep_node);
+    }
+
+    pub fn get(&self, k: &M::Key) -> Option<&M::Value> {
+        self.read(k);
+        self.map.get(k)
+    }
+
+    pub fn insert(&mut self, k: M::Key, v: M::Value) -> Option<M::Value> {
+        self.write(&k);
+        self.map.insert(k, v)
+    }
+
+    pub fn contains_key(&self, k: &M::Key) -> bool {
+        self.read(k);
+        self.map.contains_key(k)
+    }
+}
+
+impl<M: DepTrackingMapConfig> MemoizationMap for RefCell<DepTrackingMap<M>> {
+    type Key = M::Key;
+    type Value = M::Value;
+
+    /// Memoizes an entry in the dep-tracking-map. If the entry is not
+    /// already present, then `op` will be executed to compute its value.
+    /// The resulting dependency graph looks like this:
+    ///
+    ///     [op] -> Map(key) -> CurrentTask
+    ///
+    /// Here, `[op]` represents whatever nodes `op` reads in the
+    /// course of execution; `Map(key)` represents the node for this
+    /// map; and `CurrentTask` represents the current task when
+    /// `memoize` is invoked.
+    ///
+    /// **Important:* when `op` is invoked, the current task will be
+    /// switched to `Map(key)`. Therefore, if `op` makes use of any
+    /// HIR nodes or shared state accessed through its closure
+    /// environment, it must explicitly register a read of that
+    /// state. As an example, see `type_scheme_of_item` in `collect`,
+    /// which looks something like this:
+    ///
+    /// ```
+    /// fn type_scheme_of_item(..., item: &hir::Item) -> ty::TypeScheme<'tcx> {
+    ///     let item_def_id = ccx.tcx.map.local_def_id(it.id);
+    ///     ccx.tcx.tcache.memoized(item_def_id, || {
+    ///         ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); // (*)
+    ///         compute_type_scheme_of_item(ccx, item)
+    ///     });
+    /// }
+    /// ```
+    ///
+    /// The key is the line marked `(*)`: the closure implicitly
+    /// accesses the body of the item `item`, so we register a read
+    /// from `Hir(item_def_id)`.
+    fn memoize<OP>(&self, key: M::Key, op: OP) -> M::Value
+        where OP: FnOnce() -> M::Value
+    {
+        let graph;
+        {
+            let this = self.borrow();
+            if let Some(result) = this.map.get(&key) {
+                this.read(&key);
+                return result.clone();
+            }
+            graph = this.graph.clone();
+        }
+
+        let _task = graph.in_task(M::to_dep_node(&key));
+        let result = op();
+        self.borrow_mut().map.insert(key, result.clone());
+        result
+    }
+}
+
+impl<'k, M: DepTrackingMapConfig> Index<&'k M::Key> for DepTrackingMap<M> {
+    type Output = M::Value;
+
+    #[inline]
+    fn index(&self, k: &'k M::Key) -> &M::Value {
+        self.get(k).unwrap()
+    }
+}
+
diff --git a/src/librustc/dep_graph/edges.rs b/src/librustc/dep_graph/edges.rs
new file mode 100644 (file)
index 0000000..4b25285
--- /dev/null
@@ -0,0 +1,162 @@
+// 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 rustc_data_structures::fnv::{FnvHashMap, FnvHashSet};
+use super::{DepGraphQuery, DepNode};
+
+pub struct DepGraphEdges {
+    nodes: Vec<DepNode>,
+    indices: FnvHashMap<DepNode, IdIndex>,
+    edges: FnvHashSet<(IdIndex, IdIndex)>,
+    open_nodes: Vec<OpenNode>,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+struct IdIndex {
+    index: u32
+}
+
+impl IdIndex {
+    fn new(v: usize) -> IdIndex {
+        assert!((v & 0xFFFF_FFFF) == v);
+        IdIndex { index: v as u32 }
+    }
+
+    fn index(self) -> usize {
+        self.index as usize
+    }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+enum OpenNode {
+    Node(IdIndex),
+    Ignore,
+}
+
+impl DepGraphEdges {
+    pub fn new() -> DepGraphEdges {
+        DepGraphEdges {
+            nodes: vec![],
+            indices: FnvHashMap(),
+            edges: FnvHashSet(),
+            open_nodes: Vec::new()
+        }
+    }
+
+    fn id(&self, index: IdIndex) -> DepNode {
+        self.nodes[index.index()]
+    }
+
+    /// Creates a node for `id` in the graph.
+    fn make_node(&mut self, id: DepNode) -> IdIndex {
+        if let Some(&i) = self.indices.get(&id) {
+            return i;
+        }
+
+        let index = IdIndex::new(self.nodes.len());
+        self.nodes.push(id.clone());
+        self.indices.insert(id, index);
+        index
+    }
+
+    /// Top of the stack of open nodes.
+    fn current_node(&self) -> Option<OpenNode> {
+        self.open_nodes.last().cloned()
+    }
+
+    pub fn push_ignore(&mut self) {
+        self.open_nodes.push(OpenNode::Ignore);
+    }
+
+    pub fn pop_ignore(&mut self) {
+        let popped_node = self.open_nodes.pop().unwrap();
+        assert_eq!(popped_node, OpenNode::Ignore);
+    }
+
+    pub fn push_task(&mut self, key: DepNode) {
+        let top_node = self.current_node();
+
+        let new_node = self.make_node(key);
+        self.open_nodes.push(OpenNode::Node(new_node));
+
+        // if we are in the midst of doing task T, then this new task
+        // N is a subtask of T, so add an edge N -> T.
+        if let Some(top_node) = top_node {
+            self.add_edge_from_open_node(top_node, |t| (new_node, t));
+        }
+    }
+
+    pub fn pop_task(&mut self, key: DepNode) {
+        let popped_node = self.open_nodes.pop().unwrap();
+        assert_eq!(OpenNode::Node(self.indices[&key]), popped_node);
+    }
+
+    /// Indicates that the current task `C` reads `v` by adding an
+    /// edge from `v` to `C`. If there is no current task, panics. If
+    /// you want to suppress this edge, use `ignore`.
+    pub fn read(&mut self, v: DepNode) {
+        let source = self.make_node(v);
+        self.add_edge_from_current_node(|current| (source, current))
+    }
+
+    /// Indicates that the current task `C` writes `v` by adding an
+    /// edge from `C` to `v`. If there is no current task, panics. If
+    /// you want to suppress this edge, use `ignore`.
+    pub fn write(&mut self, v: DepNode) {
+        let target = self.make_node(v);
+        self.add_edge_from_current_node(|current| (current, target))
+    }
+
+    /// Invoke `add_edge_from_open_node` with the top of the stack, or
+    /// panic if stack is empty.
+    fn add_edge_from_current_node<OP>(&mut self,
+                                      op: OP)
+        where OP: FnOnce(IdIndex) -> (IdIndex, IdIndex)
+    {
+        match self.current_node() {
+            Some(open_node) => self.add_edge_from_open_node(open_node, op),
+            None => panic!("no current node, cannot add edge into dependency graph")
+        }
+    }
+
+    /// Adds an edge to or from the `open_node`, assuming `open_node`
+    /// is not `Ignore`. The direction of the edge is determined by
+    /// the closure `op` --- we pass as argument the open node `n`,
+    /// and the closure returns a (source, target) tuple, which should
+    /// include `n` in one spot or another.
+    fn add_edge_from_open_node<OP>(&mut self,
+                                   open_node: OpenNode,
+                                   op: OP)
+        where OP: FnOnce(IdIndex) -> (IdIndex, IdIndex)
+    {
+        let (source, target) = match open_node {
+            OpenNode::Node(n) => op(n),
+            OpenNode::Ignore => { return; }
+        };
+
+        // ignore trivial self edges, which are not very interesting
+        if source == target {
+            return;
+        }
+
+        if self.edges.insert((source, target)) {
+            debug!("adding edge from {:?} to {:?}",
+                   self.id(source),
+                   self.id(target));
+        }
+    }
+
+    pub fn query(&self) -> DepGraphQuery {
+        let edges: Vec<_> = self.edges.iter()
+                                      .map(|&(i, j)| (self.id(i), self.id(j)))
+                                      .collect();
+        DepGraphQuery::new(&self.nodes, &edges)
+    }
+}
diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs
new file mode 100644 (file)
index 0000000..9bf0a79
--- /dev/null
@@ -0,0 +1,196 @@
+// 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::thread::{DepGraphThreadData, DepMessage};
+use middle::def_id::DefId;
+use middle::ty;
+use middle::ty::fast_reject::SimplifiedType;
+use rustc_front::hir;
+use rustc_front::intravisit::Visitor;
+use std::rc::Rc;
+
+mod dep_tracking_map;
+mod edges;
+mod query;
+mod raii;
+mod thread;
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum DepNode {
+    // Represents the `Krate` as a whole (the `hir::Krate` value) (as
+    // distinct from the krate module). This is basically a hash of
+    // the entire krate, so if you read from `Krate` (e.g., by calling
+    // `tcx.map.krate()`), we will have to assume that any change
+    // means that you need to be recompiled. This is because the
+    // `Krate` value gives you access to all other items. To avoid
+    // this fate, do not call `tcx.map.krate()`; instead, prefer
+    // wrappers like `tcx.visit_all_items_in_krate()`.  If there is no
+    // suitable wrapper, you can use `tcx.dep_graph.ignore()` to gain
+    // access to the krate, but you must remember to add suitable
+    // edges yourself for the individual items that you read.
+    Krate,
+
+    // Represents the HIR node with the given node-id
+    Hir(DefId),
+
+    // Represents different phases in the compiler.
+    CollectItem(DefId),
+    Coherence,
+    CoherenceCheckImpl(DefId),
+    CoherenceOverlapCheck(DefId),
+    CoherenceOverlapCheckSpecial(DefId),
+    CoherenceOrphanCheck(DefId),
+    Variance,
+    WfCheck(DefId),
+    TypeckItemType(DefId),
+    TypeckItemBody(DefId),
+    Dropck,
+    DropckImpl(DefId),
+    CheckConst(DefId),
+    Privacy,
+    IntrinsicCheck(DefId),
+    MatchCheck(DefId),
+    MirMapConstruction(DefId),
+    BorrowCheck(DefId),
+    RvalueCheck(DefId),
+    Reachability,
+    DeadCheck,
+    StabilityCheck,
+    LateLintCheck,
+    IntrinsicUseCheck,
+    TransCrate,
+    TransCrateItem(DefId),
+    TransInlinedItem(DefId),
+    TransWriteMetadata,
+
+    // Nodes representing bits of computed IR in the tcx. Each shared
+    // table in the tcx (or elsewhere) maps to one of these
+    // nodes. Often we map multiple tables to the same node if there
+    // is no point in distinguishing them (e.g., both the type and
+    // predicates for an item wind up in `ItemSignature`). Other
+    // times, such as `ImplItems` vs `TraitItemDefIds`, tables which
+    // might be mergable are kept distinct because the sets of def-ids
+    // to which they apply are disjoint, and hence we might as well
+    // have distinct labels for easier debugging.
+    ImplOrTraitItems(DefId),
+    ItemSignature(DefId),
+    FieldTy(DefId),
+    TraitItemDefIds(DefId),
+    InherentImpls(DefId),
+    ImplItems(DefId),
+
+    // The set of impls for a given trait. Ultimately, it would be
+    // nice to get more fine-grained here (e.g., to include a
+    // simplified type), but we can't do that until we restructure the
+    // HIR to distinguish the *header* of an impl from its body.  This
+    // is because changes to the header may change the self-type of
+    // the impl and hence would require us to be more conservative
+    // than changes in the impl body.
+    TraitImpls(DefId),
+
+    // Nodes representing caches. To properly handle a true cache, we
+    // don't use a DepTrackingMap, but rather we push a task node.
+    // Otherwise the write into the map would be incorrectly
+    // attributed to the first task that happened to fill the cache,
+    // which would yield an overly conservative dep-graph.
+    TraitItems(DefId),
+    ReprHints(DefId),
+    TraitSelect(DefId, Option<SimplifiedType>),
+}
+
+#[derive(Clone)]
+pub struct DepGraph {
+    data: Rc<DepGraphThreadData>
+}
+
+impl DepGraph {
+    pub fn new(enabled: bool) -> DepGraph {
+        DepGraph {
+            data: Rc::new(DepGraphThreadData::new(enabled))
+        }
+    }
+
+    pub fn query(&self) -> DepGraphQuery {
+        self.data.query()
+    }
+
+    pub fn in_ignore<'graph>(&'graph self) -> raii::IgnoreTask<'graph> {
+        raii::IgnoreTask::new(&self.data)
+    }
+
+    pub fn in_task<'graph>(&'graph self, key: DepNode) -> raii::DepTask<'graph> {
+        raii::DepTask::new(&self.data, key)
+    }
+
+    pub fn with_ignore<OP,R>(&self, op: OP) -> R
+        where OP: FnOnce() -> R
+    {
+        let _task = self.in_ignore();
+        op()
+    }
+
+    pub fn with_task<OP,R>(&self, key: DepNode, op: OP) -> R
+        where OP: FnOnce() -> R
+    {
+        let _task = self.in_task(key);
+        op()
+    }
+
+    pub fn read(&self, v: DepNode) {
+        self.data.enqueue(DepMessage::Read(v));
+    }
+
+    pub fn write(&self, v: DepNode) {
+        self.data.enqueue(DepMessage::Write(v));
+    }
+}
+
+pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig};
+
+pub use self::query::DepGraphQuery;
+
+/// Visit all the items in the krate in some order. When visiting a
+/// particular item, first create a dep-node by calling `dep_node_fn`
+/// and push that onto the dep-graph stack of tasks, and also create a
+/// read edge from the corresponding AST node. This is used in
+/// compiler passes to automatically record the item that they are
+/// working on.
+pub fn visit_all_items_in_krate<'tcx,V,F>(tcx: &ty::ctxt<'tcx>,
+                                          mut dep_node_fn: F,
+                                          visitor: &mut V)
+    where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx>
+{
+    struct TrackingVisitor<'visit, 'tcx: 'visit, F: 'visit, V: 'visit> {
+        tcx: &'visit ty::ctxt<'tcx>,
+        dep_node_fn: &'visit mut F,
+        visitor: &'visit mut V
+    }
+
+    impl<'visit, 'tcx, F, V> Visitor<'tcx> for TrackingVisitor<'visit, 'tcx, F, V>
+        where F: FnMut(DefId) -> DepNode, V: Visitor<'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);
+            debug!("About to start task {:?}", task_id);
+            let _task = self.tcx.dep_graph.in_task(task_id);
+            self.tcx.dep_graph.read(DepNode::Hir(item_def_id));
+            self.visitor.visit_item(i)
+        }
+    }
+
+    let krate = tcx.dep_graph.with_ignore(|| tcx.map.krate());
+    let mut tracking_visitor = TrackingVisitor {
+        tcx: tcx,
+        dep_node_fn: &mut dep_node_fn,
+        visitor: visitor
+    };
+    krate.visit_all_items(&mut tracking_visitor)
+}
diff --git a/src/librustc/dep_graph/query.rs b/src/librustc/dep_graph/query.rs
new file mode 100644 (file)
index 0000000..74a054a
--- /dev/null
@@ -0,0 +1,68 @@
+// 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 rustc_data_structures::fnv::FnvHashMap;
+use rustc_data_structures::graph::{Graph, NodeIndex};
+
+use super::DepNode;
+
+pub struct DepGraphQuery {
+    pub graph: Graph<DepNode, ()>,
+    pub indices: FnvHashMap<DepNode, NodeIndex>,
+}
+
+impl DepGraphQuery {
+    pub fn new(nodes: &[DepNode], edges: &[(DepNode, DepNode)]) -> DepGraphQuery {
+        let mut graph = Graph::new();
+        let mut indices = FnvHashMap();
+        for node in nodes {
+            indices.insert(node.clone(), graph.next_node_index());
+            graph.add_node(node.clone());
+        }
+
+        for &(ref source, ref target) in edges {
+            let source = indices[source];
+            let target = indices[target];
+            graph.add_edge(source, target, ());
+        }
+
+        DepGraphQuery {
+            graph: graph,
+            indices: indices
+        }
+    }
+
+    pub fn nodes(&self) -> Vec<DepNode> {
+        self.graph.all_nodes()
+                  .iter()
+                  .map(|n| n.data.clone())
+                  .collect()
+    }
+
+    pub fn edges(&self) -> Vec<(DepNode,DepNode)> {
+        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()))
+                  .collect()
+    }
+
+    /// All nodes reachable from `node`. In other words, things that
+    /// will have to be recomputed if `node` changes.
+    pub fn dependents(&self, node: DepNode) -> Vec<DepNode> {
+        if let Some(&index) = self.indices.get(&node) {
+            self.graph.depth_traverse(index)
+                      .map(|dependent_node| self.graph.node_data(dependent_node).clone())
+                      .collect()
+        } else {
+            vec![]
+        }
+    }
+}
diff --git a/src/librustc/dep_graph/raii.rs b/src/librustc/dep_graph/raii.rs
new file mode 100644 (file)
index 0000000..dd7ff92
--- /dev/null
@@ -0,0 +1,47 @@
+// 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 super::DepNode;
+use super::thread::{DepGraphThreadData, DepMessage};
+
+pub struct DepTask<'graph> {
+    data: &'graph DepGraphThreadData,
+    key: DepNode,
+}
+
+impl<'graph> DepTask<'graph> {
+    pub fn new(data: &'graph DepGraphThreadData, key: DepNode) -> DepTask<'graph> {
+        data.enqueue(DepMessage::PushTask(key));
+        DepTask { data: data, key: key }
+    }
+}
+
+impl<'graph> Drop for DepTask<'graph> {
+    fn drop(&mut self) {
+        self.data.enqueue(DepMessage::PopTask(self.key));
+    }
+}
+
+pub struct IgnoreTask<'graph> {
+    data: &'graph DepGraphThreadData
+}
+
+impl<'graph> IgnoreTask<'graph> {
+    pub fn new(data: &'graph DepGraphThreadData) -> IgnoreTask<'graph> {
+        data.enqueue(DepMessage::PushIgnore);
+        IgnoreTask { data: data }
+    }
+}
+
+impl<'graph> Drop for IgnoreTask<'graph> {
+    fn drop(&mut self) {
+        self.data.enqueue(DepMessage::PopIgnore);
+    }
+}
diff --git a/src/librustc/dep_graph/thread.rs b/src/librustc/dep_graph/thread.rs
new file mode 100644 (file)
index 0000000..dbc5760
--- /dev/null
@@ -0,0 +1,137 @@
+// 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.
+
+//! Manages the communication between the compiler's main thread and
+//! the thread that constructs the dependency graph. The basic idea is
+//! to use double buffering to lower the cost of producing a message.
+//! In the compiler thread, we accumulate messages in a vector until
+//! the vector is full, or until we want to query the graph, and then
+//! we send that vector over to the depgraph thread. At the same time,
+//! we receive an empty vector from the depgraph thread that we can use
+//! to accumulate more messages. This way we only ever have two vectors
+//! allocated (and both have a fairly large capacity).
+
+use rustc_data_structures::veccell::VecCell;
+use std::sync::mpsc::{self, Sender, Receiver};
+use std::thread;
+
+use super::DepGraphQuery;
+use super::DepNode;
+use super::edges::DepGraphEdges;
+
+pub enum DepMessage {
+    Read(DepNode),
+    Write(DepNode),
+    PushTask(DepNode),
+    PopTask(DepNode),
+    PushIgnore,
+    PopIgnore,
+    Query,
+}
+
+pub struct DepGraphThreadData {
+    enabled: bool,
+
+    // current buffer, where we accumulate messages
+    messages: VecCell<DepMessage>,
+
+    // whence to receive new buffer when full
+    swap_in: Receiver<Vec<DepMessage>>,
+
+    // where to send buffer when full
+    swap_out: Sender<Vec<DepMessage>>,
+
+    // where to receive query results
+    query_in: Receiver<DepGraphQuery>,
+}
+
+const INITIAL_CAPACITY: usize = 2048;
+
+impl DepGraphThreadData {
+    pub fn new(enabled: bool) -> DepGraphThreadData {
+        let (tx1, rx1) = mpsc::channel();
+        let (tx2, rx2) = mpsc::channel();
+        let (txq, rxq) = mpsc::channel();
+        if enabled {
+            thread::spawn(move || main(rx1, tx2, txq));
+        }
+        DepGraphThreadData {
+            enabled: enabled,
+            messages: VecCell::with_capacity(INITIAL_CAPACITY),
+            swap_in: rx2,
+            swap_out: tx1,
+            query_in: rxq,
+        }
+    }
+
+    /// Sends the current batch of messages to the thread. Installs a
+    /// new vector of messages.
+    fn swap(&self) {
+        assert!(self.enabled, "should never swap if not enabled");
+
+        // should be a buffer waiting for us (though of course we may
+        // have to wait for depgraph thread to finish processing the
+        // old messages)
+        let new_messages = self.swap_in.recv().unwrap();
+        assert!(new_messages.is_empty());
+
+        // swap in the empty buffer and extract the full one
+        let old_messages = self.messages.swap(new_messages);
+
+        // send full buffer to depgraph thread to be processed
+        self.swap_out.send(old_messages).unwrap();
+    }
+
+    pub fn query(&self) -> DepGraphQuery {
+        assert!(self.enabled, "cannot query if dep graph construction not enabled");
+        self.enqueue(DepMessage::Query);
+        self.swap();
+        self.query_in.recv().unwrap()
+    }
+
+    /// Enqueue a message to be sent when things are next swapped. (If
+    /// the buffer is full, this may swap.)
+    #[inline]
+    pub fn enqueue(&self, message: DepMessage) {
+        if self.enabled {
+            let len = self.messages.push(message);
+            if len == INITIAL_CAPACITY {
+                self.swap();
+            }
+        }
+    }
+}
+
+/// Definition of the depgraph thread.
+pub fn main(swap_in: Receiver<Vec<DepMessage>>,
+            swap_out: Sender<Vec<DepMessage>>,
+            query_out: Sender<DepGraphQuery>) {
+    let mut edges = DepGraphEdges::new();
+
+    // the compiler thread always expects a fresh buffer to be
+    // waiting, so queue one up
+    swap_out.send(Vec::with_capacity(INITIAL_CAPACITY)).unwrap();
+
+    // process the buffers from compiler thread as we receive them
+    for mut messages in swap_in {
+        for msg in messages.drain(..) {
+            match msg {
+                DepMessage::Read(node) => edges.read(node),
+                DepMessage::Write(node) => edges.write(node),
+                DepMessage::PushTask(node) => edges.push_task(node),
+                DepMessage::PopTask(node) => edges.pop_task(node),
+                DepMessage::PushIgnore => edges.push_ignore(),
+                DepMessage::PopIgnore => edges.pop_ignore(),
+                DepMessage::Query => query_out.send(edges.query()).unwrap(),
+            }
+        }
+        swap_out.send(messages).unwrap();
+    }
+}
index 86dd363993aec5c0720129ec28d9d81bde0610b4..9f323379b9591b50016569ada937cd859cfb86cf 100644 (file)
@@ -316,21 +316,6 @@ See [RFC 911] for more details on the design of `const fn`s.
 [RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
 "##,
 
-E0016: r##"
-Blocks in constants may only contain items (such as constant, function
-definition, etc...) and a tail expression. Example:
-
-```
-const FOO: i32 = { let x = 0; x }; // 'x' isn't an item!
-```
-
-To avoid it, you have to replace the non-item object:
-
-```
-const FOO: i32 = { const X : i32 = 0; X };
-```
-"##,
-
 E0017: r##"
 References in statics and constants may only refer to immutable values. Example:
 
@@ -422,24 +407,6 @@ const X: i32 = 42 / 0;
 ```
 "##,
 
-E0022: r##"
-Constant functions are not allowed to mutate anything. Thus, binding to an
-argument with a mutable pattern is not allowed. For example,
-
-```
-const fn foo(mut x: u8) {
-    // do stuff
-}
-```
-
-is bad because the function body may not mutate `x`.
-
-Remove any mutable bindings from the argument list to fix this error. In case
-you need to mutate the argument, try lazily initializing a global variable
-instead of using a `const fn`, or refactoring the code to a functional style to
-avoid mutation if possible.
-"##,
-
 E0030: r##"
 When matching against a range, the compiler verifies that the range is
 non-empty.  Range patterns include both end-points, so this is equivalent to
@@ -989,6 +956,41 @@ enum Method { GET, POST }
 ```
 "##,
 
+E0229: r##"
+An associated type binding was done outside of the type parameter declaration
+and `where` clause. Erroneous code example:
+
+```
+pub trait Foo {
+    type A;
+    fn boo(&self) -> <Self as Foo>::A;
+}
+
+struct Bar;
+
+impl Foo for isize {
+    type A = usize;
+    fn boo(&self) -> usize { 42 }
+}
+
+fn baz<I>(x: &<I as Foo<A=Bar>>::A) {}
+// error: associated type bindings are not allowed here
+```
+
+To solve this error, please move the type bindings in the type parameter
+declaration:
+
+```
+fn baz<I: Foo<A=Bar>>(x: &<I as Foo>::A) {} // ok!
+```
+
+or in the `where` clause:
+
+```
+fn baz<I>(x: &<I as Foo>::A) where I: Foo<A=Bar> {}
+```
+"##,
+
 E0261: r##"
 When using a lifetime like `'a` in a type, it must be declared before being
 used.
@@ -1302,7 +1304,7 @@ explanatory comments for the same example:
 
     // `for`-loops use a protocol based on the `Iterator`
     // trait. Each item yielded in a `for` loop has the
-    // type `Iterator::Item` -- that is,I `Item` is the
+    // type `Iterator::Item` -- that is, `Item` is the
     // associated type of the concrete iterator impl.
     for v in &vs {
 //      ~    ~~~
@@ -1622,6 +1624,46 @@ This will fail because the compiler does not know which instance of `Foo` to
 call `bar` on. Change `Foo::bar()` to `Foo::<T>::bar()` to resolve the error.
 "##,
 
+E0283: r##"
+This error occurs when the compiler doesn't have enough information
+to unambiguously choose an implementation.
+
+For example:
+
+```
+trait Generator {
+    fn create() -> u32;
+}
+
+struct Impl;
+impl Generator for Impl {
+    fn create() -> u32 { 1 }
+}
+
+struct AnotherImpl;
+impl Generator for AnotherImpl {
+    fn create() -> u32 { 2 }
+}
+
+fn main() {
+    let cont: u32 = Generator::create();
+    // error, impossible to choose one of Generator trait implementation
+    // Impl or AnotherImpl? Maybe anything else?
+}
+```
+
+To resolve this error use the concrete type:
+
+```
+fn main() {
+    let gen1 = AnotherImpl::create();
+
+    // if there are multiple methods with same name (different traits)
+    let gen2 = <AnotherImpl as Generator>::create();
+}
+```
+"##,
+
 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:
@@ -1747,6 +1789,30 @@ let x: i32 = "I am not a number!";
 //      |
 //    type `i32` assigned to variable `x`
 ```
+
+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>`:
+
+```
+use std::fs::File;
+
+fn main() {
+    let mut f = try!(File::create("foo.txt"));
+}
+```
+
+This code gives an error like this:
+
+```text
+<std macros>:5:8: 6:42 error: mismatched types:
+ expected `()`,
+     found `core::result::Result<_, _>`
+ (expected (),
+     found enum `core::result::Result`) [E0308]
+```
+
+`try!` returns a `Result<T, E>`, and so the function must. But `main()` has
+`()` as its return type, hence the error.
 "##,
 
 E0309: r##"
@@ -2241,18 +2307,16 @@ register_diagnostics! {
     // E0006 // merged with E0005
 //  E0134,
 //  E0135,
-    E0229, // associated type bindings are not allowed here
     E0278, // requirement is not satisfied
     E0279, // requirement is not satisfied
     E0280, // requirement is not satisfied
-    E0283, // cannot resolve type
     E0284, // cannot resolve type
     E0285, // overflow evaluation builtin bounds
     E0298, // mismatched types between arms
     E0299, // mismatched types between arms
-    E0300, // unexpanded macro
-    E0304, // expected signed integer constant
-    E0305, // expected constant
+    // E0300, // unexpanded macro
+    // E0304, // expected signed integer constant
+    // E0305, // expected constant
     E0311, // thing may not live long enough
     E0312, // lifetime of reference outlives lifetime of borrowed content
     E0313, // lifetime of borrowed pointer outlives lifetime of captured variable
@@ -2261,7 +2325,6 @@ register_diagnostics! {
     E0316, // nested quantification of lifetimes
     E0453, // overruled by outer forbid
     E0471, // constant evaluation error: ..
-    E0472, // asm! is unsupported on this target
     E0473, // dereference of reference outside its lifetime
     E0474, // captured variable `..` does not outlive the enclosing closure
     E0475, // index of slice outside its lifetime
index 17f75074ef230e454f31d4ef995fd31b3674e1a1..e85b0ec77cbbdde79224c9c97c0547758315c133 100644 (file)
@@ -122,7 +122,7 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
         // Pick the def data. This need not be unique, but the more
         // information we encapsulate into
         let def_data = match i.node {
-            ItemDefaultImpl(..) | ItemImpl(..) => DefPathData::Impl,
+            ItemDefaultImpl(..) | ItemImpl(..) => DefPathData::Impl(i.name),
             ItemEnum(..) | ItemStruct(..) | ItemTrait(..) => DefPathData::Type(i.name),
             ItemExternCrate(..) | ItemMod(..) => DefPathData::Mod(i.name),
             ItemStatic(..) | ItemConst(..) | ItemFn(..) => DefPathData::Value(i.name),
index 0f0d59e70b0b8985c42c34d1be294b36f5c8fd93..e903fcf6a56c21e0e6c971703a28c61bddf7bf9d 100644 (file)
@@ -73,7 +73,7 @@ pub enum DefPathData {
     Misc,
 
     // Different kinds of items and item-like things:
-    Impl,
+    Impl(ast::Name),
     Type(ast::Name),
     Mod(ast::Name),
     Value(ast::Name),
@@ -177,6 +177,7 @@ impl DefPathData {
     pub fn as_interned_str(&self) -> InternedString {
         use self::DefPathData::*;
         match *self {
+            Impl(name) |
             Type(name) |
             Mod(name) |
             Value(name) |
@@ -212,10 +213,6 @@ impl DefPathData {
                 InternedString::new("?")
             }
 
-            Impl => {
-                InternedString::new("<impl>")
-            }
-
             ClosureExpr => {
                 InternedString::new("<closure>")
             }
index cbda1e8880b66c4532f902aeaea3211265dd1aa1..7de6099544525328e259949cc1a247abe96ba50e 100644 (file)
@@ -839,11 +839,10 @@ pub fn map_crate<'ast>(forest: &'ast mut Forest) -> Map<'ast> {
 }
 
 /// Used for items loaded from external crate that are being inlined into this
-/// crate.  The `path` should be the path to the item but should not include
-/// the item itself.
+/// crate.
 pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>,
-                                          path: Vec<PathElem>,
-                                          def_path: DefPath,
+                                          parent_path: Vec<PathElem>,
+                                          parent_def_path: DefPath,
                                           ii: InlinedItem,
                                           fold_ops: F)
                                           -> &'ast InlinedItem {
@@ -862,7 +861,7 @@ pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>,
     };
 
     let ii_parent = map.forest.inlined_items.alloc(InlinedParent {
-        path: path,
+        path: parent_path,
         ii: ii
     });
 
@@ -872,7 +871,7 @@ pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>,
             map.krate(),
             ii_parent,
             ii_parent_id,
-            def_path,
+            parent_def_path,
             mem::replace(&mut *map.map.borrow_mut(), vec![]),
             mem::replace(&mut *map.definitions.borrow_mut(), Definitions::new()));
     ii_parent.ii.visit(&mut collector);
index bd97cb128e967a0c9f535a66c3d087541bf86aa3..2712ed2a190380dba8c16ebd3672403501c4a350 100644 (file)
 //!
 //! This API is completely unstable and subject to change.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
 #![feature(box_patterns)]
 #![feature(box_syntax)]
 #![feature(cell_extras)]
-#![feature(clone_from_slice)]
 #![feature(collections)]
 #![feature(const_fn)]
 #![feature(enumset)]
-#![feature(hashmap_hasher)]
-#![feature(into_cow)]
 #![feature(iter_arith)]
 #![feature(libc)]
 #![feature(nonzero)]
-#![feature(num_bits_bytes)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(rustc_private)]
@@ -47,7 +40,6 @@
 #![feature(staged_api)]
 #![feature(str_char)]
 #![feature(time2)]
-#![feature(wrapping)]
 #![cfg_attr(test, feature(test))]
 
 #![allow(trivial_casts)]
@@ -59,6 +51,7 @@ extern crate fmt_macros;
 extern crate getopts;
 extern crate graphviz;
 extern crate libc;
+extern crate rbml;
 extern crate rustc_llvm;
 extern crate rustc_back;
 extern crate rustc_front;
@@ -89,6 +82,8 @@ pub mod back {
     pub use rustc_back::svh;
 }
 
+pub mod dep_graph;
+
 pub mod front {
     pub mod check_attr;
     pub mod map;
@@ -102,7 +97,6 @@ pub mod middle {
     pub mod check_static_recursion;
     pub mod check_loop;
     pub mod check_match;
-    pub mod check_no_asm;
     pub mod check_rvalues;
     pub mod const_eval;
     pub mod cstore;
index d63f0deaa61b65ff07ab15b0ff0a07023d26aeb6..3676e2306468709c08ef183f1f09a001bf422c2f 100644 (file)
@@ -16,9 +16,6 @@
 
 use lint::{LintPass, LateLintPass, LintArray};
 
-// name of the future-incompatible group
-pub const FUTURE_INCOMPATIBLE: &'static str = "future_incompatible";
-
 declare_lint! {
     pub CONST_ERR,
     Warn,
@@ -121,6 +118,18 @@ declare_lint! {
     "detects trivial casts of numeric types which could be removed"
 }
 
+declare_lint! {
+    pub PRIVATE_IN_PUBLIC,
+    Warn,
+    "detect private items in public interfaces not caught by the old implementation"
+}
+
+declare_lint! {
+    pub INVALID_TYPE_PARAM_DEFAULT,
+    Warn,
+    "type parameter default erroneously allowed in invalid location"
+}
+
 declare_lint! {
     pub MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
     Warn,
@@ -157,6 +166,8 @@ impl LintPass for HardwiredLints {
             FAT_PTR_TRANSMUTES,
             TRIVIAL_CASTS,
             TRIVIAL_NUMERIC_CASTS,
+            PRIVATE_IN_PUBLIC,
+            INVALID_TYPE_PARAM_DEFAULT,
             MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
             CONST_ERR,
             RAW_POINTER_DERIVE
index 343963e7c809d08ab870bd590d7aa92c40fd0fd3..c41a361fcc309338ea1b2c3c332636038372baa7 100644 (file)
 //! for all lint attributes.
 use self::TargetLint::*;
 
+use dep_graph::DepNode;
 use middle::privacy::AccessLevels;
-use middle::ty::{self, Ty};
-use session::{early_error, Session};
+use middle::ty;
+use session::{config, early_error, Session};
 use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass};
 use lint::{EarlyLintPass, EarlyLintPassObject, LateLintPass, LateLintPassObject};
 use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid};
@@ -36,10 +37,12 @@ use util::nodemap::FnvHashMap;
 
 use std::cell::RefCell;
 use std::cmp;
+use std::default::Default as StdDefault;
 use std::mem;
 use syntax::ast_util::{self, IdVisitingOperation};
 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;
@@ -47,7 +50,6 @@ use rustc_front::hir;
 use rustc_front::util;
 use rustc_front::intravisit as hir_visit;
 use syntax::visit as ast_visit;
-use syntax::diagnostic;
 
 /// Information about the registered lints.
 ///
@@ -74,10 +76,22 @@ pub struct LintStore {
     /// is true if the lint group was added by a plugin.
     lint_groups: FnvHashMap<&'static str, (Vec<LintId>, bool)>,
 
+    /// Extra info for future incompatibility lints, descibing the
+    /// issue or RFC that caused the incompatibility.
+    future_incompatible: FnvHashMap<LintId, FutureIncompatibleInfo>,
+
     /// Maximum level a lint can be
     lint_cap: Option<Level>,
 }
 
+/// Extra information for a future incompatibility lint. See the call
+/// to `register_future_incompatible` in `librustc_lint/lib.rs` for
+/// guidelines.
+pub struct FutureIncompatibleInfo {
+    pub id: LintId,
+    pub reference: &'static str // e.g., a URL for an issue/PR/RFC or error code
+}
+
 /// The targed of the `by_name` map, which accounts for renaming/deprecation.
 enum TargetLint {
     /// A direct lint target
@@ -122,6 +136,7 @@ impl LintStore {
             late_passes: Some(vec!()),
             by_name: FnvHashMap(),
             levels: FnvHashMap(),
+            future_incompatible: FnvHashMap(),
             lint_groups: FnvHashMap(),
             lint_cap: None,
         }
@@ -167,7 +182,7 @@ impl LintStore {
                 match (sess, from_plugin) {
                     // We load builtin lints first, so a duplicate is a compiler bug.
                     // Use early_error when handling -W help with no crate.
-                    (None, _) => early_error(diagnostic::Auto, &msg[..]),
+                    (None, _) => early_error(config::ErrorOutputType::default(), &msg[..]),
                     (Some(sess), false) => sess.bug(&msg[..]),
 
                     // A duplicate name from a plugin is a user error.
@@ -181,6 +196,20 @@ impl LintStore {
         }
     }
 
+    pub fn register_future_incompatible(&mut self,
+                                        sess: Option<&Session>,
+                                        lints: Vec<FutureIncompatibleInfo>) {
+        let ids = lints.iter().map(|f| f.id).collect();
+        self.register_group(sess, false, "future_incompatible", ids);
+        for info in lints {
+            self.future_incompatible.insert(info.id, info);
+        }
+    }
+
+    pub fn future_incompatible(&self, id: LintId) -> Option<&FutureIncompatibleInfo> {
+        self.future_incompatible.get(&id)
+    }
+
     pub fn register_group(&mut self, sess: Option<&Session>,
                           from_plugin: bool, name: &'static str,
                           to: Vec<LintId>) {
@@ -191,7 +220,7 @@ impl LintStore {
             match (sess, from_plugin) {
                 // We load builtin lints first, so a duplicate is a compiler bug.
                 // Use early_error when handling -W help with no crate.
-                (None, _) => early_error(diagnostic::Auto, &msg[..]),
+                (None, _) => early_error(config::ErrorOutputType::default(), &msg[..]),
                 (Some(sess), false) => sess.bug(&msg[..]),
 
                 // A duplicate name from a plugin is a user error.
@@ -375,8 +404,20 @@ pub fn raw_emit_lint(sess: &Session,
                      lvlsrc: LevelSource,
                      span: Option<Span>,
                      msg: &str) {
+    raw_struct_lint(sess, lints, lint, lvlsrc, span, msg).emit();
+}
+
+pub fn raw_struct_lint<'a>(sess: &'a Session,
+                           lints: &LintStore,
+                           lint: &'static Lint,
+                           lvlsrc: LevelSource,
+                           span: Option<Span>,
+                           msg: &str)
+                           -> DiagnosticBuilder<'a> {
     let (mut level, source) = lvlsrc;
-    if level == Allow { return }
+    if level == Allow {
+        return sess.diagnostic().struct_dummy();
+    }
 
     let name = lint.name_lower();
     let mut def = None;
@@ -401,29 +442,35 @@ pub fn raw_emit_lint(sess: &Session,
     // For purposes of printing, we can treat forbid as deny.
     if level == Forbid { level = Deny; }
 
-    match (level, span) {
-        (Warn, Some(sp)) => sess.span_warn(sp, &msg[..]),
-        (Warn, None)     => sess.warn(&msg[..]),
-        (Deny, Some(sp)) => sess.span_err(sp, &msg[..]),
-        (Deny, None)     => sess.err(&msg[..]),
+    let mut err = match (level, span) {
+        (Warn, Some(sp)) => sess.struct_span_warn(sp, &msg[..]),
+        (Warn, None)     => sess.struct_warn(&msg[..]),
+        (Deny, Some(sp)) => sess.struct_span_err(sp, &msg[..]),
+        (Deny, None)     => sess.struct_err(&msg[..]),
         _ => sess.bug("impossible level in raw_emit_lint"),
-    }
+    };
 
     // Check for future incompatibility lints and issue a stronger warning.
-    let future_incompat_lints = &lints.lint_groups[builtin::FUTURE_INCOMPATIBLE];
-    let this_id = LintId::of(lint);
-    if future_incompat_lints.0.iter().any(|&id| id == this_id) {
-        let msg = "this lint will become a HARD ERROR in a future release!";
+    if let Some(future_incompatible) = lints.future_incompatible(LintId::of(lint)) {
+        let explanation = format!("this was previously accepted by the compiler \
+                                   but is being phased out; \
+                                   it will become a hard error in a future release!");
+        let citation = format!("for more information, see {}",
+                               future_incompatible.reference);
         if let Some(sp) = span {
-            sess.span_note(sp, msg);
+            err.fileline_warn(sp, &explanation);
+            err.fileline_note(sp, &citation);
         } else {
-            sess.note(msg);
+            err.warn(&explanation);
+            err.note(&citation);
         }
     }
 
     if let Some(span) = def {
-        sess.span_note(span, "lint level defined here");
+        err.span_note(span, "lint level defined here");
     }
+
+    err
 }
 
 pub trait LintContext: Sized {
@@ -440,44 +487,74 @@ pub trait LintContext: Sized {
         self.lints().levels.get(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl)
     }
 
-    fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
-        let (level, src) = match self.lints().levels.get(&LintId::of(lint)) {
-            None => return,
-            Some(&(Warn, src)) => {
+    fn level_src(&self, lint: &'static Lint) -> Option<LevelSource> {
+        self.lints().levels.get(&LintId::of(lint)).map(|ls| match ls {
+            &(Warn, src) => {
                 let lint_id = LintId::of(builtin::WARNINGS);
                 (self.lints().get_level_source(lint_id).0, src)
             }
-            Some(&pair) => pair,
+            _ => *ls
+        })
+    }
+
+    fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
+        let (level, src) = match self.level_src(lint) {
+            None => return,
+            Some(pair) => pair,
         };
 
         raw_emit_lint(&self.sess(), self.lints(), lint, (level, src), span, msg);
     }
 
+    fn lookup(&self,
+              lint: &'static Lint,
+              span: Option<Span>,
+              msg: &str)
+              -> DiagnosticBuilder {
+        let (level, src) = match self.level_src(lint) {
+            None => return self.sess().diagnostic().struct_dummy(),
+            Some(pair) => pair,
+        };
+
+        raw_struct_lint(&self.sess(), self.lints(), lint, (level, src), span, msg)
+    }
+
     /// Emit a lint at the appropriate level, for a particular span.
     fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
         self.lookup_and_emit(lint, Some(span), msg);
     }
 
+    fn struct_span_lint(&self,
+                        lint: &'static Lint,
+                        span: Span,
+                        msg: &str)
+                        -> DiagnosticBuilder {
+        self.lookup(lint, Some(span), msg)
+    }
+
     /// Emit a lint and note at the appropriate level, for a particular span.
     fn span_lint_note(&self, lint: &'static Lint, span: Span, msg: &str,
                       note_span: Span, note: &str) {
-        self.span_lint(lint, span, msg);
+        let mut err = self.lookup(lint, Some(span), msg);
         if self.current_level(lint) != Level::Allow {
             if note_span == span {
-                self.sess().fileline_note(note_span, note)
+                err.fileline_note(note_span, note);
             } else {
-                self.sess().span_note(note_span, note)
+                err.span_note(note_span, note);
             }
         }
+        err.emit();
     }
 
     /// Emit a lint and help at the appropriate level, for a particular span.
     fn span_lint_help(&self, lint: &'static Lint, span: Span,
                       msg: &str, help: &str) {
+        let mut err = self.lookup(lint, Some(span), msg);
         self.span_lint(lint, span, msg);
         if self.current_level(lint) != Level::Allow {
-            self.sess().span_help(span, help)
+            err.span_help(span, help);
         }
+        err.emit();
     }
 
     /// Emit a lint at the appropriate level, with no associated span.
@@ -1044,12 +1121,13 @@ impl LateLintPass for GatherNodeLevels {
     }
 }
 
-enum CheckLintNameResult {
+enum CheckLintNameResult<'a> {
     Ok,
     // Lint doesn't exist
     NoLint,
-    // The lint is either renamed or removed and a warning was generated
-    Mentioned
+    // The lint is either renamed or removed and a warning was
+    // generated in the DiagnosticBuilder
+    Mentioned(DiagnosticBuilder<'a>)
 }
 
 /// Checks the name of a lint for its existence, and whether it was
@@ -1059,27 +1137,27 @@ enum CheckLintNameResult {
 /// it emits non-fatal warnings and there are *two* lint passes that
 /// inspect attributes, this is only run from the late pass to avoid
 /// printing duplicate warnings.
-fn check_lint_name(sess: &Session,
-                   lint_cx: &LintStore,
-                   lint_name: &str,
-                   span: Option<Span>) -> CheckLintNameResult {
+fn check_lint_name<'a>(sess: &'a Session,
+                       lint_cx: &LintStore,
+                       lint_name: &str,
+                       span: Option<Span>) -> CheckLintNameResult<'a> {
     match lint_cx.by_name.get(lint_name) {
         Some(&Renamed(ref new_name, _)) => {
             let warning = format!("lint {} has been renamed to {}",
                                   lint_name, new_name);
-            match span {
-                Some(span) => sess.span_warn(span, &warning[..]),
-                None => sess.warn(&warning[..]),
+            let db = match span {
+                Some(span) => sess.struct_span_warn(span, &warning[..]),
+                None => sess.struct_warn(&warning[..]),
             };
-            CheckLintNameResult::Mentioned
+            CheckLintNameResult::Mentioned(db)
         },
         Some(&Removed(ref reason)) => {
             let warning = format!("lint {} has been removed: {}", lint_name, reason);
-            match span {
-                Some(span) => sess.span_warn(span, &warning[..]),
-                None => sess.warn(&warning[..])
+            let db = match span {
+                Some(span) => sess.struct_span_warn(span, &warning[..]),
+                None => sess.struct_warn(&warning[..])
             };
-            CheckLintNameResult::Mentioned
+            CheckLintNameResult::Mentioned(db)
         },
         None => {
             match lint_cx.lint_groups.get(lint_name) {
@@ -1110,7 +1188,9 @@ fn check_lint_name_attribute(cx: &LateContext, attr: &ast::Attribute) {
             Ok((lint_name, _, span)) => {
                 match check_lint_name(&cx.tcx.sess, &cx.lints, &lint_name[..], Some(span)) {
                     CheckLintNameResult::Ok => (),
-                    CheckLintNameResult::Mentioned => (),
+                    CheckLintNameResult::Mentioned(mut db) => {
+                        db.emit();
+                    }
                     CheckLintNameResult::NoLint => {
                         cx.span_lint(builtin::UNKNOWN_LINTS, span,
                                      &format!("unknown lint: `{}`",
@@ -1125,16 +1205,15 @@ fn check_lint_name_attribute(cx: &LateContext, attr: &ast::Attribute) {
 // Checks the validity of lint names derived from the command line
 fn check_lint_name_cmdline(sess: &Session, lint_cx: &LintStore,
                            lint_name: &str, level: Level) {
-    let explain = match check_lint_name(sess, lint_cx, lint_name, None) {
-        CheckLintNameResult::Ok => false,
-        CheckLintNameResult::Mentioned => true,
+    let db = match check_lint_name(sess, lint_cx, lint_name, None) {
+        CheckLintNameResult::Ok => None,
+        CheckLintNameResult::Mentioned(db) => Some(db),
         CheckLintNameResult::NoLint => {
-            sess.err(&format!("unknown lint: `{}`", lint_name));
-            true
+            Some(sess.struct_err(&format!("unknown lint: `{}`", lint_name)))
         }
     };
 
-    if explain {
+    if let Some(mut db) = db {
         let msg = format!("requested on the command line with `{} {}`",
                           match level {
                               Level::Allow => "-A",
@@ -1143,7 +1222,8 @@ fn check_lint_name_cmdline(sess: &Session, lint_cx: &LintStore,
                               Level::Forbid => "-F",
                           },
                           lint_name);
-        sess.note(&msg);
+        db.note(&msg);
+        db.emit();
     }
 }
 
@@ -1152,6 +1232,8 @@ fn check_lint_name_cmdline(sess: &Session, lint_cx: &LintStore,
 ///
 /// Consumes the `lint_store` field of the `Session`.
 pub fn check_crate(tcx: &ty::ctxt, access_levels: &AccessLevels) {
+    let _task = tcx.dep_graph.in_task(DepNode::LateLintCheck);
+
     let krate = tcx.map.krate();
     let mut cx = LateContext::new(tcx, krate, access_levels);
 
index 23be6117f190f51376c31aee808d994756fd9c93..6061525ef398cc09da3749f08068d88c4113e3c6 100644 (file)
@@ -41,7 +41,7 @@ use rustc_front::hir;
 
 pub use lint::context::{LateContext, EarlyContext, LintContext, LintStore,
                         raw_emit_lint, check_crate, check_ast_crate, gather_attrs,
-                        GatherNodeLevels};
+                        raw_struct_lint, GatherNodeLevels, FutureIncompatibleInfo};
 
 /// Specification of a single lint.
 #[derive(Copy, Clone, Debug)]
index 3b436907ac50a1de438fc29f97bac26a2b53e442..abe85125215708f38abd8038f0295e6efb8df76b 100644 (file)
@@ -352,6 +352,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
             hir::ExprBox(ref e) |
             hir::ExprAddrOf(_, ref e) |
             hir::ExprCast(ref e, _) |
+            hir::ExprType(ref e, _) |
             hir::ExprUnary(_, ref e) |
             hir::ExprField(ref e, _) |
             hir::ExprTupField(ref e, _) => {
@@ -368,8 +369,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
                 }), pred);
                 let post_outputs = self.exprs(outputs.map(|a| {
                     debug!("cfg::construct InlineAsm id:{} output:{:?}", expr.id, a);
-                    let &(_, ref expr, _) = a;
-                    &**expr
+                    &*a.expr
                 }), post_inputs);
                 self.add_ast_node(expr.id, &[post_outputs])
             }
index 8fa2dac2da14020b7a90a2d55c8cc90e2f3b78ef..e807092507082156781aab3edb6619d801daabb8 100644 (file)
 /// This module provides linkage between rustc::middle::graph and
 /// libgraphviz traits.
 
-use std::borrow::IntoCow;
-
 // For clarity, rename the graphviz crate locally to dot.
 use graphviz as dot;
+use graphviz::IntoCow;
 
 use syntax::ast;
 
index 69d8dfc361328e10440e8c8e93212e2cedec3e7f..6001b1205871afe404bcb9f109e9bf55e53953dc 100644 (file)
@@ -24,6 +24,7 @@
 // - It's not possible to take the address of a static item with unsafe interior. This is enforced
 // by borrowck::gather_loans
 
+use dep_graph::DepNode;
 use middle::ty::cast::{CastKind};
 use middle::const_eval::{self, ConstEvalErr};
 use middle::const_eval::ErrKind::IndexOpFeatureGated;
@@ -174,21 +175,6 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
             _ => Mode::Var
         };
 
-        // Ensure the arguments are simple, not mutable/by-ref or patterns.
-        if mode == Mode::ConstFn {
-            for arg in &fd.inputs {
-                match arg.pat.node {
-                    hir::PatWild => {}
-                    hir::PatIdent(hir::BindByValue(hir::MutImmutable), _, None) => {}
-                    _ => {
-                        span_err!(self.tcx.sess, arg.pat.span, E0022,
-                                  "arguments of constant functions can only \
-                                   be immutable by-value bindings");
-                    }
-                }
-            }
-        }
-
         let qualif = self.with_mode(mode, |this| {
             this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
             intravisit::walk_fn(this, fk, fd, b, s);
@@ -224,14 +210,15 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
                 // this doesn't come from a macro that has #[allow_internal_unstable]
                 !self.tcx.sess.codemap().span_allows_unstable(expr.span)
             {
-                self.tcx.sess.span_err(
+                let mut err = self.tcx.sess.struct_span_err(
                     expr.span,
                     "const fns are an unstable feature");
                 fileline_help!(
-                    self.tcx.sess,
+                    &mut err,
                     expr.span,
                     "in Nightly builds, add `#![feature(const_fn)]` to the crate \
                      attributes to enable");
+                err.emit();
             }
 
             let qualif = self.fn_like(fn_like.kind(),
@@ -395,24 +382,20 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
     fn visit_block(&mut self, block: &hir::Block) {
         // Check all statements in the block
         for stmt in &block.stmts {
-            let span = match stmt.node {
+            match stmt.node {
                 hir::StmtDecl(ref decl, _) => {
                     match decl.node {
-                        hir::DeclLocal(_) => decl.span,
-
+                        hir::DeclLocal(_) => {},
                         // Item statements are allowed
                         hir::DeclItem(_) => continue
                     }
                 }
-                hir::StmtExpr(ref expr, _) => expr.span,
-                hir::StmtSemi(ref semi, _) => semi.span,
-            };
-            self.add_qualif(ConstQualif::NOT_CONST);
-            if self.mode != Mode::Var {
-                span_err!(self.tcx.sess, span, E0016,
-                          "blocks in {}s are limited to items and \
-                           tail expressions", self.msg());
+                hir::StmtExpr(_, _) => {},
+                hir::StmtSemi(_, _) => {},
             }
+            self.add_qualif(ConstQualif::NOT_CONST);
+            // anything else should have been caught by check_const_fn
+            assert_eq!(self.mode, Mode::Var);
         }
         intravisit::walk_block(self, block);
     }
@@ -655,13 +638,10 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                 Some(def::DefConst(did)) |
                 Some(def::DefAssociatedConst(did)) => {
                     if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did,
-                                                                       Some(e.id)) {
+                                                                       Some(e.id),
+                                                                       None) {
                         let inner = v.global_expr(Mode::Const, expr);
                         v.add_qualif(inner);
-                    } else {
-                        v.tcx.sess.span_bug(e.span,
-                                            "DefConst or DefAssociatedConst \
-                                             doesn't point to a constant");
                     }
                 }
                 Some(def::DefLocal(..)) if v.mode == Mode::ConstFn => {
@@ -714,27 +694,27 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
             if !is_const {
                 v.add_qualif(ConstQualif::NOT_CONST);
                 if v.mode != Mode::Var {
-                    fn span_limited_call_error(tcx: &ty::ctxt, span: Span, s: &str) {
-                        span_err!(tcx.sess, span, E0015, "{}", s);
-                    }
-
                     // FIXME(#24111) Remove this check when const fn stabilizes
-                    if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features {
-                        span_limited_call_error(&v.tcx, e.span,
-                                                &format!("function calls in {}s are limited to \
-                                                          struct and enum constructors",
-                                                         v.msg()));
-                        v.tcx.sess.span_note(e.span,
-                                             "a limited form of compile-time function \
-                                              evaluation is available on a nightly \
-                                              compiler via `const fn`");
+                    let (msg, note) =
+                        if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features {
+                        (format!("function calls in {}s are limited to \
+                                  struct and enum constructors",
+                                 v.msg()),
+                         Some("a limited form of compile-time function \
+                               evaluation is available on a nightly \
+                               compiler via `const fn`"))
                     } else {
-                        span_limited_call_error(&v.tcx, e.span,
-                                                &format!("function calls in {}s are limited \
-                                                          to constant functions, \
-                                                          struct and enum constructors",
-                                                         v.msg()));
+                        (format!("function calls in {}s are limited \
+                                  to constant functions, \
+                                  struct and enum constructors",
+                                 v.msg()),
+                         None)
+                    };
+                    let mut err = struct_span_err!(v.tcx.sess, e.span, E0015, "{}", msg);
+                    if let Some(note) = note {
+                        err.span_note(e.span, note);
                     }
+                    err.emit();
                 }
             }
         }
@@ -784,6 +764,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
         hir::ExprField(..) |
         hir::ExprTupField(..) |
         hir::ExprVec(_) |
+        hir::ExprType(..) |
         hir::ExprTup(..) => {}
 
         // Conditional control flow (possible to implement).
@@ -840,13 +821,12 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp
 }
 
 pub fn check_crate(tcx: &ty::ctxt) {
-    tcx.map.krate().visit_all_items(&mut CheckCrateVisitor {
+    tcx.visit_all_items_in_krate(DepNode::CheckConst, &mut CheckCrateVisitor {
         tcx: tcx,
         mode: Mode::Var,
         qualif: ConstQualif::NOT_CONST,
         rvalue_borrows: NodeMap()
     });
-
     tcx.sess.abort_if_errors();
 }
 
index 04f907af70bbcbd6ebaa8108afb0d0c35927e908..dc777585e4194e941b08b1478e5cde2ce88e7b31 100644 (file)
@@ -12,15 +12,15 @@ pub use self::Constructor::*;
 use self::Usefulness::*;
 use self::WitnessPreference::*;
 
+use dep_graph::DepNode;
 use middle::const_eval::{compare_const_vals, ConstVal};
 use middle::const_eval::{eval_const_expr, eval_const_expr_partial};
 use middle::const_eval::{const_expr_to_pat, lookup_const_by_id};
 use middle::const_eval::EvalHint::ExprTypeChecked;
 use middle::def::*;
 use middle::def_id::{DefId};
-use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Init};
-use middle::expr_use_visitor::{JustWrite, LoanCause, MutateMode};
-use middle::expr_use_visitor::WriteAndRead;
+use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor};
+use middle::expr_use_visitor::{LoanCause, MutateMode};
 use middle::expr_use_visitor as euv;
 use middle::infer;
 use middle::mem_categorization::{cmt};
@@ -155,7 +155,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> {
 }
 
 pub fn check_crate(tcx: &ty::ctxt) {
-    tcx.map.krate().visit_all_items(&mut MatchCheckCtxt {
+    tcx.visit_all_items_in_krate(DepNode::MatchCheck, &mut MatchCheckCtxt {
         tcx: tcx,
         param_env: tcx.empty_parameter_environment(),
     });
@@ -215,12 +215,13 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) {
             if inlined_arms.is_empty() {
                 if !pat_ty.is_empty(cx.tcx) {
                     // We know the type is inhabited, so this must be wrong
-                    span_err!(cx.tcx.sess, ex.span, E0002,
-                              "non-exhaustive patterns: type {} is non-empty",
-                              pat_ty);
-                    span_help!(cx.tcx.sess, ex.span,
+                    let mut err = struct_span_err!(cx.tcx.sess, ex.span, E0002,
+                                                   "non-exhaustive patterns: type {} is non-empty",
+                                                   pat_ty);
+                    span_help!(&mut err, ex.span,
                         "Please ensure that all possible cases are being handled; \
                          possibly adding wildcards or more match arms.");
+                    err.emit();
                 }
                 // If the type *is* empty, it's vacuously exhaustive
                 return;
@@ -250,14 +251,16 @@ fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat)
                             variant.name == ident.node.unhygienic_name
                                 && variant.kind() == VariantKind::Unit
                         ) {
-                            span_warn!(cx.tcx.sess, p.span, E0170,
+                            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 `{}`",
-                                ident.node, pat_ty);
-                            fileline_help!(cx.tcx.sess, p.span,
+                                ident.node, ty_path);
+                            fileline_help!(err, p.span,
                                 "if you meant to match on a variant, \
                                  consider making the path in the pattern qualified: `{}::{}`",
-                                pat_ty, ident.node);
+                                ty_path, ident.node);
+                            err.emit();
                         }
                     }
                 }
@@ -281,13 +284,13 @@ fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) {
                 Ok(_) => {}
 
                 Err(err) => {
-                    span_err!(cx.tcx.sess, err.span, E0471,
-                              "constant evaluation error: {}",
-                              err.description());
+                    let mut diag = struct_span_err!(cx.tcx.sess, err.span, E0471,
+                                                    "constant evaluation error: {}",
+                                                    err.description());
                     if !p.span.contains(err.span) {
-                        cx.tcx.sess.span_note(p.span,
-                                              "in pattern here")
+                        diag.span_note(p.span, "in pattern here");
                     }
+                    diag.emit();
                 }
             }
         }
@@ -452,7 +455,8 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
                 let def = self.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def());
                 match def {
                     Some(DefAssociatedConst(did)) |
-                    Some(DefConst(did)) => match lookup_const_by_id(self.tcx, did, Some(pat.id)) {
+                    Some(DefConst(did)) => match lookup_const_by_id(self.tcx, did,
+                                                                    Some(pat.id), None) {
                         Some(const_expr) => {
                             const_expr_to_pat(self.tcx, const_expr, pat.span).map(|new_pat| {
 
@@ -516,7 +520,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
         ty::TyEnum(adt, _) | ty::TyStruct(adt, _)  => {
             let v = adt.variant_of_ctor(ctor);
             if let VariantKind::Struct = v.kind() {
-                let field_pats: Vec<_> = v.fields.iter()
+                let field_pats: hir::HirVec<_> = v.fields.iter()
                     .zip(pats)
                     .filter(|&(_, ref pat)| pat.node != hir::PatWild)
                     .map(|(field, pat)| Spanned {
@@ -539,14 +543,14 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
                ty::TyArray(_, n) => match ctor {
                     &Single => {
                         assert_eq!(pats_len, n);
-                        hir::PatVec(pats.collect(), None, vec!())
+                        hir::PatVec(pats.collect(), None, hir::HirVec::new())
                     },
                     _ => unreachable!()
                 },
                 ty::TySlice(_) => match ctor {
                     &Slice(n) => {
                         assert_eq!(pats_len, n);
-                        hir::PatVec(pats.collect(), None, vec!())
+                        hir::PatVec(pats.collect(), None, hir::HirVec::new())
                     },
                     _ => unreachable!()
                 },
@@ -561,7 +565,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
 
         ty::TyArray(_, len) => {
             assert_eq!(pats_len, len);
-            hir::PatVec(pats.collect(), None, vec![])
+            hir::PatVec(pats.collect(), None, hir::HirVec::new())
         }
 
         _ => {
@@ -1075,9 +1079,10 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
         } else if has_guard {
             span_err!(cx.tcx.sess, p.span, E0008, "cannot bind by-move into a pattern guard");
         } else if by_ref_span.is_some() {
-            span_err!(cx.tcx.sess, p.span, E0009,
-                "cannot bind by-move and by-ref in the same pattern");
-            span_note!(cx.tcx.sess, by_ref_span.unwrap(), "by-ref binding occurs here");
+            let mut err = struct_span_err!(cx.tcx.sess, p.span, E0009,
+                                           "cannot bind by-move and by-ref in the same pattern");
+            span_note!(&mut err, by_ref_span.unwrap(), "by-ref binding occurs here");
+            err.emit();
         }
     };
 
@@ -1156,10 +1161,10 @@ impl<'a, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'tcx> {
     fn decl_without_init(&mut self, _: NodeId, _: Span) {}
     fn mutate(&mut self, _: NodeId, span: Span, _: cmt, mode: MutateMode) {
         match mode {
-            JustWrite | WriteAndRead => {
+            MutateMode::JustWrite | MutateMode::WriteAndRead => {
                 span_err!(self.cx.tcx.sess, span, E0302, "cannot assign in a pattern guard")
             }
-            Init => {}
+            MutateMode::Init => {}
         }
     }
 }
diff --git a/src/librustc/middle/check_no_asm.rs b/src/librustc/middle/check_no_asm.rs
deleted file mode 100644 (file)
index b9f8f20..0000000
+++ /dev/null
@@ -1,41 +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.
-
-/// Run over the whole crate and check for ExprInlineAsm.
-/// Inline asm isn't allowed on virtual ISA based targets, so we reject it
-/// here.
-
-use session::Session;
-
-use syntax::ast;
-use syntax::visit::Visitor;
-use syntax::visit;
-
-pub fn check_crate(sess: &Session, krate: &ast::Crate) {
-    if sess.target.target.options.allow_asm { return; }
-
-    visit::walk_crate(&mut CheckNoAsm { sess: sess, }, krate);
-}
-
-#[derive(Copy, Clone)]
-struct CheckNoAsm<'a> {
-    sess: &'a Session,
-}
-
-impl<'a, 'v> Visitor<'v> for CheckNoAsm<'a> {
-    fn visit_expr(&mut self, e: &ast::Expr) {
-        match e.node {
-            ast::ExprInlineAsm(_) => span_err!(self.sess, e.span, E0472,
-                                               "asm! is unsupported on this target"),
-            _ => {},
-        }
-        visit::walk_expr(self, e)
-    }
-}
index 35adeae3e617ce4966ff461208d0553ccc8bbde7..8a3e039ac6e535cc2fcc2d514d449b22b8d0f465 100644 (file)
 // Checks that all rvalues in a crate have statically known size. check_crate
 // is the public starting point.
 
+use dep_graph::DepNode;
 use middle::expr_use_visitor as euv;
 use middle::infer;
 use middle::mem_categorization as mc;
 use middle::ty::ParameterEnvironment;
 use middle::ty;
 
-use syntax::ast;
 use rustc_front::hir;
-use syntax::codemap::Span;
 use rustc_front::intravisit;
+use syntax::ast;
+use syntax::codemap::Span;
 
-pub fn check_crate(tcx: &ty::ctxt,
-                   krate: &hir::Crate) {
+pub fn check_crate(tcx: &ty::ctxt) {
     let mut rvcx = RvalueContext { tcx: tcx };
-    krate.visit_all_items(&mut rvcx);
+    tcx.visit_all_items_in_krate(DepNode::RvalueCheck, &mut rvcx);
 }
 
 struct RvalueContext<'a, 'tcx: 'a> {
index 85a3117196acf909c3d0ac7849234053ecdc601a..0882f3f1137eca0c4a829d6b1206a0a5d0da6544 100644 (file)
@@ -99,8 +99,9 @@ pub fn check_crate<'ast>(sess: &Session,
         ast_map: ast_map,
         discriminant_map: RefCell::new(NodeMap()),
     };
-    krate.visit_all_items(&mut visitor);
-    sess.abort_if_errors();
+    sess.abort_if_new_errors(|| {
+        krate.visit_all_items(&mut visitor);
+    });
 }
 
 struct CheckItemRecursionVisitor<'a, 'ast: 'a> {
index d6932c7ca3ce1cc320678f2ccdf2965450fc48c2..2d8d5fdd4b6ebdcc6c16e9abc48d7b3bcd1a67d5 100644 (file)
@@ -18,6 +18,7 @@ use front::map as ast_map;
 use front::map::blocks::FnLikeNode;
 use middle::cstore::{self, CrateStore, InlinedItem};
 use middle::{def, infer, subst, traits};
+use middle::subst::Subst;
 use middle::def_id::DefId;
 use middle::pat_util::def_to_path;
 use middle::ty::{self, Ty};
@@ -25,6 +26,7 @@ use middle::astconv_util::ast_ty_to_prim_ty;
 use util::num::ToPrimitive;
 use util::nodemap::NodeMap;
 
+use graphviz::IntoCow;
 use syntax::{ast, abi};
 use rustc_front::hir::Expr;
 use rustc_front::hir;
@@ -34,8 +36,7 @@ use syntax::parse::token::InternedString;
 use syntax::ptr::P;
 use syntax::codemap;
 
-use std::borrow::{Cow, IntoCow};
-use std::num::wrapping::OverflowingOps;
+use std::borrow::Cow;
 use std::cmp::Ordering;
 use std::collections::hash_map::Entry::Vacant;
 use std::hash;
@@ -43,20 +44,6 @@ use std::mem::transmute;
 use std::{i8, i16, i32, i64, u8, u16, u32, u64};
 use std::rc::Rc;
 
-fn lookup_const<'a>(tcx: &'a ty::ctxt, e: &Expr) -> Option<&'a Expr> {
-    let opt_def = tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
-    match opt_def {
-        Some(def::DefConst(def_id)) |
-        Some(def::DefAssociatedConst(def_id)) => {
-            lookup_const_by_id(tcx, def_id, Some(e.id))
-        }
-        Some(def::DefVariant(enum_def, variant_def, _)) => {
-            lookup_variant_by_id(tcx, enum_def, variant_def)
-        }
-        _ => None
-    }
-}
-
 fn lookup_variant_by_id<'a>(tcx: &'a ty::ctxt,
                             enum_def: DefId,
                             variant_def: DefId)
@@ -88,9 +75,17 @@ fn lookup_variant_by_id<'a>(tcx: &'a ty::ctxt,
     }
 }
 
+/// * `def_id` is the id of the constant.
+/// * `maybe_ref_id` is the id of the expr referencing the constant.
+/// * `param_substs` is the monomorphization substitution for the expression.
+///
+/// `maybe_ref_id` and `param_substs` are optional and are used for
+/// finding substitutions in associated constants. This generally
+/// happens in late/trans const evaluation.
 pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
                                         def_id: DefId,
-                                        maybe_ref_id: Option<ast::NodeId>)
+                                        maybe_ref_id: Option<ast::NodeId>,
+                                        param_substs: Option<&'tcx subst::Substs<'tcx>>)
                                         -> Option<&'tcx Expr> {
     if let Some(node_id) = tcx.map.as_local_node_id(def_id) {
         match tcx.map.find(node_id) {
@@ -111,8 +106,11 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
                         Some(ref_id) => {
                             let trait_id = tcx.trait_of_item(def_id)
                                               .unwrap();
-                            let substs = tcx.node_id_item_substs(ref_id)
-                                            .substs;
+                            let mut substs = tcx.node_id_item_substs(ref_id)
+                                                .substs;
+                            if let Some(param_substs) = param_substs {
+                                substs = substs.subst(tcx, param_substs);
+                            }
                             resolve_trait_associated_const(tcx, ti, trait_id,
                                                            substs)
                         }
@@ -158,8 +156,11 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
                         // a trait-associated const if the caller gives us
                         // the expression that refers to it.
                         Some(ref_id) => {
-                            let substs = tcx.node_id_item_substs(ref_id)
-                                            .substs;
+                            let mut substs = tcx.node_id_item_substs(ref_id)
+                                                .substs;
+                            if let Some(param_substs) = param_substs {
+                                substs = substs.subst(tcx, param_substs);
+                            }
                             resolve_trait_associated_const(tcx, ti, trait_id,
                                                            substs).map(|e| e.id)
                         }
@@ -242,7 +243,7 @@ pub fn lookup_const_fn_by_id<'tcx>(tcx: &ty::ctxt<'tcx>, def_id: DefId)
     }
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub enum ConstVal {
     Float(f64),
     Int(i64),
@@ -332,6 +333,11 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<hir::Pat>
             let path = match def.full_def() {
                 def::DefStruct(def_id) => def_to_path(tcx, def_id),
                 def::DefVariant(_, variant_did, _) => def_to_path(tcx, variant_did),
+                def::DefFn(..) => return P(hir::Pat {
+                    id: expr.id,
+                    node: hir::PatLit(P(expr.clone())),
+                    span: span,
+                }),
                 _ => unreachable!()
             };
             let pats = args.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect();
@@ -352,22 +358,22 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<hir::Pat>
 
         hir::ExprVec(ref exprs) => {
             let pats = exprs.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect();
-            hir::PatVec(pats, None, vec![])
+            hir::PatVec(pats, None, hir::HirVec::new())
         }
 
         hir::ExprPath(_, ref path) => {
             let opt_def = tcx.def_map.borrow().get(&expr.id).map(|d| d.full_def());
             match opt_def {
                 Some(def::DefStruct(..)) =>
-                    hir::PatStruct(path.clone(), vec![], false),
+                    hir::PatStruct(path.clone(), hir::HirVec::new(), false),
                 Some(def::DefVariant(..)) =>
                     hir::PatEnum(path.clone(), None),
-                _ => {
-                    match lookup_const(tcx, expr) {
-                        Some(actual) => return const_expr_to_pat(tcx, actual, span),
-                        _ => unreachable!()
-                    }
-                }
+                Some(def::DefConst(def_id)) |
+                Some(def::DefAssociatedConst(def_id)) => {
+                    let expr = lookup_const_by_id(tcx, def_id, Some(expr.id), None).unwrap();
+                    return const_expr_to_pat(tcx, expr, span);
+                },
+                _ => unreachable!(),
             }
         }
 
@@ -1008,7 +1014,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
                           _ => (None, None)
                       }
                   } else {
-                      (lookup_const_by_id(tcx, def_id, Some(e.id)), None)
+                      (lookup_const_by_id(tcx, def_id, Some(e.id), None), None)
                   }
               }
               Some(def::DefAssociatedConst(def_id)) => {
@@ -1043,7 +1049,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
                           },
                       }
                   } else {
-                      (lookup_const_by_id(tcx, def_id, Some(e.id)), None)
+                      (lookup_const_by_id(tcx, def_id, Some(e.id), None), None)
                   }
               }
               Some(def::DefVariant(enum_def, variant_def, _)) => {
@@ -1121,6 +1127,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
             None => unreachable!(),
         }
       }
+      hir::ExprType(ref e, _) => try!(eval_const_expr_partial(tcx, &**e, ty_hint, fn_args)),
       hir::ExprTup(_) => Tuple(e.id),
       hir::ExprStruct(..) => Struct(e.id),
       hir::ExprIndex(ref arr, ref idx) => {
@@ -1177,46 +1184,40 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
       },
       hir::ExprTupField(ref base, index) => {
         let base_hint = ty_hint.erase_hint();
-        if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint, fn_args) {
-            if let Tuple(tup_id) = c {
-                if let hir::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node {
-                    if index.node < fields.len() {
-                        return eval_const_expr_partial(tcx, &fields[index.node], base_hint, fn_args)
-                    } else {
-                        signal!(e, TupleIndexOutOfBounds);
-                    }
+        let c = try!(eval_const_expr_partial(tcx, base, base_hint, fn_args));
+        if let Tuple(tup_id) = c {
+            if let hir::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node {
+                if index.node < fields.len() {
+                    return eval_const_expr_partial(tcx, &fields[index.node], base_hint, fn_args)
                 } else {
-                    unreachable!()
+                    signal!(e, TupleIndexOutOfBounds);
                 }
             } else {
-                signal!(base, ExpectedConstTuple);
+                unreachable!()
             }
         } else {
-            signal!(base, NonConstPath)
+            signal!(base, ExpectedConstTuple);
         }
       }
       hir::ExprField(ref base, field_name) => {
         let base_hint = ty_hint.erase_hint();
         // Get the base expression if it is a struct and it is constant
-        if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint, fn_args) {
-            if let Struct(struct_id) = c {
-                if let hir::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node {
-                    // Check that the given field exists and evaluate it
-                    // if the idents are compared run-pass/issue-19244 fails
-                    if let Some(f) = fields.iter().find(|f| f.name.node
-                                                         == field_name.node) {
-                        return eval_const_expr_partial(tcx, &*f.expr, base_hint, fn_args)
-                    } else {
-                        signal!(e, MissingStructField);
-                    }
+        let c = try!(eval_const_expr_partial(tcx, base, base_hint, fn_args));
+        if let Struct(struct_id) = c {
+            if let hir::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node {
+                // Check that the given field exists and evaluate it
+                // if the idents are compared run-pass/issue-19244 fails
+                if let Some(f) = fields.iter().find(|f| f.name.node
+                                                     == field_name.node) {
+                    return eval_const_expr_partial(tcx, &*f.expr, base_hint, fn_args)
                 } else {
-                    unreachable!()
+                    signal!(e, MissingStructField);
                 }
             } else {
-                signal!(base, ExpectedConstStruct);
+                unreachable!()
             }
         } else {
-            signal!(base, NonConstPath);
+            signal!(base, ExpectedConstStruct);
         }
       }
       _ => signal!(e, MiscCatchAll)
@@ -1260,12 +1261,8 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
         Ok(None) => {
             return None
         }
-        Err(e) => {
-            tcx.sess.span_bug(ti.span,
-                              &format!("Encountered error `{:?}` when trying \
-                                        to select an implementation for \
-                                        constant trait item reference.",
-                                       e))
+        Err(_) => {
+            return None
         }
     };
 
@@ -1273,7 +1270,7 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
         traits::VtableImpl(ref impl_data) => {
             match tcx.associated_consts(impl_data.impl_def_id)
                      .iter().find(|ic| ic.name == ti.name) {
-                Some(ic) => lookup_const_by_id(tcx, ic.def_id, None),
+                Some(ic) => lookup_const_by_id(tcx, ic.def_id, None, None),
                 None => match ti.node {
                     hir::ConstTraitItem(_, Some(ref expr)) => Some(&*expr),
                     _ => None,
@@ -1440,6 +1437,6 @@ fn get_fn_def<'a>(tcx: &'a ty::ctxt,
             _ => signal!(e, NonConstPath),
         },
         Some(ast_map::NodeTraitItem(..)) => signal!(e, NonConstPath),
-        Some(_) => unimplemented!(),
+        Some(_) => signal!(e, UnimplementedConstVal("calling struct, tuple or variant")),
     }
 }
index fba6dea4448b204a3ea6a4b8089b0a22ee1001a6..380f543f969f06b4c2a77dde7c27233957032325 100644 (file)
@@ -28,6 +28,7 @@ use middle::def;
 use middle::lang_items;
 use middle::ty::{self, Ty};
 use middle::def_id::{DefId, DefIndex};
+use mir::repr::Mir;
 use session::Session;
 use session::search_paths::PathKind;
 use util::nodemap::{FnvHashMap, NodeMap, NodeSet};
@@ -100,6 +101,7 @@ pub enum InlinedItem {
 }
 
 /// A borrowed version of `hir::InlinedItem`.
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
 pub enum InlinedItemRef<'a> {
     Item(&'a hir::Item),
     TraitItem(DefId, &'a hir::TraitItem),
@@ -133,6 +135,7 @@ pub enum FoundAst<'ast> {
 pub trait CrateStore<'tcx> : Any {
     // item info
     fn stability(&self, def: DefId) -> Option<attr::Stability>;
+    fn deprecation(&self, def: DefId) -> Option<attr::Deprecation>;
     fn closure_kind(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId)
                     -> ty::ClosureKind;
     fn closure_ty(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId)
@@ -216,6 +219,8 @@ pub trait CrateStore<'tcx> : Any {
     // misc. metadata
     fn maybe_get_item_ast(&'tcx self, tcx: &ty::ctxt<'tcx>, def: DefId)
                           -> FoundAst<'tcx>;
+    fn maybe_get_item_mir(&self, tcx: &ty::ctxt<'tcx>, def: DefId)
+                          -> Option<Mir<'tcx>>;
     // This is basically a 1-based range of ints, which is a little
     // silly - I may fix that.
     fn crates(&self) -> Vec<ast::CrateNum>;
@@ -235,6 +240,7 @@ pub trait CrateStore<'tcx> : Any {
                        item_symbols: &RefCell<NodeMap<String>>,
                        link_meta: &LinkMeta,
                        reachable: &NodeSet,
+                       mir_map: &NodeMap<Mir<'tcx>>,
                        krate: &hir::Crate) -> Vec<u8>;
     fn metadata_encoding_version(&self) -> &[u8];
 }
@@ -287,6 +293,7 @@ pub struct DummyCrateStore;
 impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     // item info
     fn stability(&self, def: DefId) -> Option<attr::Stability> { unimplemented!() }
+    fn deprecation(&self, def: DefId) -> Option<attr::Deprecation> { unimplemented!() }
     fn closure_kind(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId)
                     -> ty::ClosureKind  { unimplemented!() }
     fn closure_ty(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId)
@@ -383,6 +390,9 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     // misc. metadata
     fn maybe_get_item_ast(&'tcx self, tcx: &ty::ctxt<'tcx>, def: DefId)
                           -> FoundAst<'tcx> { unimplemented!() }
+    fn maybe_get_item_mir(&self, tcx: &ty::ctxt<'tcx>, def: DefId)
+                          -> Option<Mir<'tcx>> { unimplemented!() }
+
     // This is basically a 1-based range of ints, which is a little
     // silly - I may fix that.
     fn crates(&self) -> Vec<ast::CrateNum> { vec![] }
@@ -404,6 +414,145 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
                        item_symbols: &RefCell<NodeMap<String>>,
                        link_meta: &LinkMeta,
                        reachable: &NodeSet,
+                       mir_map: &NodeMap<Mir<'tcx>>,
                        krate: &hir::Crate) -> Vec<u8> { vec![] }
     fn metadata_encoding_version(&self) -> &[u8] { unimplemented!() }
 }
+
+
+/// Metadata encoding and decoding can make use of thread-local encoding and
+/// decoding contexts. These allow implementers of serialize::Encodable and
+/// Decodable to access information and datastructures that would otherwise not
+/// be available to them. For example, we can automatically translate def-id and
+/// span information during decoding because the decoding context knows which
+/// crate the data is decoded from. Or it allows to make ty::Ty decodable
+/// because the context has access to the ty::ctxt that is needed for creating
+/// ty::Ty instances.
+///
+/// Note, however, that this only works for RBML-based encoding and decoding at
+/// the moment.
+pub mod tls {
+    use rbml::opaque::Encoder as OpaqueEncoder;
+    use rbml::opaque::Decoder as OpaqueDecoder;
+    use serialize;
+    use std::mem;
+    use middle::ty::{self, Ty};
+    use middle::subst::Substs;
+    use middle::def_id::DefId;
+
+    pub trait EncodingContext<'tcx> {
+        fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>;
+        fn encode_ty(&self, encoder: &mut OpaqueEncoder, t: Ty<'tcx>);
+        fn encode_substs(&self, encoder: &mut OpaqueEncoder, substs: &Substs<'tcx>);
+    }
+
+    /// Marker type used for the scoped TLS slot.
+    /// The type context cannot be used directly because the scoped TLS
+    /// in libstd doesn't allow types generic over lifetimes.
+    struct TlsPayload;
+
+    scoped_thread_local!(static TLS_ENCODING: TlsPayload);
+
+    /// Execute f after pushing the given EncodingContext onto the TLS stack.
+    pub fn enter_encoding_context<'tcx, F, R>(ecx: &EncodingContext<'tcx>,
+                                              encoder: &mut OpaqueEncoder,
+                                              f: F) -> R
+        where F: FnOnce(&EncodingContext<'tcx>, &mut OpaqueEncoder) -> R
+    {
+        let tls_payload = (ecx as *const _, encoder as *mut _);
+        let tls_ptr = &tls_payload as *const _ as *const TlsPayload;
+        TLS_ENCODING.set(unsafe { &*tls_ptr }, || f(ecx, encoder))
+    }
+
+    /// Execute f with access to the thread-local encoding context and
+    /// rbml encoder. This function will panic if the encoder passed in and the
+    /// context encoder are not the same.
+    ///
+    /// Note that this method is 'practically' safe due to its checking that the
+    /// encoder passed in is the same as the one in TLS, but it would still be
+    /// possible to construct cases where the EncodingContext is exchanged
+    /// while the same encoder is used, thus working with a wrong context.
+    pub fn with_encoding_context<'tcx, E, F, R>(encoder: &mut E, f: F) -> R
+        where F: FnOnce(&EncodingContext<'tcx>, &mut OpaqueEncoder) -> R,
+              E: serialize::Encoder
+    {
+        unsafe {
+            unsafe_with_encoding_context(|ecx, tls_encoder| {
+                assert!(encoder as *mut _ as usize == tls_encoder as *mut _ as usize);
+
+                let ecx: &EncodingContext<'tcx> = mem::transmute(ecx);
+
+                f(ecx, tls_encoder)
+            })
+        }
+    }
+
+    /// Execute f with access to the thread-local encoding context and
+    /// rbml encoder.
+    pub unsafe fn unsafe_with_encoding_context<F, R>(f: F) -> R
+        where F: FnOnce(&EncodingContext, &mut OpaqueEncoder) -> R
+    {
+        TLS_ENCODING.with(|tls| {
+            let tls_payload = (tls as *const TlsPayload)
+                                   as *mut (&EncodingContext, &mut OpaqueEncoder);
+            f((*tls_payload).0, (*tls_payload).1)
+        })
+    }
+
+    pub trait DecodingContext<'tcx> {
+        fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>;
+        fn decode_ty(&self, decoder: &mut OpaqueDecoder) -> ty::Ty<'tcx>;
+        fn decode_substs(&self, decoder: &mut OpaqueDecoder) -> Substs<'tcx>;
+        fn translate_def_id(&self, def_id: DefId) -> DefId;
+    }
+
+    scoped_thread_local!(static TLS_DECODING: TlsPayload);
+
+    /// Execute f after pushing the given DecodingContext onto the TLS stack.
+    pub fn enter_decoding_context<'tcx, F, R>(dcx: &DecodingContext<'tcx>,
+                                              decoder: &mut OpaqueDecoder,
+                                              f: F) -> R
+        where F: FnOnce(&DecodingContext<'tcx>, &mut OpaqueDecoder) -> R
+    {
+        let tls_payload = (dcx as *const _, decoder as *mut _);
+        let tls_ptr = &tls_payload as *const _ as *const TlsPayload;
+        TLS_DECODING.set(unsafe { &*tls_ptr }, || f(dcx, decoder))
+    }
+
+    /// Execute f with access to the thread-local decoding context and
+    /// rbml decoder. This function will panic if the decoder passed in and the
+    /// context decoder are not the same.
+    ///
+    /// Note that this method is 'practically' safe due to its checking that the
+    /// decoder passed in is the same as the one in TLS, but it would still be
+    /// possible to construct cases where the DecodingContext is exchanged
+    /// while the same decoder is used, thus working with a wrong context.
+    pub fn with_decoding_context<'decoder, 'tcx, D, F, R>(d: &'decoder mut D, f: F) -> R
+        where D: serialize::Decoder,
+              F: FnOnce(&DecodingContext<'tcx>,
+                        &mut OpaqueDecoder) -> R,
+              'tcx: 'decoder
+    {
+        unsafe {
+            unsafe_with_decoding_context(|dcx, decoder| {
+                assert!((d as *mut _ as usize) == (decoder as *mut _ as usize));
+
+                let dcx: &DecodingContext<'tcx> = mem::transmute(dcx);
+
+                f(dcx, decoder)
+            })
+        }
+    }
+
+    /// Execute f with access to the thread-local decoding context and
+    /// rbml decoder.
+    pub unsafe fn unsafe_with_decoding_context<F, R>(f: F) -> R
+        where F: FnOnce(&DecodingContext, &mut OpaqueDecoder) -> R
+    {
+        TLS_DECODING.with(|tls| {
+            let tls_payload = (tls as *const TlsPayload)
+                                   as *mut (&DecodingContext, &mut OpaqueDecoder);
+            f((*tls_payload).0, (*tls_payload).1)
+        })
+    }
+}
index 72013c533da51415f0159a801994f2391f256dc4..e9029958880bd73ced7005c7c81598f49bb6a6cf 100644 (file)
@@ -18,6 +18,7 @@ use middle::cfg;
 use middle::cfg::CFGIndex;
 use middle::ty;
 use std::io;
+use std::mem;
 use std::usize;
 use syntax::ast;
 use syntax::ast_util::IdRange;
@@ -229,7 +230,8 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
                oper: O,
                id_range: IdRange,
                bits_per_id: usize) -> DataFlowContext<'a, 'tcx, O> {
-        let words_per_id = (bits_per_id + usize::BITS - 1) / usize::BITS;
+        let usize_bits = mem::size_of::<usize>() * 8;
+        let words_per_id = (bits_per_id + usize_bits - 1) / usize_bits;
         let num_nodes = cfg.graph.all_nodes().len();
 
         debug!("DataFlowContext::new(analysis_name: {}, id_range={:?}, \
@@ -408,10 +410,11 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
         //! Returns false on the first call to `f` that returns false;
         //! if all calls to `f` return true, then returns true.
 
+        let usize_bits = mem::size_of::<usize>() * 8;
         for (word_index, &word) in words.iter().enumerate() {
             if word != 0 {
-                let base_index = word_index * usize::BITS;
-                for offset in 0..usize::BITS {
+                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
@@ -618,7 +621,7 @@ fn bits_to_string(words: &[usize]) -> String {
 
     for &word in words {
         let mut v = word;
-        for _ in 0..usize::BYTES {
+        for _ in 0..mem::size_of::<usize>() {
             result.push(sep);
             result.push_str(&format!("{:02x}", v & 0xFF));
             v >>= 8;
@@ -647,8 +650,9 @@ fn bitwise<Op:BitwiseOperator>(out_vec: &mut [usize],
 fn set_bit(words: &mut [usize], bit: usize) -> bool {
     debug!("set_bit: words={} bit={}",
            mut_bits_to_string(words), bit_str(bit));
-    let word = bit / usize::BITS;
-    let bit_in_word = bit % usize::BITS;
+    let usize_bits = mem::size_of::<usize>() * 8;
+    let word = bit / usize_bits;
+    let bit_in_word = bit % usize_bits;
     let bit_mask = 1 << bit_in_word;
     debug!("word={} bit_in_word={} bit_mask={}", word, bit_in_word, word);
     let oldv = words[word];
index ec1b447d7111b2d71678e82b368e1418ddd1fa37..1386ef91c70bf49e0ec7209f2252cbd9cb1bbcfa 100644 (file)
@@ -12,6 +12,7 @@
 // closely. The idea is that all reachable symbols are live, codes called
 // from live codes are live, and everything else is dead.
 
+use dep_graph::DepNode;
 use front::map as ast_map;
 use rustc_front::hir;
 use rustc_front::intravisit::{self, Visitor};
@@ -590,6 +591,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for DeadVisitor<'a, 'tcx> {
 }
 
 pub fn check_crate(tcx: &ty::ctxt, access_levels: &privacy::AccessLevels) {
+    let _task = tcx.dep_graph.in_task(DepNode::DeadCheck);
     let krate = tcx.map.krate();
     let live_symbols = find_live(tcx, access_levels, krate);
     let mut visitor = DeadVisitor { tcx: tcx, live_symbols: live_symbols };
index b1d24187957535040a75be1a5a394061954b16cb..9ef2828c947aad2469d7f766ebe799b7e5830aeb 100644 (file)
@@ -35,7 +35,6 @@ pub enum Def {
     DefTrait(DefId),
     DefPrimTy(hir::PrimTy),
     DefTyParam(ParamSpace, u32, DefId, ast::Name),
-    DefUse(DefId),
     DefUpvar(DefId,        // def id of closed over local
              ast::NodeId,  // node id of closed over local
              usize,        // index in the freevars list of the closure
@@ -52,6 +51,7 @@ pub enum Def {
     DefStruct(DefId),
     DefLabel(ast::NodeId),
     DefMethod(DefId),
+    DefErr,
 }
 
 /// The result of resolving a path.
@@ -122,9 +122,9 @@ impl Def {
 
             DefFn(..) | DefMod(..) | DefForeignMod(..) | DefStatic(..) |
             DefVariant(..) | DefTy(..) | DefAssociatedTy(..) |
-            DefTyParam(..) | DefUse(..) | DefStruct(..) | DefTrait(..) |
+            DefTyParam(..) | DefStruct(..) | DefTrait(..) |
             DefMethod(..) | DefConst(..) | DefAssociatedConst(..) |
-            DefPrimTy(..) | DefLabel(..) | DefSelfTy(..) => {
+            DefPrimTy(..) | DefLabel(..) | DefSelfTy(..) | DefErr => {
                 panic!("attempted .def_id() on invalid {:?}", self)
             }
         }
@@ -134,7 +134,7 @@ impl Def {
         match *self {
             DefFn(id, _) | DefMod(id) | DefForeignMod(id) | DefStatic(id, _) |
             DefVariant(_, id, _) | DefTy(id, _) | DefAssociatedTy(_, id) |
-            DefTyParam(_, _, id, _) | DefUse(id) | DefStruct(id) | DefTrait(id) |
+            DefTyParam(_, _, id, _) | DefStruct(id) | DefTrait(id) |
             DefMethod(id) | DefConst(id) | DefAssociatedConst(id) |
             DefLocal(id, _) | DefUpvar(id, _, _, _) => {
                 id
@@ -142,7 +142,8 @@ impl Def {
 
             DefLabel(..)  |
             DefPrimTy(..) |
-            DefSelfTy(..) => {
+            DefSelfTy(..) |
+            DefErr => {
                 panic!("attempted .def_id() on invalid def: {:?}", self)
             }
         }
index ab5153e1a61d4b86025656e4fb4a856034cd61af..aac6f1edc051da6fe085ebf06848cc85fd2581c0 100644 (file)
@@ -243,10 +243,11 @@ fn add_library(sess: &session::Session,
             // This error is probably a little obscure, but I imagine that it
             // can be refined over time.
             if link2 != link || link == RequireStatic {
-                sess.err(&format!("cannot satisfy dependencies so `{}` only \
-                                   shows up once", sess.cstore.crate_name(cnum)));
-                sess.help("having upstream crates all available in one format \
-                           will likely make this go away");
+                sess.struct_err(&format!("cannot satisfy dependencies so `{}` only \
+                                          shows up once", sess.cstore.crate_name(cnum)))
+                    .help("having upstream crates all available in one format \
+                           will likely make this go away")
+                    .emit();
             }
         }
         None => { m.insert(cnum, link); }
index ecf16aaed836a699c72457bb2781a120fce1e7d1..2d096f66e09f6798e5e9358410bc36322c4fbdbc 100644 (file)
@@ -146,17 +146,20 @@ fn configure_main(this: &mut EntryContext) {
         this.session.entry_type.set(Some(config::EntryMain));
     } else {
         // No main function
-        this.session.err("main function not found");
+        let mut err = this.session.struct_err("main function not found");
         if !this.non_main_fns.is_empty() {
             // There were some functions named 'main' though. Try to give the user a hint.
-            this.session.note("the main function must be defined at the crate level \
-                               but you have one or more functions named 'main' that are not \
-                               defined at the crate level. Either move the definition or \
-                               attach the `#[main]` attribute to override this behavior.");
+            err.note("the main function must be defined at the crate level \
+                      but you have one or more functions named 'main' that are not \
+                      defined at the crate level. Either move the definition or \
+                      attach the `#[main]` attribute to override this behavior.");
             for &(_, span) in &this.non_main_fns {
-                this.session.span_note(span, "here is a function named 'main'");
+                err.span_note(span, "here is a function named 'main'");
             }
+            err.emit();
             this.session.abort_if_errors();
+        } else {
+            err.emit();
         }
     }
 }
index 899bad982eba5359ec5cc9c63d0890565cd449bd..e746f3ac57914a712f61b39e935902a1219e72fe 100644 (file)
@@ -12,7 +12,6 @@
 //! normal visitor, which just walks the entire body in one shot, the
 //! `ExprUseVisitor` determines how expressions are being used.
 
-pub use self::MutateMode::*;
 pub use self::LoanCause::*;
 pub use self::ConsumeMode::*;
 pub use self::MoveReason::*;
@@ -242,8 +241,6 @@ impl OverloadedCallType {
 // mem_categorization, it requires a TYPER, which is a type that
 // supplies types from the tree. After type checking is complete, you
 // can just use the tcx as the typer.
-//
-// FIXME(stage0): the :'t here is probably only important for stage0
 pub struct ExprUseVisitor<'d, 't, 'a: 't, 'tcx:'a+'d> {
     typer: &'t infer::InferCtxt<'a, 'tcx>,
     mc: mc::MemCategorizationContext<'t, 'a, 'tcx>,
@@ -276,7 +273,7 @@ enum PassArgs {
 }
 
 impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
-    pub fn new(delegate: &'d mut (Delegate<'tcx>),
+    pub fn new(delegate: &'d mut (Delegate<'tcx>+'d),
                typer: &'t infer::InferCtxt<'a, 'tcx>)
                -> ExprUseVisitor<'d,'t,'a,'tcx> where 'tcx:'a+'d
     {
@@ -324,7 +321,7 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
         self.delegate.consume(consume_id, consume_span, cmt, mode);
     }
 
-    fn consume_exprs(&mut self, exprs: &Vec<P<hir::Expr>>) {
+    fn consume_exprs(&mut self, exprs: &[P<hir::Expr>]) {
         for expr in exprs {
             self.consume_expr(&**expr);
         }
@@ -373,6 +370,10 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
         match expr.node {
             hir::ExprPath(..) => { }
 
+            hir::ExprType(ref subexpr, _) => {
+                self.walk_expr(&**subexpr)
+            }
+
             hir::ExprUnary(hir::UnDeref, ref base) => {      // *base
                 if !self.walk_overloaded_operator(expr, &**base, Vec::new(), PassArgs::ByRef) {
                     self.select_from_expr(&**base);
@@ -458,9 +459,17 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
                     self.consume_expr(&**input);
                 }
 
-                for &(_, ref output, is_rw) in &ia.outputs {
-                    self.mutate_expr(expr, &**output,
-                                           if is_rw { WriteAndRead } else { JustWrite });
+                for output in &ia.outputs {
+                    if output.is_indirect {
+                        self.consume_expr(&*output.expr);
+                    } else {
+                        self.mutate_expr(expr, &*output.expr,
+                                         if output.is_rw {
+                                             MutateMode::WriteAndRead
+                                         } else {
+                                             MutateMode::JustWrite
+                                         });
+                    }
                 }
             }
 
@@ -513,7 +522,7 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
             }
 
             hir::ExprAssign(ref lhs, ref rhs) => {
-                self.mutate_expr(expr, &**lhs, JustWrite);
+                self.mutate_expr(expr, &**lhs, MutateMode::JustWrite);
                 self.consume_expr(&**rhs);
             }
 
@@ -526,7 +535,7 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
                 assert!(::rustc_front::util::is_by_value_binop(op.node));
 
                 if !self.walk_overloaded_operator(expr, lhs, vec![rhs], PassArgs::ByValue) {
-                    self.mutate_expr(expr, &**lhs, WriteAndRead);
+                    self.mutate_expr(expr, &**lhs, MutateMode::WriteAndRead);
                     self.consume_expr(&**rhs);
                 }
             }
@@ -647,7 +656,7 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
 
     fn walk_struct_expr(&mut self,
                         _expr: &hir::Expr,
-                        fields: &Vec<hir::Field>,
+                        fields: &[hir::Field],
                         opt_with: &Option<P<hir::Expr>>) {
         // Consume the expressions supplying values for each field.
         for field in fields {
@@ -693,7 +702,7 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
         self.walk_expr(with_expr);
 
         fn contains_field_named(field: ty::FieldDef,
-                                fields: &Vec<hir::Field>)
+                                fields: &[hir::Field])
                                 -> bool
         {
             fields.iter().any(
@@ -985,7 +994,7 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
                 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) => {
-                        delegate.mutate(pat.id, pat.span, binding_cmt, Init);
+                        delegate.mutate(pat.id, pat.span, binding_cmt, MutateMode::Init);
                     }
                     Err(_) => { }
                 }
index 9f33c4df03d41717ce77cd477f3c6fcc3ceac594..d25084bbdffb5e5794cc0840f0c4cf9d7b4750e9 100644 (file)
@@ -14,7 +14,7 @@ use middle::def_id::DefId;
 use middle::infer::{InferCtxt, GenericKind};
 use middle::subst::Substs;
 use middle::traits;
-use middle::ty::{self, RegionEscape, ToPredicate, Ty};
+use middle::ty::{self, ToPredicate, Ty};
 use middle::ty::fold::{TypeFoldable, TypeFolder};
 
 use syntax::ast;
@@ -404,7 +404,7 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> {
     }
 
     fn fully_normalize<T>(&self, value: &T) -> Result<T,ErrorReported>
-        where T : TypeFoldable<'tcx> + ty::HasTypeFlags
+        where T : TypeFoldable<'tcx>
     {
         let value =
             traits::fully_normalize(self.infcx,
index 03554a59655b162c4ef18e7819333a0e2cd7d7bf..faf1bdb0ce504b87358bfac3203a718bbdbc8baa 100644 (file)
@@ -41,7 +41,6 @@ use super::{InferCtxt};
 use super::{MiscVariable, TypeTrace};
 use super::type_variable::{RelationDir, BiTo, EqTo, SubtypeOf, SupertypeOf};
 
-use middle::ty::{TyVar};
 use middle::ty::{IntType, UintType};
 use middle::ty::{self, Ty};
 use middle::ty::error::TypeError;
@@ -319,7 +318,7 @@ impl<'cx, 'tcx> ty::fold::TypeFolder<'tcx> for Generalizer<'cx, 'tcx> {
                 }
             }
             _ => {
-                ty::fold::super_fold_ty(self, t)
+                t.super_fold_with(self)
             }
         }
     }
index 5f2a3bec423f5bf7dcd41751d47ab53a5f74bd9e..5cc848d2ca3d1516cb0ee15825db05abce6c0820 100644 (file)
@@ -82,7 +82,7 @@ use middle::def_id::DefId;
 use middle::infer::{self, TypeOrigin};
 use middle::region;
 use middle::subst;
-use middle::ty::{self, Ty, HasTypeFlags};
+use middle::ty::{self, Ty, TypeFoldable};
 use middle::ty::{Region, ReFree};
 use middle::ty::error::TypeError;
 
@@ -90,13 +90,14 @@ use std::cell::{Cell, RefCell};
 use std::char::from_u32;
 use std::fmt;
 use syntax::ast;
-use syntax::owned_slice::OwnedSlice;
+use syntax::errors::DiagnosticBuilder;
 use syntax::codemap::{self, Pos, Span};
 use syntax::parse::token;
 use syntax::ptr::P;
 
 impl<'tcx> ty::ctxt<'tcx> {
     pub fn note_and_explain_region(&self,
+                                   err: &mut DiagnosticBuilder,
                                    prefix: &str,
                                    region: ty::Region,
                                    suffix: &str) {
@@ -127,7 +128,10 @@ impl<'tcx> ty::ctxt<'tcx> {
                 };
                 let span = match scope.span(&self.region_maps, &self.map) {
                     Some(s) => s,
-                    None => return self.sess.note(&unknown_scope())
+                    None => {
+                        err.note(&unknown_scope());
+                        return;
+                    }
                 };
                 let tag = match self.map.find(scope.node_id(&self.region_maps)) {
                     Some(ast_map::NodeBlock(_)) => "block",
@@ -143,11 +147,15 @@ impl<'tcx> ty::ctxt<'tcx> {
                     Some(ast_map::NodeStmt(_)) => "statement",
                     Some(ast_map::NodeItem(it)) => item_scope_tag(&*it),
                     Some(_) | None => {
-                        return self.sess.span_note(span, &unknown_scope());
+                        err.span_note(span, &unknown_scope());
+                        return;
                     }
                 };
                 let scope_decorated_tag = match self.region_maps.code_extent_data(scope) {
                     region::CodeExtentData::Misc(_) => tag,
+                    region::CodeExtentData::CallSiteScope { .. } => {
+                        "scope of call-site for function"
+                    }
                     region::CodeExtentData::ParameterScope { .. } => {
                         "scope of parameters for function"
                     }
@@ -212,9 +220,9 @@ impl<'tcx> ty::ctxt<'tcx> {
         };
         let message = format!("{}{}{}", prefix, description, suffix);
         if let Some(span) = span {
-            self.sess.span_note(span, &message);
+            err.span_note(span, &message);
         } else {
-            self.sess.note(&message);
+            err.note(&message);
         }
     }
 }
@@ -226,9 +234,15 @@ pub trait ErrorReporting<'tcx> {
     fn process_errors(&self, errors: &Vec<RegionResolutionError<'tcx>>)
                       -> Vec<RegionResolutionError<'tcx>>;
 
-    fn report_type_error(&self, trace: TypeTrace<'tcx>, terr: &TypeError<'tcx>);
+    fn report_type_error(&self,
+                         trace: TypeTrace<'tcx>,
+                         terr: &TypeError<'tcx>)
+                         -> DiagnosticBuilder<'tcx>;
 
-    fn check_and_note_conflicting_crates(&self, terr: &TypeError<'tcx>, sp: Span);
+    fn check_and_note_conflicting_crates(&self,
+                                         err: &mut DiagnosticBuilder,
+                                         terr: &TypeError<'tcx>,
+                                         sp: Span);
 
     fn report_and_explain_type_error(&self,
                                      trace: TypeTrace<'tcx>,
@@ -236,7 +250,7 @@ pub trait ErrorReporting<'tcx> {
 
     fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<String>;
 
-    fn expected_found_str<T: fmt::Display + Resolvable<'tcx> + HasTypeFlags>(
+    fn expected_found_str<T: fmt::Display + Resolvable<'tcx> + TypeFoldable<'tcx>>(
         &self,
         exp_found: &ty::error::ExpectedFound<T>)
         -> Option<String>;
@@ -263,17 +277,20 @@ pub trait ErrorReporting<'tcx> {
                                trace_origin: &[(TypeTrace<'tcx>, TypeError<'tcx>)],
                                same_regions: &[SameRegions]);
 
-    fn give_suggestion(&self, same_regions: &[SameRegions]);
+    fn give_suggestion(&self, err: &mut DiagnosticBuilder, same_regions: &[SameRegions]);
 }
 
 trait ErrorReportingHelpers<'tcx> {
     fn report_inference_failure(&self,
-                                var_origin: RegionVariableOrigin);
+                                var_origin: RegionVariableOrigin)
+                                -> DiagnosticBuilder<'tcx>;
 
     fn note_region_origin(&self,
+                          err: &mut DiagnosticBuilder,
                           origin: &SubregionOrigin<'tcx>);
 
     fn give_expl_lifetime_param(&self,
+                                err: &mut DiagnosticBuilder,
                                 decl: &hir::FnDecl,
                                 unsafety: hir::Unsafety,
                                 constness: hir::Constness,
@@ -458,35 +475,58 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
         }
     }
 
-    fn report_type_error(&self, trace: TypeTrace<'tcx>, terr: &TypeError<'tcx>) {
+    fn report_type_error(&self,
+                         trace: TypeTrace<'tcx>,
+                         terr: &TypeError<'tcx>)
+                         -> DiagnosticBuilder<'tcx> {
         let expected_found_str = match self.values_str(&trace.values) {
             Some(v) => v,
             None => {
-                return; /* derived error */
+                return self.tcx.sess.diagnostic().struct_dummy(); /* derived error */
             }
         };
 
-        span_err!(self.tcx.sess, trace.origin.span(), E0308,
-            "{}: {} ({})",
-                 trace.origin,
-                 expected_found_str,
-                 terr);
+        let is_simple_error = if let &TypeError::Sorts(ref values) = terr {
+            values.expected.is_primitive() && values.found.is_primitive()
+        } else {
+            false
+        };
+
+        let expected_found_str = if is_simple_error {
+            expected_found_str
+        } else {
+            format!("{} ({})", expected_found_str, terr)
+        };
+
+        let mut err = struct_span_err!(self.tcx.sess,
+                                       trace.origin.span(),
+                                       E0308,
+                                       "{}: {}",
+                                       trace.origin,
+                                       expected_found_str);
 
-        self.check_and_note_conflicting_crates(terr, trace.origin.span());
+        self.check_and_note_conflicting_crates(&mut err, terr, trace.origin.span());
 
         match trace.origin {
             TypeOrigin::MatchExpressionArm(_, arm_span, source) => match source {
-                hir::MatchSource::IfLetDesugar{..} =>
-                    self.tcx.sess.span_note(arm_span, "`if let` arm with an incompatible type"),
-                _ => self.tcx.sess.span_note(arm_span, "match arm with an incompatible type"),
+                hir::MatchSource::IfLetDesugar{..} => {
+                    err.span_note(arm_span, "`if let` arm with an incompatible type");
+                }
+                _ => {
+                    err.span_note(arm_span, "match arm with an incompatible type");
+                }
             },
             _ => ()
         }
+        err
     }
 
     /// Adds a note if the types come from similarly named crates
-    fn check_and_note_conflicting_crates(&self, terr: &TypeError<'tcx>, sp: Span) {
-        let report_path_match = |did1: DefId, did2: DefId| {
+    fn check_and_note_conflicting_crates(&self,
+                                         err: &mut DiagnosticBuilder,
+                                         terr: &TypeError<'tcx>,
+                                         sp: Span) {
+        let report_path_match = |err: &mut DiagnosticBuilder, did1: DefId, did2: DefId| {
             // Only external crates, if either is from a local
             // module we could have false positives
             if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate {
@@ -500,9 +540,9 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                 // for imported and non-imported crates
                 if exp_path == found_path {
                     let crate_name = self.tcx.sess.cstore.crate_name(did1.krate);
-                    self.tcx.sess.span_note(sp, &format!("Perhaps two different versions \
-                                                          of crate `{}` are being used?",
-                                                          crate_name));
+                    err.span_note(sp, &format!("Perhaps two different versions \
+                                                of crate `{}` are being used?",
+                                               crate_name));
                 }
             }
         };
@@ -515,14 +555,13 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     (&ty::TyStruct(ref exp_adt, _), &ty::TyStruct(ref found_adt, _)) |
                     (&ty::TyEnum(ref exp_adt, _), &ty::TyStruct(ref found_adt, _)) |
                     (&ty::TyStruct(ref exp_adt, _), &ty::TyEnum(ref found_adt, _)) => {
-                        report_path_match(exp_adt.did, found_adt.did);
+                        report_path_match(err, exp_adt.did, found_adt.did);
                     },
                     _ => ()
                 }
             },
             TypeError::Traits(ref exp_found) => {
-                self.tcx.sess.note("errrr0");
-                report_path_match(exp_found.expected, exp_found.found);
+                report_path_match(err, exp_found.expected, exp_found.found);
             },
             _ => () // FIXME(#22750) handle traits and stuff
         }
@@ -532,8 +571,9 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                                      trace: TypeTrace<'tcx>,
                                      terr: &TypeError<'tcx>) {
         let span = trace.origin.span();
-        self.report_type_error(trace, terr);
-        self.tcx.note_and_explain_type_err(terr, span);
+        let mut err = self.report_type_error(trace, terr);
+        self.tcx.note_and_explain_type_err(&mut err, terr, span);
+        err.emit();
     }
 
     /// Returns a string of the form "expected `{}`, found `{}`", or None if this is a derived
@@ -546,7 +586,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
         }
     }
 
-    fn expected_found_str<T: fmt::Display + Resolvable<'tcx> + HasTypeFlags>(
+    fn expected_found_str<T: fmt::Display + Resolvable<'tcx> + TypeFoldable<'tcx>>(
         &self,
         exp_found: &ty::error::ExpectedFound<T>)
         -> Option<String>
@@ -576,11 +616,6 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
         // where the error was detected. But that span is not readily
         // accessible.
 
-        let is_warning = match origin {
-            infer::RFC1214Subregion(_) => true,
-            _ => false,
-        };
-
         let labeled_user_string = match bound_kind {
             GenericKind::Param(ref p) =>
                 format!("the parameter type `{}`", p),
@@ -588,55 +623,56 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                 format!("the associated type `{}`", p),
         };
 
-        match sub {
+        let mut err = match sub {
             ty::ReFree(ty::FreeRegion {bound_region: ty::BrNamed(..), ..}) => {
                 // Does the required lifetime have a nice name we can print?
-                span_err_or_warn!(
-                    is_warning, self.tcx.sess, origin.span(), E0309,
-                    "{} may not live long enough", labeled_user_string);
-                self.tcx.sess.fileline_help(
-                    origin.span(),
-                    &format!(
-                        "consider adding an explicit lifetime bound `{}: {}`...",
-                        bound_kind,
-                        sub));
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               origin.span(),
+                                               E0309,
+                                               "{} may not live long enough",
+                                               labeled_user_string);
+                err.fileline_help(origin.span(),
+                                  &format!("consider adding an explicit lifetime bound `{}: {}`...",
+                                           bound_kind,
+                                           sub));
+                err
             }
 
             ty::ReStatic => {
                 // Does the required lifetime have a nice name we can print?
-                span_err_or_warn!(
-                    is_warning, self.tcx.sess, origin.span(), E0310,
-                    "{} may not live long enough", labeled_user_string);
-                self.tcx.sess.fileline_help(
-                    origin.span(),
-                    &format!(
-                        "consider adding an explicit lifetime bound `{}: 'static`...",
-                        bound_kind));
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               origin.span(),
+                                               E0310,
+                                               "{} may not live long enough",
+                                               labeled_user_string);
+                err.fileline_help(origin.span(),
+                                  &format!("consider adding an explicit lifetime \
+                                            bound `{}: 'static`...",
+                                           bound_kind));
+                err
             }
 
             _ => {
                 // If not, be less specific.
-                span_err_or_warn!(
-                    is_warning, self.tcx.sess, origin.span(), E0311,
-                    "{} may not live long enough",
-                    labeled_user_string);
-                self.tcx.sess.fileline_help(
-                    origin.span(),
-                    &format!(
-                        "consider adding an explicit lifetime bound for `{}`",
-                        bound_kind));
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               origin.span(),
+                                               E0311,
+                                               "{} may not live long enough",
+                                               labeled_user_string);
+                err.fileline_help(origin.span(),
+                                  &format!("consider adding an explicit lifetime bound for `{}`",
+                                           bound_kind));
                 self.tcx.note_and_explain_region(
+                    &mut err,
                     &format!("{} must be valid for ", labeled_user_string),
                     sub,
                     "...");
+                err
             }
-        }
-
-        if is_warning {
-            self.tcx.sess.note_rfc_1214(origin.span());
-        }
+        };
 
-        self.note_region_origin(&origin);
+        self.note_region_origin(&mut err, &origin);
+        err.emit();
     }
 
     fn report_concrete_failure(&self,
@@ -644,251 +680,267 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                                sub: Region,
                                sup: Region) {
         match origin {
-            infer::RFC1214Subregion(ref suborigin) => {
-                // Ideally, this would be a warning, but it doesn't
-                // seem to come up in practice, since the changes from
-                // RFC1214 mostly trigger errors in type definitions
-                // that don't wind up coming down this path.
-                self.report_concrete_failure((**suborigin).clone(), sub, sup);
-            }
             infer::Subtype(trace) => {
                 let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
                 self.report_and_explain_type_error(trace, &terr);
             }
             infer::Reborrow(span) => {
-                span_err!(self.tcx.sess, span, E0312,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0312,
                     "lifetime of reference outlines \
                      lifetime of borrowed content...");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "...the reference is valid for ",
                     sub,
                     "...");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "...but the borrowed content is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::ReborrowUpvar(span, ref upvar_id) => {
-                span_err!(self.tcx.sess, span, E0313,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0313,
                     "lifetime of borrowed pointer outlives \
                             lifetime of captured variable `{}`...",
                             self.tcx.local_var_name_str(upvar_id.var_id));
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "...the borrowed pointer is valid for ",
                     sub,
                     "...");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     &format!("...but `{}` is only valid for ",
                              self.tcx.local_var_name_str(upvar_id.var_id)),
                     sup,
                     "");
+                err.emit();
             }
             infer::InfStackClosure(span) => {
-                span_err!(self.tcx.sess, span, E0314,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0314,
                     "closure outlives stack frame");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "...the closure must be valid for ",
                     sub,
                     "...");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "...but the closure's stack frame is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::InvokeClosure(span) => {
-                span_err!(self.tcx.sess, span, E0315,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0315,
                     "cannot invoke closure outside of its lifetime");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the closure is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::DerefPointer(span) => {
-                span_err!(self.tcx.sess, span, E0473,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0473,
                           "dereference of reference outside its lifetime");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the reference is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::FreeVariable(span, id) => {
-                span_err!(self.tcx.sess, span, E0474,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0474,
                           "captured variable `{}` does not outlive the enclosing closure",
                           self.tcx.local_var_name_str(id));
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "captured variable is valid for ",
                     sup,
                     "");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "closure is valid for ",
                     sub,
                     "");
+                err.emit();
             }
             infer::IndexSlice(span) => {
-                span_err!(self.tcx.sess, span, E0475,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0475,
                           "index of slice outside its lifetime");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the slice is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::RelateObjectBound(span) => {
-                span_err!(self.tcx.sess, span, E0476,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0476,
                           "lifetime of the source pointer does not outlive \
                            lifetime bound of the object type");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "object type is valid for ",
                     sub,
                     "");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "source pointer is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::RelateParamBound(span, ty) => {
-                span_err!(self.tcx.sess, span, E0477,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0477,
                           "the type `{}` does not fulfill the required lifetime",
                           self.ty_to_string(ty));
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                                         "type must outlive ",
                                         sub,
                                         "");
+                err.emit();
             }
             infer::RelateRegionParamBound(span) => {
-                span_err!(self.tcx.sess, span, E0478,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0478,
                           "lifetime bound not satisfied");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "lifetime parameter instantiated with ",
                     sup,
                     "");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "but lifetime parameter must outlive ",
                     sub,
                     "");
+                err.emit();
             }
             infer::RelateDefaultParamBound(span, ty) => {
-                span_err!(self.tcx.sess, span, E0479,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0479,
                           "the type `{}` (provided as the value of \
                            a type parameter) is not valid at this point",
                           self.ty_to_string(ty));
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                                         "type must outlive ",
                                         sub,
                                         "");
+                err.emit();
             }
             infer::CallRcvr(span) => {
-                span_err!(self.tcx.sess, span, E0480,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0480,
                           "lifetime of method receiver does not outlive \
                            the method call");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the receiver is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::CallArg(span) => {
-                span_err!(self.tcx.sess, span, E0481,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0481,
                           "lifetime of function argument does not outlive \
                            the function call");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the function argument is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::CallReturn(span) => {
-                span_err!(self.tcx.sess, span, E0482,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0482,
                           "lifetime of return value does not outlive \
                            the function call");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the return value is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::Operand(span) => {
-                span_err!(self.tcx.sess, span, E0483,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0483,
                           "lifetime of operand does not outlive \
                            the operation");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the operand is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::AddrOf(span) => {
-                span_err!(self.tcx.sess, span, E0484,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0484,
                           "reference is not valid at the time of borrow");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the borrow is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::AutoBorrow(span) => {
-                span_err!(self.tcx.sess, span, E0485,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0485,
                           "automatically reference is not valid \
                            at the time of borrow");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the automatic borrow is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::ExprTypeIsNotInScope(t, span) => {
-                span_err!(self.tcx.sess, span, E0486,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0486,
                           "type of expression contains references \
                            that are not valid during the expression: `{}`",
                           self.ty_to_string(t));
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "type is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::SafeDestructor(span) => {
-                span_err!(self.tcx.sess, span, E0487,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0487,
                           "unsafe use of destructor: destructor might be called \
                            while references are dead");
                 // FIXME (22171): terms "super/subregion" are suboptimal
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "superregion: ",
                     sup,
                     "");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "subregion: ",
                     sub,
                     "");
+                err.emit();
             }
             infer::BindingTypeIsNotValidAtDecl(span) => {
-                span_err!(self.tcx.sess, span, E0488,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0488,
                           "lifetime of variable does not enclose its declaration");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the variable is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::ParameterInScope(_, span) => {
-                span_err!(self.tcx.sess, span, E0489,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0489,
                           "type/lifetime parameter not in scope here");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the parameter is only valid for ",
                     sub,
                     "");
+                err.emit();
             }
             infer::DataBorrowed(ty, span) => {
-                span_err!(self.tcx.sess, span, E0490,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0490,
                           "a value of type `{}` is borrowed for too long",
                           self.ty_to_string(ty));
-                self.tcx.note_and_explain_region("the type is valid for ", sub, "");
-                self.tcx.note_and_explain_region("but the borrow lasts for ", sup, "");
+                self.tcx.note_and_explain_region(&mut err, "the type is valid for ", sub, "");
+                self.tcx.note_and_explain_region(&mut err, "but the borrow lasts for ", sup, "");
+                err.emit();
             }
             infer::ReferenceOutlivesReferent(ty, span) => {
-                span_err!(self.tcx.sess, span, E0491,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0491,
                           "in type `{}`, reference has a longer lifetime \
                            than the data it references",
                           self.ty_to_string(ty));
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the pointer is valid for ",
                     sub,
                     "");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "but the referenced data is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
         }
     }
@@ -899,37 +951,42 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                                sub_region: Region,
                                sup_origin: SubregionOrigin<'tcx>,
                                sup_region: Region) {
-        self.report_inference_failure(var_origin);
+        let mut err = self.report_inference_failure(var_origin);
 
-        self.tcx.note_and_explain_region(
+        self.tcx.note_and_explain_region(&mut err,
             "first, the lifetime cannot outlive ",
             sup_region,
             "...");
 
-        self.note_region_origin(&sup_origin);
+        self.note_region_origin(&mut err, &sup_origin);
 
-        self.tcx.note_and_explain_region(
+        self.tcx.note_and_explain_region(&mut err,
             "but, the lifetime must be valid for ",
             sub_region,
             "...");
 
-        self.note_region_origin(&sub_origin);
+        self.note_region_origin(&mut err, &sub_origin);
+        err.emit();
     }
 
     fn report_processed_errors(&self,
                                var_origins: &[RegionVariableOrigin],
                                trace_origins: &[(TypeTrace<'tcx>, TypeError<'tcx>)],
                                same_regions: &[SameRegions]) {
-        for vo in var_origins {
-            self.report_inference_failure(vo.clone());
+        for (i, vo) in var_origins.iter().enumerate() {
+            let mut err = self.report_inference_failure(vo.clone());
+            if i == var_origins.len() - 1 {
+                self.give_suggestion(&mut err, same_regions);
+            }
+            err.emit();
         }
-        self.give_suggestion(same_regions);
+
         for &(ref trace, ref terr) in trace_origins {
             self.report_and_explain_type_error(trace.clone(), terr);
         }
     }
 
-    fn give_suggestion(&self, same_regions: &[SameRegions]) {
+    fn give_suggestion(&self, err: &mut DiagnosticBuilder, same_regions: &[SameRegions]) {
         let scope_id = same_regions[0].scope_id;
         let parent = self.tcx.map.get_parent(scope_id);
         let parent_node = self.tcx.map.find(parent);
@@ -983,7 +1040,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
         let rebuilder = Rebuilder::new(self.tcx, fn_decl, expl_self,
                                        generics, same_regions, &life_giver);
         let (fn_decl, expl_self, generics) = rebuilder.rebuild();
-        self.give_expl_lifetime_param(&fn_decl, unsafety, constness, name,
+        self.give_expl_lifetime_param(err, &fn_decl, unsafety, constness, name,
                                       expl_self.as_ref(), &generics, span);
     }
 }
@@ -1152,11 +1209,11 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
     }
 
     fn rebuild_ty_params(&self,
-                         ty_params: OwnedSlice<hir::TyParam>,
+                         ty_params: hir::HirVec<hir::TyParam>,
                          lifetime: hir::Lifetime,
                          region_names: &HashSet<ast::Name>)
-                         -> OwnedSlice<hir::TyParam> {
-        ty_params.map(|ty_param| {
+                         -> hir::HirVec<hir::TyParam> {
+        ty_params.iter().map(|ty_param| {
             let bounds = self.rebuild_ty_param_bounds(ty_param.bounds.clone(),
                                                       lifetime,
                                                       region_names);
@@ -1167,15 +1224,15 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
                 default: ty_param.default.clone(),
                 span: ty_param.span,
             }
-        })
+        }).collect()
     }
 
     fn rebuild_ty_param_bounds(&self,
-                               ty_param_bounds: OwnedSlice<hir::TyParamBound>,
+                               ty_param_bounds: hir::TyParamBounds,
                                lifetime: hir::Lifetime,
                                region_names: &HashSet<ast::Name>)
-                               -> OwnedSlice<hir::TyParamBound> {
-        ty_param_bounds.map(|tpb| {
+                               -> hir::TyParamBounds {
+        ty_param_bounds.iter().map(|tpb| {
             match tpb {
                 &hir::RegionTyParamBound(lt) => {
                     // FIXME -- it's unclear whether I'm supposed to
@@ -1211,7 +1268,7 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
                     }, modifier)
                 }
             }
-        })
+        }).collect()
     }
 
     fn rebuild_expl_self(&self,
@@ -1247,13 +1304,13 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
                         add: &Vec<hir::Lifetime>,
                         keep: &HashSet<ast::Name>,
                         remove: &HashSet<ast::Name>,
-                        ty_params: OwnedSlice<hir::TyParam>,
+                        ty_params: hir::HirVec<hir::TyParam>,
                         where_clause: hir::WhereClause)
                         -> hir::Generics {
         let mut lifetimes = Vec::new();
         for lt in add {
             lifetimes.push(hir::LifetimeDef { lifetime: *lt,
-                                              bounds: Vec::new() });
+                                              bounds: hir::HirVec::new() });
         }
         for lt in &generics.lifetimes {
             if keep.contains(&lt.lifetime.name) ||
@@ -1262,7 +1319,7 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
             }
         }
         hir::Generics {
-            lifetimes: lifetimes,
+            lifetimes: lifetimes.into(),
             ty_params: ty_params,
             where_clause: where_clause,
         }
@@ -1273,7 +1330,7 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
                        lifetime: hir::Lifetime,
                        anon_nums: &HashSet<u32>,
                        region_names: &HashSet<ast::Name>)
-                       -> Vec<hir::Arg> {
+                       -> hir::HirVec<hir::Arg> {
         let mut new_inputs = Vec::new();
         for arg in inputs {
             let new_ty = self.rebuild_arg_ty_or_output(&*arg.ty, lifetime,
@@ -1285,7 +1342,7 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
             };
             new_inputs.push(possibly_new_arg);
         }
-        new_inputs
+        new_inputs.into()
     }
 
     fn rebuild_output(&self, ty: &hir::FunctionRetTy,
@@ -1497,10 +1554,10 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
                         }
                     }
                 }
-                let new_types = data.types.map(|t| {
+                let new_types = data.types.iter().map(|t| {
                     self.rebuild_arg_ty_or_output(&**t, lifetime, anon_nums, region_names)
-                });
-                let new_bindings = data.bindings.map(|b| {
+                }).collect();
+                let new_bindings = data.bindings.iter().map(|b| {
                     hir::TypeBinding {
                         id: b.id,
                         name: b.name,
@@ -1510,9 +1567,9 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
                                                           region_names),
                         span: b.span
                     }
-                });
+                }).collect();
                 hir::AngleBracketedParameters(hir::AngleBracketedParameterData {
-                    lifetimes: new_lts,
+                    lifetimes: new_lts.into(),
                     types: new_types,
                     bindings: new_bindings,
                })
@@ -1528,13 +1585,14 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
         hir::Path {
             span: path.span,
             global: path.global,
-            segments: new_segs
+            segments: new_segs.into()
         }
     }
 }
 
 impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
     fn give_expl_lifetime_param(&self,
+                                err: &mut DiagnosticBuilder,
                                 decl: &hir::FnDecl,
                                 unsafety: hir::Unsafety,
                                 constness: hir::Constness,
@@ -1546,11 +1604,12 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
                                                  opt_explicit_self, generics);
         let msg = format!("consider using an explicit lifetime \
                            parameter as shown: {}", suggested_fn);
-        self.tcx.sess.span_help(span, &msg[..]);
+        err.span_help(span, &msg[..]);
     }
 
     fn report_inference_failure(&self,
-                                var_origin: RegionVariableOrigin) {
+                                var_origin: RegionVariableOrigin)
+                                -> DiagnosticBuilder<'tcx> {
         let br_string = |br: ty::BoundRegion| {
             let mut s = br.to_string();
             if !s.is_empty() {
@@ -1589,17 +1648,14 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
             }
         };
 
-        span_err!(self.tcx.sess, var_origin.span(), E0495,
+        struct_span_err!(self.tcx.sess, var_origin.span(), E0495,
                   "cannot infer an appropriate lifetime{} \
                    due to conflicting requirements",
-                  var_description);
+                  var_description)
     }
 
-    fn note_region_origin(&self, origin: &SubregionOrigin<'tcx>) {
+    fn note_region_origin(&self, err: &mut DiagnosticBuilder, origin: &SubregionOrigin<'tcx>) {
         match *origin {
-            infer::RFC1214Subregion(ref suborigin) => {
-                self.note_region_origin(suborigin);
-            }
             infer::Subtype(ref trace) => {
                 let desc = match trace.origin {
                     TypeOrigin::Misc(_) => {
@@ -1640,7 +1696,7 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
 
                 match self.values_str(&trace.values) {
                     Some(values_str) => {
-                        self.tcx.sess.span_note(
+                        err.span_note(
                             trace.origin.span(),
                             &format!("...so that {} ({})",
                                     desc, values_str));
@@ -1650,130 +1706,130 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
                         // all, since it is derived, but that would
                         // require more refactoring than I feel like
                         // doing right now. - nmatsakis
-                        self.tcx.sess.span_note(
+                        err.span_note(
                             trace.origin.span(),
                             &format!("...so that {}", desc));
                     }
                 }
             }
             infer::Reborrow(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that reference does not outlive \
                     borrowed content");
             }
             infer::ReborrowUpvar(span, ref upvar_id) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!(
                         "...so that closure can access `{}`",
                         self.tcx.local_var_name_str(upvar_id.var_id)
-                            .to_string()))
+                            .to_string()));
             }
             infer::InfStackClosure(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that closure does not outlive its stack frame");
             }
             infer::InvokeClosure(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that closure is not invoked outside its lifetime");
             }
             infer::DerefPointer(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that pointer is not dereferenced \
                     outside its lifetime");
             }
             infer::FreeVariable(span, id) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!("...so that captured variable `{}` \
                             does not outlive the enclosing closure",
                             self.tcx.local_var_name_str(id)));
             }
             infer::IndexSlice(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that slice is not indexed outside the lifetime");
             }
             infer::RelateObjectBound(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that it can be closed over into an object");
             }
             infer::CallRcvr(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that method receiver is valid for the method call");
             }
             infer::CallArg(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that argument is valid for the call");
             }
             infer::CallReturn(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that return value is valid for the call");
             }
             infer::Operand(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that operand is valid for operation");
             }
             infer::AddrOf(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that reference is valid \
                      at the time of borrow");
             }
             infer::AutoBorrow(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that auto-reference is valid \
                      at the time of borrow");
             }
             infer::ExprTypeIsNotInScope(t, span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!("...so type `{}` of expression is valid during the \
                              expression",
                             self.ty_to_string(t)));
             }
             infer::BindingTypeIsNotValidAtDecl(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that variable is valid at time of its declaration");
             }
             infer::ParameterInScope(_, span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that a type/lifetime parameter is in scope here");
             }
             infer::DataBorrowed(ty, span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!("...so that the type `{}` is not borrowed for too long",
                              self.ty_to_string(ty)));
             }
             infer::ReferenceOutlivesReferent(ty, span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!("...so that the reference type `{}` \
                              does not outlive the data it points at",
                             self.ty_to_string(ty)));
             }
             infer::RelateParamBound(span, t) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!("...so that the type `{}` \
                              will meet its required lifetime bounds",
                             self.ty_to_string(t)));
             }
             infer::RelateDefaultParamBound(span, t) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!("...so that type parameter \
                              instantiated with `{}`, \
@@ -1781,16 +1837,16 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
                             self.ty_to_string(t)));
             }
             infer::RelateRegionParamBound(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that the declared lifetime parameter bounds \
                                 are satisfied");
             }
             infer::SafeDestructor(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that references are valid when the destructor \
-                     runs")
+                     runs");
             }
         }
     }
index 0bae3cd2d86903dac5912b503c914c83293a2d67..76dd62383f1b1f1c65c1de47961185094967b2ec 100644 (file)
@@ -30,8 +30,7 @@
 //! variable only once, and it does so as soon as it can, so it is reasonable to ask what the type
 //! inferencer knows "so far".
 
-use middle::ty::{self, Ty, HasTypeFlags};
-use middle::ty::fold::TypeFoldable;
+use middle::ty::{self, Ty, TypeFoldable};
 use middle::ty::fold::TypeFolder;
 use std::collections::hash_map::{self, Entry};
 
@@ -169,7 +168,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
             ty::TyTuple(..) |
             ty::TyProjection(..) |
             ty::TyParam(..) => {
-                ty::fold::super_fold_ty(self, t)
+                t.super_fold_with(self)
             }
         }
     }
index ef6d9ae41914b00e84755e1450300b09eb486d42..e8f542db933cbf0c309aac04961aa8641cfd36ec 100644 (file)
@@ -14,9 +14,8 @@
 use super::{CombinedSnapshot, InferCtxt, HigherRankedType, SkolemizationMap};
 use super::combine::CombineFields;
 
-use middle::ty::{self, Binder};
+use middle::ty::{self, Binder, TypeFoldable};
 use middle::ty::error::TypeError;
-use middle::ty::fold::TypeFoldable;
 use middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use syntax::codemap::Span;
 use util::nodemap::{FnvHashMap, FnvHashSet};
@@ -557,7 +556,7 @@ pub fn plug_leaks<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
                              snapshot: &CombinedSnapshot,
                              value: &T)
                              -> T
-    where T : TypeFoldable<'tcx> + ty::HasTypeFlags
+    where T : TypeFoldable<'tcx>
 {
     debug_assert!(leak_check(infcx, &skol_map, snapshot).is_ok());
 
index b39ddfe95c83ab0c7c74629bbf6cad28d78b0038..922d4c251bb64a08402b9df9d026826205a72419 100644 (file)
@@ -27,21 +27,20 @@ use middle::region::CodeExtent;
 use middle::subst;
 use middle::subst::Substs;
 use middle::subst::Subst;
-use middle::traits::{self, FulfillmentContext, Normalized,
-                     SelectionContext, ObligationCause};
+use middle::traits;
 use middle::ty::adjustment;
-use middle::ty::{TyVid, IntVid, FloatVid, RegionVid};
-use middle::ty::{self, Ty, HasTypeFlags};
+use middle::ty::{TyVid, IntVid, FloatVid};
+use middle::ty::{self, Ty};
 use middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric};
 use middle::ty::fold::{TypeFolder, TypeFoldable};
 use middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use rustc_data_structures::unify::{self, UnificationTable};
 use std::cell::{RefCell, Ref};
 use std::fmt;
-use std::rc::Rc;
 use syntax::ast;
 use syntax::codemap;
 use syntax::codemap::{Span, DUMMY_SP};
+use syntax::errors::DiagnosticBuilder;
 use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap};
 
 use self::combine::CombineFields;
@@ -199,11 +198,6 @@ pub struct TypeTrace<'tcx> {
 /// See `error_reporting.rs` for more details
 #[derive(Clone, Debug)]
 pub enum SubregionOrigin<'tcx> {
-    // Marker to indicate a constraint that only arises due to new
-    // provisions from RFC 1214. This will result in a warning, not an
-    // error.
-    RFC1214Subregion(Rc<SubregionOrigin<'tcx>>),
-
     // Arose from a subtyping relation
     Subtype(TypeTrace<'tcx>),
 
@@ -518,7 +512,7 @@ pub struct CombinedSnapshot {
 }
 
 pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> T
-    where T : TypeFoldable<'tcx> + HasTypeFlags
+    where T : TypeFoldable<'tcx>
 {
     debug!("normalize_associated_type(t={:?})", value);
 
@@ -552,7 +546,7 @@ pub fn drain_fulfillment_cx_or_panic<'a,'tcx,T>(span: Span,
                                                 fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
                                                 result: &T)
                                                 -> T
-    where T : TypeFoldable<'tcx> + HasTypeFlags
+    where T : TypeFoldable<'tcx>
 {
     match drain_fulfillment_cx(infcx, fulfill_cx, result) {
         Ok(v) => v,
@@ -576,7 +570,7 @@ pub fn drain_fulfillment_cx<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
                                        fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
                                        result: &T)
                                        -> Result<T,Vec<traits::FulfillmentError<'tcx>>>
-    where T : TypeFoldable<'tcx> + HasTypeFlags
+    where T : TypeFoldable<'tcx>
 {
     debug!("drain_fulfillment_cx(result={:?})",
            result);
@@ -935,7 +929,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                          snapshot: &CombinedSnapshot,
                          value: &T)
                          -> T
-        where T : TypeFoldable<'tcx> + HasTypeFlags
+        where T : TypeFoldable<'tcx>
     {
         /*! See `higher_ranked::plug_leaks` */
 
@@ -1207,7 +1201,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     }
 
     pub fn resolve_type_vars_if_possible<T>(&self, value: &T) -> T
-        where T: TypeFoldable<'tcx> + HasTypeFlags
+        where T: TypeFoldable<'tcx>
     {
         /*!
          * Where possible, replaces type/int/float variables in
@@ -1225,6 +1219,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         value.fold_with(&mut r)
     }
 
+    pub fn resolve_type_and_region_vars_if_possible<T>(&self, value: &T) -> T
+        where T: TypeFoldable<'tcx>
+    {
+        let mut r = resolve::OpportunisticTypeAndRegionResolver::new(self);
+        value.fold_with(&mut r)
+    }
+
     /// Resolves all type variables in `t` and then, if any were left
     /// unresolved, substitutes an error type. This is used after the
     /// main checking when doing a second pass before writeback. The
@@ -1269,19 +1270,43 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                                      sp: Span,
                                      mk_msg: M,
                                      actual_ty: String,
-                                     err: Option<&TypeError<'tcx>>) where
-        M: FnOnce(Option<String>, String) -> String,
+                                     err: Option<&TypeError<'tcx>>)
+        where M: FnOnce(Option<String>, String) -> String,
     {
         self.type_error_message_str_with_expected(sp, mk_msg, None, actual_ty, err)
     }
 
+    pub fn type_error_struct_str<M>(&self,
+                                    sp: Span,
+                                    mk_msg: M,
+                                    actual_ty: String,
+                                    err: Option<&TypeError<'tcx>>)
+                                    -> DiagnosticBuilder<'tcx>
+        where M: FnOnce(Option<String>, String) -> String,
+    {
+        self.type_error_struct_str_with_expected(sp, mk_msg, None, actual_ty, err)
+    }
+
     pub fn type_error_message_str_with_expected<M>(&self,
                                                    sp: Span,
                                                    mk_msg: M,
                                                    expected_ty: Option<Ty<'tcx>>,
                                                    actual_ty: String,
-                                                   err: Option<&TypeError<'tcx>>) where
-        M: FnOnce(Option<String>, String) -> String,
+                                                   err: Option<&TypeError<'tcx>>)
+        where M: FnOnce(Option<String>, String) -> String,
+    {
+        self.type_error_struct_str_with_expected(sp, mk_msg, expected_ty, actual_ty, err)
+            .emit();
+    }
+
+    pub fn type_error_struct_str_with_expected<M>(&self,
+                                                  sp: Span,
+                                                  mk_msg: M,
+                                                  expected_ty: Option<Ty<'tcx>>,
+                                                  actual_ty: String,
+                                                  err: Option<&TypeError<'tcx>>)
+                                                  -> DiagnosticBuilder<'tcx>
+        where M: FnOnce(Option<String>, String) -> String,
     {
         debug!("hi! expected_ty = {:?}, actual_ty = {}", expected_ty, actual_ty);
 
@@ -1292,13 +1317,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 format!(" ({})", t_err)
             });
 
-            self.tcx.sess.span_err(sp, &format!("{}{}",
+            let mut db = self.tcx.sess.struct_span_err(sp, &format!("{}{}",
                 mk_msg(resolved_expected.map(|t| self.ty_to_string(t)), actual_ty),
                 error_str));
 
             if let Some(err) = err {
-                self.tcx.note_and_explain_type_err(err, sp)
+                self.tcx.note_and_explain_type_err(&mut db, err, sp);
             }
+            db
+        } else {
+            self.tcx.sess.diagnostic().struct_dummy()
         }
     }
 
@@ -1306,19 +1334,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                                  sp: Span,
                                  mk_msg: M,
                                  actual_ty: Ty<'tcx>,
-                                 err: Option<&TypeError<'tcx>>) where
-        M: FnOnce(String) -> String,
+                                 err: Option<&TypeError<'tcx>>)
+        where M: FnOnce(String) -> String,
+    {
+        self.type_error_struct(sp, mk_msg, actual_ty, err).emit();
+    }
+
+    pub fn type_error_struct<M>(&self,
+                                sp: Span,
+                                mk_msg: M,
+                                actual_ty: Ty<'tcx>,
+                                err: Option<&TypeError<'tcx>>)
+                                -> DiagnosticBuilder<'tcx>
+        where M: FnOnce(String) -> String,
     {
         let actual_ty = self.resolve_type_vars_if_possible(&actual_ty);
 
         // Don't report an error if actual type is TyError.
         if actual_ty.references_error() {
-            return;
+            return self.tcx.sess.diagnostic().struct_dummy();
         }
 
-        self.type_error_message_str(sp,
+        self.type_error_struct_str(sp,
             move |_e, a| { mk_msg(a) },
-            self.ty_to_string(actual_ty), err);
+            self.ty_to_string(actual_ty), err)
     }
 
     pub fn report_mismatched_types(&self,
@@ -1562,7 +1601,6 @@ impl TypeOrigin {
 impl<'tcx> SubregionOrigin<'tcx> {
     pub fn span(&self) -> Span {
         match *self {
-            RFC1214Subregion(ref a) => a.span(),
             Subtype(ref a) => a.span(),
             InfStackClosure(a) => a,
             InvokeClosure(a) => a,
index f6068f1b6449852d94227bfb22af402330deb901..bfc770d53aaba5c8b5dae220ae268e56dae3f3e1 100644 (file)
@@ -18,11 +18,13 @@ pub use self::RegionResolutionError::*;
 pub use self::VarValue::*;
 
 use super::{RegionVariableOrigin, SubregionOrigin, TypeTrace, MiscVariable};
+use super::unify_key;
 
 use rustc_data_structures::graph::{self, Direction, NodeIndex};
+use rustc_data_structures::unify::{self, UnificationTable};
 use middle::free_region::FreeRegionMap;
 use middle::ty::{self, Ty};
-use middle::ty::{BoundRegion, FreeRegion, Region, RegionVid};
+use middle::ty::{BoundRegion, Region, RegionVid};
 use middle::ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound};
 use middle::ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh};
 use middle::ty::error::TypeError;
@@ -234,15 +236,16 @@ pub struct RegionVarBindings<'a, 'tcx: 'a> {
     // bound on a variable and so forth, which can never be rolled
     // back.
     undo_log: RefCell<Vec<UndoLogEntry>>,
+    unification_table: RefCell<UnificationTable<ty::RegionVid>>,
 
     // This contains the results of inference.  It begins as an empty
     // option and only acquires a value after inference is complete.
     values: RefCell<Option<Vec<VarValue>>>,
 }
 
-#[derive(Debug)]
 pub struct RegionSnapshot {
     length: usize,
+    region_snapshot: unify::Snapshot<ty::RegionVid>,
     skolemization_count: u32,
 }
 
@@ -260,6 +263,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
             skolemization_count: Cell::new(0),
             bound_count: Cell::new(0),
             undo_log: RefCell::new(Vec::new()),
+            unification_table: RefCell::new(UnificationTable::new()),
         }
     }
 
@@ -273,6 +277,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
         self.undo_log.borrow_mut().push(OpenSnapshot);
         RegionSnapshot {
             length: length,
+            region_snapshot: self.unification_table.borrow_mut().snapshot(),
             skolemization_count: self.skolemization_count.get(),
         }
     }
@@ -289,6 +294,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
             (*undo_log)[snapshot.length] = CommitedSnapshot;
         }
         self.skolemization_count.set(snapshot.skolemization_count);
+        self.unification_table.borrow_mut().commit(snapshot.region_snapshot);
     }
 
     pub fn rollback_to(&self, snapshot: RegionSnapshot) {
@@ -328,6 +334,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
         let c = undo_log.pop().unwrap();
         assert!(c == OpenSnapshot);
         self.skolemization_count.set(snapshot.skolemization_count);
+        self.unification_table.borrow_mut()
+            .rollback_to(snapshot.region_snapshot);
     }
 
     pub fn num_vars(&self) -> u32 {
@@ -338,9 +346,13 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
     }
 
     pub fn new_region_var(&self, origin: RegionVariableOrigin) -> RegionVid {
-        let id = self.num_vars();
+        let vid = RegionVid { index: self.num_vars() };
         self.var_origins.borrow_mut().push(origin.clone());
-        let vid = RegionVid { index: id };
+
+        let u_vid = self.unification_table.borrow_mut().new_key(
+            unify_key::RegionVidKey { min_vid: vid }
+            );
+        assert_eq!(vid, u_vid);
         if self.in_snapshot() {
             self.undo_log.borrow_mut().push(AddVar(vid));
         }
@@ -460,6 +472,10 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
             // equating regions.
             self.make_subregion(origin.clone(), sub, sup);
             self.make_subregion(origin, sup, sub);
+
+            if let (ty::ReVar(sub), ty::ReVar(sup)) = (sub, sup) {
+                self.unification_table.borrow_mut().union(sub, sup);
+            }
         }
     }
 
@@ -473,13 +489,6 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
                origin);
 
         match (sub, sup) {
-            (ReEarlyBound(..), ReEarlyBound(..)) => {
-                // This case is used only to make sure that explicitly-specified
-                // `Self` types match the real self type in implementations.
-                //
-                // FIXME(NDM) -- we really shouldn't be comparing bound things
-                self.add_verify(VerifyRegSubReg(origin, sub, sup));
-            }
             (ReEarlyBound(..), _) |
             (ReLateBound(..), _) |
             (_, ReEarlyBound(..)) |
@@ -568,6 +577,10 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
         }
     }
 
+    pub fn opportunistic_resolve_var(&self, rid: RegionVid) -> ty::Region {
+        ty::ReVar(self.unification_table.borrow_mut().find_value(rid).min_vid)
+    }
+
     fn combine_map(&self, t: CombineMapType) -> &RefCell<CombineMap> {
         match t {
             Glb => &self.glbs,
@@ -1092,7 +1105,14 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
         for _ in 0..num_vars {
             graph.add_node(());
         }
-        let dummy_idx = graph.add_node(());
+
+        // Issue #30438: two distinct dummy nodes, one for incoming
+        // edges (dummy_source) and another for outgoing edges
+        // (dummy_sink). In `dummy -> a -> b -> dummy`, using one
+        // dummy node leads one to think (erroneously) there exists a
+        // path from `b` to `a`. Two dummy nodes sidesteps the issue.
+        let dummy_source = graph.add_node(());
+        let dummy_sink = graph.add_node(());
 
         for (constraint, _) in constraints.iter() {
             match *constraint {
@@ -1102,10 +1122,10 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
                                    *constraint);
                 }
                 ConstrainRegSubVar(_, b_id) => {
-                    graph.add_edge(dummy_idx, NodeIndex(b_id.index as usize), *constraint);
+                    graph.add_edge(dummy_source, NodeIndex(b_id.index as usize), *constraint);
                 }
                 ConstrainVarSubReg(a_id, _) => {
-                    graph.add_edge(NodeIndex(a_id.index as usize), dummy_idx, *constraint);
+                    graph.add_edge(NodeIndex(a_id.index as usize), dummy_sink, *constraint);
                 }
             }
         }
@@ -1312,6 +1332,13 @@ impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> {
     }
 }
 
+impl fmt::Debug for RegionSnapshot {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "RegionSnapshot(length={},skolemization={})",
+               self.length, self.skolemization_count)
+    }
+}
+
 impl<'tcx> fmt::Debug for GenericKind<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
index 4bcceade775999c1d90d024890a7bfb4421e044f..c68d0a9fa5683948af04daabf08ac25d21e361e6 100644 (file)
@@ -9,8 +9,7 @@
 // except according to those terms.
 
 use super::{InferCtxt, FixupError, FixupResult};
-use middle::ty::{self, Ty, HasTypeFlags};
-use middle::ty::fold::{TypeFoldable};
+use middle::ty::{self, Ty, TypeFoldable};
 
 ///////////////////////////////////////////////////////////////////////////
 // OPPORTUNISTIC TYPE RESOLVER
@@ -40,7 +39,42 @@ impl<'a, 'tcx> ty::fold::TypeFolder<'tcx> for OpportunisticTypeResolver<'a, 'tcx
             t // micro-optimize -- if there is nothing in this type that this fold affects...
         } else {
             let t0 = self.infcx.shallow_resolve(t);
-            ty::fold::super_fold_ty(self, t0)
+            t0.super_fold_with(self)
+        }
+    }
+}
+
+/// The opportunistic type and region resolver is similar to the
+/// opportunistic type resolver, but also opportunistly resolves
+/// regions. It is useful for canonicalization.
+pub struct OpportunisticTypeAndRegionResolver<'a, 'tcx:'a> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+}
+
+impl<'a, 'tcx> OpportunisticTypeAndRegionResolver<'a, 'tcx> {
+    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self {
+        OpportunisticTypeAndRegionResolver { infcx: infcx }
+    }
+}
+
+impl<'a, 'tcx> ty::fold::TypeFolder<'tcx> for OpportunisticTypeAndRegionResolver<'a, 'tcx> {
+    fn tcx(&self) -> &ty::ctxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        if !t.needs_infer() {
+            t // micro-optimize -- if there is nothing in this type that this fold affects...
+        } else {
+            let t0 = self.infcx.shallow_resolve(t);
+            t0.super_fold_with(self)
+        }
+    }
+
+    fn fold_region(&mut self, r: ty::Region) -> ty::Region {
+        match r {
+          ty::ReVar(rid) => self.infcx.region_vars.opportunistic_resolve_var(rid),
+          _ => r,
         }
     }
 }
@@ -98,7 +132,7 @@ impl<'a, 'tcx> ty::fold::TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> {
                                 t));
                 }
                 _ => {
-                    ty::fold::super_fold_ty(self, t)
+                    t.super_fold_with(self)
                 }
             }
         }
index 41aa191ac24d210f23a3e36dd7cf84293ad6fe5c..c83231930f502e0302d0c29327c8a05b43e3241d 100644 (file)
@@ -10,7 +10,7 @@
 
 use syntax::ast;
 use middle::ty::{self, IntVarValue, Ty};
-use rustc_data_structures::unify::UnifyKey;
+use rustc_data_structures::unify::{Combine, UnifyKey};
 
 pub trait ToType<'tcx> {
     fn to_type(&self, tcx: &ty::ctxt<'tcx>) -> Ty<'tcx>;
@@ -23,6 +23,33 @@ impl UnifyKey for ty::IntVid {
     fn tag(_: Option<ty::IntVid>) -> &'static str { "IntVid" }
 }
 
+#[derive(PartialEq, Copy, Clone, Debug)]
+pub struct RegionVidKey {
+    /// The minimum region vid in the unification set. This is needed
+    /// to have a canonical name for a type to prevent infinite
+    /// recursion.
+    pub min_vid: ty::RegionVid
+}
+
+impl Combine for RegionVidKey {
+    fn combine(&self, other: &RegionVidKey) -> RegionVidKey {
+        let min_vid = if self.min_vid.index < other.min_vid.index {
+            self.min_vid
+        } else {
+            other.min_vid
+        };
+
+        RegionVidKey { min_vid: min_vid }
+    }
+}
+
+impl UnifyKey for ty::RegionVid {
+    type Value = RegionVidKey;
+    fn index(&self) -> u32 { self.index }
+    fn from_index(i: u32) -> ty::RegionVid { ty::RegionVid { index: i } }
+    fn tag(_: Option<ty::RegionVid>) -> &'static str { "RegionVid" }
+}
+
 impl<'tcx> ToType<'tcx> for IntVarValue {
     fn to_type(&self, tcx: &ty::ctxt<'tcx>) -> Ty<'tcx> {
         match *self {
index 48d7f44063ec361db0684114fd867a0d44ac0714..69b952ca1f3fcf3ae42320e8d02d3c294cdb46cf 100644 (file)
@@ -8,11 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use dep_graph::DepNode;
 use middle::def::DefFn;
 use middle::def_id::DefId;
 use middle::subst::{Subst, Substs, EnumeratedItems};
 use middle::ty::{TransmuteRestriction, ctxt, TyBareFn};
-use middle::ty::{self, Ty, HasTypeFlags};
+use middle::ty::{self, Ty, TypeFoldable};
 
 use std::fmt;
 
@@ -29,7 +30,7 @@ pub fn check_crate(tcx: &ctxt) {
         dummy_sized_ty: tcx.types.isize,
         dummy_unsized_ty: tcx.mk_slice(tcx.types.isize),
     };
-    tcx.map.krate().visit_all_items(&mut visitor);
+    tcx.visit_all_items_in_krate(DepNode::IntrinsicCheck, &mut visitor);
 }
 
 struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
index d983f26daf7ae75b0110ee215ee564ea8dbdc9b4..29299f01ed36fcc2f4a4829a1906e8ef9549c321 100644 (file)
@@ -496,7 +496,7 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
       hir::ExprBlock(..) | hir::ExprAssign(..) | hir::ExprAssignOp(..) |
       hir::ExprStruct(..) | hir::ExprRepeat(..) |
       hir::ExprInlineAsm(..) | hir::ExprBox(..) |
-      hir::ExprRange(..) => {
+      hir::ExprRange(..) | hir::ExprType(..) => {
           intravisit::walk_expr(ir, expr);
       }
     }
@@ -1160,18 +1160,26 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
           hir::ExprBox(ref e) |
           hir::ExprAddrOf(_, ref e) |
           hir::ExprCast(ref e, _) |
+          hir::ExprType(ref e, _) |
           hir::ExprUnary(_, ref e) => {
             self.propagate_through_expr(&**e, succ)
           }
 
           hir::ExprInlineAsm(ref ia) => {
 
-            let succ = ia.outputs.iter().rev().fold(succ, |succ, &(_, ref expr, _)| {
-                // see comment on lvalues
-                // in propagate_through_lvalue_components()
-                let succ = self.write_lvalue(&**expr, succ, ACC_WRITE);
-                self.propagate_through_lvalue_components(&**expr, succ)
-            });
+            let succ = ia.outputs.iter().rev().fold(succ,
+                |succ, out| {
+                    // see comment on lvalues
+                    // in propagate_through_lvalue_components()
+                    if out.is_indirect {
+                        self.propagate_through_expr(&*out.expr, succ)
+                    } else {
+                        let acc = if out.is_rw { ACC_WRITE|ACC_READ } else { ACC_WRITE };
+                        let succ = self.write_lvalue(&*out.expr, succ, acc);
+                        self.propagate_through_lvalue_components(&*out.expr, succ)
+                    }
+                }
+            );
             // Inputs are executed first. Propagate last because of rev order
             ia.inputs.iter().rev().fold(succ, |succ, &(_, ref expr)| {
                 self.propagate_through_expr(&**expr, succ)
@@ -1416,9 +1424,11 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
         }
 
         // Output operands must be lvalues
-        for &(_, ref out, _) in &ia.outputs {
-          this.check_lvalue(&**out);
-          this.visit_expr(&**out);
+        for out in &ia.outputs {
+          if !out.is_indirect {
+            this.check_lvalue(&*out.expr);
+          }
+          this.visit_expr(&*out.expr);
         }
 
         intravisit::walk_expr(this, expr);
@@ -1434,7 +1444,7 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
       hir::ExprBlock(..) | hir::ExprAddrOf(..) |
       hir::ExprStruct(..) | hir::ExprRepeat(..) |
       hir::ExprClosure(..) | hir::ExprPath(..) | hir::ExprBox(..) |
-      hir::ExprRange(..) => {
+      hir::ExprRange(..) | hir::ExprType(..) => {
         intravisit::walk_expr(this, expr);
       }
     }
@@ -1466,10 +1476,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                  entry_ln: LiveNode,
                  body: &hir::Block)
     {
-        // within the fn body, late-bound regions are liberated:
+        // within the fn body, late-bound regions are liberated
+        // and must outlive the *call-site* of the function.
         let fn_ret =
             self.ir.tcx.liberate_late_bound_regions(
-                self.ir.tcx.region_maps.item_extent(body.id),
+                self.ir.tcx.region_maps.call_site_extent(id, body.id),
                 &self.fn_ret(id));
 
         match fn_ret {
@@ -1489,7 +1500,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                             },
                         _ => false
                     };
-                    span_err!(self.ir.tcx.sess, sp, E0269, "not all control paths return a value");
+                    let mut err = struct_span_err!(self.ir.tcx.sess,
+                                                   sp,
+                                                   E0269,
+                                                   "not all control paths return a value");
                     if ends_with_stmt {
                         let last_stmt = body.stmts.first().unwrap();
                         let original_span = original_sp(self.ir.tcx.sess.codemap(),
@@ -1499,9 +1513,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                             hi: original_span.hi,
                             expn_id: original_span.expn_id
                         };
-                        self.ir.tcx.sess.span_help(
-                            span_semicolon, "consider removing this semicolon:");
+                        err.span_help(span_semicolon, "consider removing this semicolon:");
                     }
+                    err.emit();
                 }
             }
             ty::FnDiverging
index 70ef112efbaaba872032ad1298e77fddc4f7a990..1eb5efa0bda446eb88fe613d786608c88fb4359f 100644 (file)
@@ -518,6 +518,10 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
             self.cat_def(expr.id, expr.span, expr_ty, def)
           }
 
+          hir::ExprType(ref e, _) => {
+            self.cat_expr(&**e)
+          }
+
           hir::ExprAddrOf(..) | hir::ExprCall(..) |
           hir::ExprAssign(..) | hir::ExprAssignOp(..) |
           hir::ExprClosure(..) | hir::ExprRet(..) |
@@ -548,7 +552,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
           def::DefAssociatedConst(..) | def::DefFn(..) | def::DefMethod(..) => {
                 Ok(self.cat_rvalue_node(id, span, expr_ty))
           }
-          def::DefMod(_) | def::DefForeignMod(_) | def::DefUse(_) |
+          def::DefMod(_) | def::DefForeignMod(_) |
           def::DefTrait(_) | def::DefTy(..) | def::DefPrimTy(_) |
           def::DefTyParam(..) |
           def::DefLabel(_) | def::DefSelfTy(..) |
@@ -609,6 +613,8 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
                 note: NoteNone
             }))
           }
+
+          def::DefErr => panic!("DefErr in memory categorization")
         }
     }
 
@@ -1196,7 +1202,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, '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 {
+            if path_res.depth != 0 || path_res.base_def == def::DefErr {
                 // Since patterns can be associated constants
                 // which are resolved during typeck, we might have
                 // some unresolved patterns reaching this stage
@@ -1261,7 +1267,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
                 _ => {
                     self.tcx().sess.span_bug(
                         pat.span,
-                        "enum pattern didn't resolve to enum or struct");
+                        &format!("enum pattern didn't resolve to enum or struct {:?}", opt_def));
                 }
             }
           }
index d146ad2d8003b766d2eabae34cd0e1d0e62c4f25..738440adf416d9280ff08a92a998db81e66c4f87 100644 (file)
@@ -15,6 +15,7 @@
 // makes all other generics or inline functions that it references
 // reachable as well.
 
+use dep_graph::DepNode;
 use front::map as ast_map;
 use middle::def;
 use middle::def_id::DefId;
@@ -349,6 +350,7 @@ impl<'a, 'v> Visitor<'v> for CollectPrivateImplItemsVisitor<'a> {
 pub fn find_reachable(tcx: &ty::ctxt,
                       access_levels: &privacy::AccessLevels)
                       -> NodeSet {
+    let _task = tcx.dep_graph.in_task(DepNode::Reachability);
 
     let mut reachable_context = ReachableContext::new(tcx);
 
index c9f26ffe4163bffe7a1d6543e0e7d6ec7175d4e5..2f1342a598dc4fae5679baf63389dfb9fcb2c4bf 100644 (file)
@@ -14,7 +14,7 @@
 //! region parameterized.
 //!
 //! Most of the documentation on regions can be found in
-//! `middle/typeck/infer/region_inference.rs`
+//! `middle/infer/region_inference/README.md`
 
 use front::map as ast_map;
 use session::Session;
@@ -125,6 +125,10 @@ pub const DUMMY_CODE_EXTENT : CodeExtent = CodeExtent(1);
 pub enum CodeExtentData {
     Misc(ast::NodeId),
 
+    // extent of the call-site for a function or closure (outlives
+    // the parameters as well as the body).
+    CallSiteScope { fn_id: ast::NodeId, body_id: ast::NodeId },
+
     // extent of parameters passed to a function or closure (they
     // outlive its body)
     ParameterScope { fn_id: ast::NodeId, body_id: ast::NodeId },
@@ -136,20 +140,20 @@ pub enum CodeExtentData {
     Remainder(BlockRemainder)
 }
 
-/// extent of destructors for temporaries of node-id
+/// extent of call-site for a function/method.
 #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, RustcEncodable,
            RustcDecodable, Debug, Copy)]
-pub struct DestructionScopeData {
-    pub node_id: ast::NodeId
+pub struct CallSiteScopeData {
+    pub fn_id: ast::NodeId, pub body_id: ast::NodeId,
 }
 
-impl DestructionScopeData {
-    pub fn new(node_id: ast::NodeId) -> DestructionScopeData {
-        DestructionScopeData { node_id: node_id }
-    }
+impl CallSiteScopeData {
     pub fn to_code_extent(&self, region_maps: &RegionMaps) -> CodeExtent {
         region_maps.lookup_code_extent(
-            CodeExtentData::DestructionScope(self.node_id))
+            match *self {
+                CallSiteScopeData { fn_id, body_id } =>
+                    CodeExtentData::CallSiteScope { fn_id: fn_id, body_id: body_id },
+            })
     }
 }
 
@@ -190,6 +194,7 @@ impl CodeExtentData {
             // precise extent denoted by `self`.
             CodeExtentData::Remainder(br) => br.block,
             CodeExtentData::DestructionScope(node_id) => node_id,
+            CodeExtentData::CallSiteScope { fn_id: _, body_id } |
             CodeExtentData::ParameterScope { fn_id: _, body_id } => body_id,
         }
     }
@@ -215,6 +220,7 @@ impl CodeExtent {
         match ast_map.find(self.node_id(region_maps)) {
             Some(ast_map::NodeBlock(ref blk)) => {
                 match region_maps.code_extent_data(*self) {
+                    CodeExtentData::CallSiteScope { .. } |
                     CodeExtentData::ParameterScope { .. } |
                     CodeExtentData::Misc(_) |
                     CodeExtentData::DestructionScope(_) => Some(blk.span),
@@ -346,6 +352,10 @@ impl RegionMaps {
     pub fn item_extent(&self, n: ast::NodeId) -> CodeExtent {
         self.lookup_code_extent(CodeExtentData::DestructionScope(n))
     }
+    pub fn call_site_extent(&self, fn_id: ast::NodeId, body_id: ast::NodeId) -> CodeExtent {
+        assert!(fn_id != body_id);
+        self.lookup_code_extent(CodeExtentData::CallSiteScope { fn_id: fn_id, body_id: body_id })
+    }
     pub fn opt_destruction_extent(&self, n: ast::NodeId) -> Option<CodeExtent> {
         self.code_extent_interner.borrow().get(&CodeExtentData::DestructionScope(n)).cloned()
     }
@@ -1101,6 +1111,9 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
            body.id,
            visitor.cx.parent);
 
+    visitor.cx.parent = visitor.new_code_extent(
+        CodeExtentData::CallSiteScope { fn_id: id, body_id: body.id });
+
     let fn_decl_scope = visitor.new_code_extent(
         CodeExtentData::ParameterScope { fn_id: id, body_id: body.id });
 
index b37b30703101a1b2986689529945204ff678aef8..2c74f3a82e41441e39cc501e9436691c96b5a669 100644 (file)
@@ -42,7 +42,7 @@ pub enum DefRegion {
                         /* lifetime decl */ ast::NodeId),
     DefLateBoundRegion(ty::DebruijnIndex,
                        /* lifetime decl */ ast::NodeId),
-    DefFreeRegion(/* block scope */ region::DestructionScopeData,
+    DefFreeRegion(region::CallSiteScopeData,
                   /* lifetime decl */ ast::NodeId),
 }
 
@@ -79,13 +79,13 @@ struct LifetimeContext<'a> {
 enum ScopeChain<'a> {
     /// EarlyScope(i, ['a, 'b, ...], s) extends s with early-bound
     /// lifetimes, assigning indexes 'a => i, 'b => i+1, ... etc.
-    EarlyScope(subst::ParamSpace, &'a Vec<hir::LifetimeDef>, Scope<'a>),
+    EarlyScope(subst::ParamSpace, &'a [hir::LifetimeDef], Scope<'a>),
     /// LateScope(['a, 'b, ...], s) extends s with late-bound
     /// lifetimes introduced by the declaration binder_id.
-    LateScope(&'a Vec<hir::LifetimeDef>, Scope<'a>),
-    /// lifetimes introduced by items within a code block are scoped
-    /// to that block.
-    BlockScope(region::DestructionScopeData, Scope<'a>),
+    LateScope(&'a [hir::LifetimeDef], Scope<'a>),
+
+    /// lifetimes introduced by a fn are scoped to the call-site for that fn.
+    FnScope { fn_id: ast::NodeId, body_id: ast::NodeId, s: Scope<'a> },
     RootScope
 }
 
@@ -95,15 +95,16 @@ static ROOT_SCOPE: ScopeChain<'static> = RootScope;
 
 pub fn krate(sess: &Session, krate: &hir::Crate, def_map: &DefMap) -> NamedRegionMap {
     let mut named_region_map = NodeMap();
-    krate.visit_all_items(&mut LifetimeContext {
-        sess: sess,
-        named_region_map: &mut named_region_map,
-        scope: &ROOT_SCOPE,
-        def_map: def_map,
-        trait_ref_hack: false,
-        labels_in_fn: vec![],
+    sess.abort_if_new_errors(|| {
+        krate.visit_all_items(&mut LifetimeContext {
+            sess: sess,
+            named_region_map: &mut named_region_map,
+            scope: &ROOT_SCOPE,
+            def_map: def_map,
+            trait_ref_hack: false,
+            labels_in_fn: vec![],
+        });
     });
-    sess.abort_if_errors();
     named_region_map
 }
 
@@ -172,20 +173,20 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
     }
 
     fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
-                b: &'v hir::Block, s: Span, _: ast::NodeId) {
+                b: &'v hir::Block, s: Span, fn_id: ast::NodeId) {
         match fk {
             FnKind::ItemFn(_, generics, _, _, _, _) => {
                 self.visit_early_late(subst::FnSpace, generics, |this| {
-                    this.walk_fn(fk, fd, b, s)
+                    this.add_scope_and_walk_fn(fk, fd, b, s, fn_id)
                 })
             }
             FnKind::Method(_, sig, _) => {
                 self.visit_early_late(subst::FnSpace, &sig.generics, |this| {
-                    this.walk_fn(fk, fd, b, s)
+                    this.add_scope_and_walk_fn(fk, fd, b, s, fn_id)
                 })
             }
             FnKind::Closure => {
-                self.walk_fn(fk, fd, b, s)
+                self.add_scope_and_walk_fn(fk, fd, b, s, fn_id)
             }
         }
     }
@@ -205,7 +206,7 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
                 // a trait ref, which introduces a binding scope.
                 match self.def_map.get(&ty.id).map(|d| (d.base_def, d.depth)) {
                     Some((def::DefTrait(..), 0)) => {
-                        self.with(LateScope(&Vec::new(), self.scope), |_, this| {
+                        self.with(LateScope(&[], self.scope), |_, this| {
                             this.visit_path(path, ty.id);
                         });
                     }
@@ -236,12 +237,6 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
         replace(&mut self.labels_in_fn, saved);
     }
 
-    fn visit_block(&mut self, b: &hir::Block) {
-        self.with(BlockScope(region::DestructionScopeData::new(b.id),
-                             self.scope),
-                  |_, this| intravisit::walk_block(this, b));
-    }
-
     fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
         if lifetime_ref.name == special_idents::static_lifetime.name {
             self.insert_lifetime(lifetime_ref, DefStaticRegion);
@@ -350,25 +345,25 @@ impl ShadowKind {
     }
 }
 
-fn signal_shadowing_problem(
-    sess: &Session, name: ast::Name, orig: Original, shadower: Shadower) {
-    if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) {
+fn signal_shadowing_problem(sess: &Session, name: ast::Name, orig: Original, shadower: Shadower) {
+    let mut err = if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) {
         // lifetime/lifetime shadowing is an error
-        span_err!(sess, shadower.span, E0496,
-                  "{} name `{}` shadows a \
-                   {} name that is already in scope",
-                  shadower.kind.desc(), name, orig.kind.desc());
+        struct_span_err!(sess, shadower.span, E0496,
+                         "{} name `{}` shadows a \
+                          {} name that is already in scope",
+                         shadower.kind.desc(), name, orig.kind.desc())
     } else {
         // shadowing involving a label is only a warning, due to issues with
         // labels and lifetimes not being macro-hygienic.
-        sess.span_warn(shadower.span,
-                      &format!("{} name `{}` shadows a \
-                                {} name that is already in scope",
-                               shadower.kind.desc(), name, orig.kind.desc()));
-    }
-    sess.span_note(orig.span,
-                   &format!("shadowed {} `{}` declared here",
-                            orig.kind.desc(), name));
+        sess.struct_span_warn(shadower.span,
+                              &format!("{} name `{}` shadows a \
+                                        {} name that is already in scope",
+                                       shadower.kind.desc(), name, orig.kind.desc()))
+    };
+    err.span_note(orig.span,
+                  &format!("shadowed {} `{}` declared here",
+                           orig.kind.desc(), name));
+    err.emit();
 }
 
 // Adds all labels in `b` to `ctxt.labels_in_fn`, signalling a warning
@@ -437,7 +432,7 @@ fn extract_labels<'v, 'a>(ctxt: &mut LifetimeContext<'a>, b: &'v hir::Block) {
                                            label_span: Span) {
         loop {
             match *scope {
-                BlockScope(_, s) => { scope = s; }
+                FnScope { s, .. } => { scope = s; }
                 RootScope => { return; }
 
                 EarlyScope(_, lifetimes, s) |
@@ -461,14 +456,13 @@ fn extract_labels<'v, 'a>(ctxt: &mut LifetimeContext<'a>, b: &'v hir::Block) {
 }
 
 impl<'a> LifetimeContext<'a> {
-    // This is just like intravisit::walk_fn, except that it extracts the
-    // labels of the function body and swaps them in before visiting
-    // the function body itself.
-    fn walk_fn<'b>(&mut self,
-                   fk: FnKind,
-                   fd: &hir::FnDecl,
-                   fb: &'b hir::Block,
-                   _span: Span) {
+    fn add_scope_and_walk_fn<'b>(&mut self,
+                                 fk: FnKind,
+                                 fd: &hir::FnDecl,
+                                 fb: &'b hir::Block,
+                                 _span: Span,
+                                 fn_id: ast::NodeId) {
+
         match fk {
             FnKind::ItemFn(_, generics, _, _, _, _) => {
                 intravisit::walk_fn_decl(self, fd);
@@ -488,7 +482,8 @@ impl<'a> LifetimeContext<'a> {
         // `self.labels_in_fn`.
         extract_labels(self, fb);
 
-        self.visit_block(fb);
+        self.with(FnScope { fn_id: fn_id, body_id: fb.id, s: self.scope },
+                  |_old_scope, this| this.visit_block(fb))
     }
 
     fn with<F>(&mut self, wrap_scope: ScopeChain, f: F) where
@@ -559,8 +554,11 @@ impl<'a> LifetimeContext<'a> {
         let mut scope = self.scope;
         loop {
             match *scope {
-                BlockScope(blk_scope, s) => {
-                    return self.resolve_free_lifetime_ref(blk_scope, lifetime_ref, s);
+                FnScope {fn_id, body_id, s } => {
+                    return self.resolve_free_lifetime_ref(
+                        region::CallSiteScopeData { fn_id: fn_id, body_id: body_id },
+                        lifetime_ref,
+                        s);
                 }
 
                 RootScope => {
@@ -604,7 +602,7 @@ impl<'a> LifetimeContext<'a> {
     }
 
     fn resolve_free_lifetime_ref(&mut self,
-                                 scope_data: region::DestructionScopeData,
+                                 scope_data: region::CallSiteScopeData,
                                  lifetime_ref: &hir::Lifetime,
                                  scope: Scope) {
         debug!("resolve_free_lifetime_ref \
@@ -622,8 +620,10 @@ impl<'a> LifetimeContext<'a> {
                     scope_data: {:?} scope: {:?} search_result: {:?}",
                    scope_data, scope, search_result);
             match *scope {
-                BlockScope(blk_scope_data, s) => {
-                    scope_data = blk_scope_data;
+                FnScope { fn_id, body_id, s } => {
+                    scope_data = region::CallSiteScopeData {
+                        fn_id: fn_id, body_id: body_id
+                    };
                     scope = s;
                 }
 
@@ -661,7 +661,7 @@ impl<'a> LifetimeContext<'a> {
                     lifetime_ref.name);
     }
 
-    fn check_lifetime_defs(&mut self, old_scope: Scope, lifetimes: &Vec<hir::LifetimeDef>) {
+    fn check_lifetime_defs(&mut self, old_scope: Scope, lifetimes: &[hir::LifetimeDef]) {
         for i in 0..lifetimes.len() {
             let lifetime_i = &lifetimes[i];
 
@@ -711,7 +711,7 @@ impl<'a> LifetimeContext<'a> {
 
         loop {
             match *old_scope {
-                BlockScope(_, s) => {
+                FnScope { s, .. } => {
                     old_scope = s;
                 }
 
@@ -753,7 +753,7 @@ impl<'a> LifetimeContext<'a> {
     }
 }
 
-fn search_lifetimes<'a>(lifetimes: &'a Vec<hir::LifetimeDef>,
+fn search_lifetimes<'a>(lifetimes: &'a [hir::LifetimeDef],
                     lifetime_ref: &hir::Lifetime)
                     -> Option<(u32, &'a hir::Lifetime)> {
     for (i, lifetime_decl) in lifetimes.iter().enumerate() {
@@ -864,7 +864,7 @@ impl<'a> fmt::Debug for ScopeChain<'a> {
         match *self {
             EarlyScope(space, defs, _) => write!(fmt, "EarlyScope({:?}, {:?})", space, defs),
             LateScope(defs, _) => write!(fmt, "LateScope({:?})", defs),
-            BlockScope(id, _) => write!(fmt, "BlockScope({:?})", id),
+            FnScope { fn_id, body_id, s: _ } => write!(fmt, "FnScope({:?}, {:?})", fn_id, body_id),
             RootScope => write!(fmt, "RootScope"),
         }
     }
index 0d92c3da83c8bed89dde98903e70b2db4f213b63..87bc8bb885584a9ec7f599777083fc15c4d3e236 100644 (file)
@@ -13,6 +13,7 @@
 
 pub use self::StabilityLevel::*;
 
+use dep_graph::DepNode;
 use session::Session;
 use lint;
 use middle::cstore::{CrateStore, LOCAL_CRATE};
@@ -25,11 +26,11 @@ use syntax::codemap::{Span, DUMMY_SP};
 use syntax::ast;
 use syntax::ast::{NodeId, Attribute};
 use syntax::feature_gate::{GateIssue, emit_feature_err};
-use syntax::attr::{self, Stability, AttrMetaMethods};
+use syntax::attr::{self, Stability, Deprecation, AttrMetaMethods};
 use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap};
 
 use rustc_front::hir;
-use rustc_front::hir::{Block, Crate, Item, Generics, StructField, Variant};
+use rustc_front::hir::{Crate, Item, Generics, StructField, Variant};
 use rustc_front::intravisit::{self, Visitor};
 
 use std::mem::replace;
@@ -61,7 +62,8 @@ enum AnnotationKind {
 pub struct Index<'tcx> {
     /// This is mostly a cache, except the stabilities of local items
     /// are filled by the annotator.
-    map: DefIdMap<Option<&'tcx Stability>>,
+    stab_map: DefIdMap<Option<&'tcx Stability>>,
+    depr_map: DefIdMap<Option<Deprecation>>,
 
     /// Maps for each crate whether it is part of the staged API.
     staged_api: FnvHashMap<ast::CrateNum, bool>
@@ -71,37 +73,41 @@ pub struct Index<'tcx> {
 struct Annotator<'a, 'tcx: 'a> {
     tcx: &'a ty::ctxt<'tcx>,
     index: &'a mut Index<'tcx>,
-    parent: Option<&'tcx Stability>,
+    parent_stab: Option<&'tcx Stability>,
+    parent_depr: Option<Deprecation>,
     access_levels: &'a AccessLevels,
     in_trait_impl: bool,
-    in_enum: bool,
 }
 
 impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
     // Determine the stability for a node based on its attributes and inherited
     // stability. The stability is recorded in the index and used as the parent.
-    fn annotate<F>(&mut self, id: NodeId, attrs: &Vec<Attribute>,
+    fn annotate<F>(&mut self, id: NodeId, attrs: &[Attribute],
                    item_sp: Span, kind: AnnotationKind, visit_children: F)
         where F: FnOnce(&mut Annotator)
     {
         if self.index.staged_api[&LOCAL_CRATE] && self.tcx.sess.features.borrow().staged_api {
             debug!("annotate(id = {:?}, attrs = {:?})", id, attrs);
+            if let Some(..) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) {
+                self.tcx.sess.span_err(item_sp, "`#[deprecated]` cannot be used in staged api, \
+                                                 use `#[rustc_deprecated]` instead");
+            }
             if let Some(mut stab) = attr::find_stability(self.tcx.sess.diagnostic(),
                                                          attrs, item_sp) {
                 // Error if prohibited, or can't inherit anything from a container
                 if kind == AnnotationKind::Prohibited ||
                    (kind == AnnotationKind::Container &&
                     stab.level.is_stable() &&
-                    stab.depr.is_none()) {
+                    stab.rustc_depr.is_none()) {
                     self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
                 }
 
                 debug!("annotate: found {:?}", stab);
                 // If parent is deprecated and we're not, inherit this by merging
                 // deprecated_since and its reason.
-                if let Some(parent_stab) = self.parent {
-                    if parent_stab.depr.is_some() && stab.depr.is_none() {
-                        stab.depr = parent_stab.depr.clone()
+                if let Some(parent_stab) = self.parent_stab {
+                    if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() {
+                        stab.rustc_depr = parent_stab.rustc_depr.clone()
                     }
                 }
 
@@ -109,8 +115,8 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
 
                 // Check if deprecated_since < stable_since. If it is,
                 // this is *almost surely* an accident.
-                if let (&Some(attr::Deprecation {since: ref dep_since, ..}),
-                        &attr::Stable {since: ref stab_since}) = (&stab.depr, &stab.level) {
+                if let (&Some(attr::RustcDeprecation {since: ref dep_since, ..}),
+                        &attr::Stable {since: ref stab_since}) = (&stab.rustc_depr, &stab.level) {
                     // Explicit version of iter::order::lt to handle parse errors properly
                     for (dep_v, stab_v) in dep_since.split(".").zip(stab_since.split(".")) {
                         if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) {
@@ -134,20 +140,20 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
                 }
 
                 let def_id = self.tcx.map.local_def_id(id);
-                self.index.map.insert(def_id, Some(stab));
+                self.index.stab_map.insert(def_id, Some(stab));
 
-                let parent = replace(&mut self.parent, Some(stab));
+                let orig_parent_stab = replace(&mut self.parent_stab, Some(stab));
                 visit_children(self);
-                self.parent = parent;
+                self.parent_stab = orig_parent_stab;
             } else {
-                debug!("annotate: not found, parent = {:?}", self.parent);
+                debug!("annotate: not found, parent = {:?}", self.parent_stab);
                 let mut is_error = kind == AnnotationKind::Required &&
                                    self.access_levels.is_reachable(id) &&
                                    !self.tcx.sess.opts.test;
-                if let Some(stab) = self.parent {
+                if let Some(stab) = self.parent_stab {
                     if stab.level.is_unstable() {
                         let def_id = self.tcx.map.local_def_id(id);
-                        self.index.map.insert(def_id, Some(stab));
+                        self.index.stab_map.insert(def_id, Some(stab));
                         is_error = false;
                     }
                 }
@@ -167,7 +173,26 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
                                                          outside of the standard library");
                 }
             }
-            visit_children(self);
+
+            if let Some(depr) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) {
+                if kind == AnnotationKind::Prohibited {
+                    self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless");
+                }
+
+                // `Deprecation` is just two pointers, no need to intern it
+                let def_id = self.tcx.map.local_def_id(id);
+                self.index.depr_map.insert(def_id, Some(depr.clone()));
+
+                let orig_parent_depr = replace(&mut self.parent_depr, Some(depr));
+                visit_children(self);
+                self.parent_depr = orig_parent_depr;
+            } else if let Some(depr) = self.parent_depr.clone() {
+                let def_id = self.tcx.map.local_def_id(id);
+                self.index.depr_map.insert(def_id, Some(depr));
+                visit_children(self);
+            } else {
+                visit_children(self);
+            }
         }
     }
 }
@@ -182,7 +207,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Annotator<'a, 'tcx> {
 
     fn visit_item(&mut self, i: &Item) {
         let orig_in_trait_impl = self.in_trait_impl;
-        let orig_in_enum = self.in_enum;
         let mut kind = AnnotationKind::Required;
         match i.node {
             // Inherent impls and foreign modules serve only as containers for other items,
@@ -197,14 +221,10 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Annotator<'a, 'tcx> {
                 self.in_trait_impl = true;
             }
             hir::ItemStruct(ref sd, _) => {
-                self.in_enum = false;
                 if !sd.is_struct() {
                     self.annotate(sd.id(), &i.attrs, i.span, AnnotationKind::Required, |_| {})
                 }
             }
-            hir::ItemEnum(..) => {
-                self.in_enum = true;
-            }
             _ => {}
         }
 
@@ -212,7 +232,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Annotator<'a, 'tcx> {
             intravisit::walk_item(v, i)
         });
         self.in_trait_impl = orig_in_trait_impl;
-        self.in_enum = orig_in_enum;
     }
 
     fn visit_trait_item(&mut self, ti: &hir::TraitItem) {
@@ -239,13 +258,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Annotator<'a, 'tcx> {
     }
 
     fn visit_struct_field(&mut self, s: &StructField) {
-        // FIXME: This is temporary, can't use attributes with tuple variant fields until snapshot
-        let kind = if self.in_enum && s.node.kind.is_unnamed() {
-            AnnotationKind::Prohibited
-        } else {
-            AnnotationKind::Required
-        };
-        self.annotate(s.node.id, &s.node.attrs, s.span, kind, |v| {
+        self.annotate(s.node.id, &s.node.attrs, s.span, AnnotationKind::Required, |v| {
             intravisit::walk_struct_field(v, s);
         });
     }
@@ -269,10 +282,10 @@ impl<'tcx> Index<'tcx> {
         let mut annotator = Annotator {
             tcx: tcx,
             index: self,
-            parent: None,
+            parent_stab: None,
+            parent_depr: None,
             access_levels: access_levels,
             in_trait_impl: false,
-            in_enum: false,
         };
         annotator.annotate(ast::CRATE_NODE_ID, &krate.attrs, krate.span, AnnotationKind::Required,
                            |v| intravisit::walk_crate(v, krate));
@@ -291,7 +304,8 @@ impl<'tcx> Index<'tcx> {
         staged_api.insert(LOCAL_CRATE, is_staged_api);
         Index {
             staged_api: staged_api,
-            map: DefIdMap(),
+            stab_map: DefIdMap(),
+            depr_map: DefIdMap(),
         }
     }
 }
@@ -301,6 +315,7 @@ impl<'tcx> Index<'tcx> {
 /// features used.
 pub fn check_unstable_api_usage(tcx: &ty::ctxt)
                                 -> FnvHashMap<InternedString, StabilityLevel> {
+    let _task = tcx.dep_graph.in_task(DepNode::StabilityCheck);
     let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features;
 
     // Put the active features into a map for quick lookup
@@ -314,8 +329,7 @@ pub fn check_unstable_api_usage(tcx: &ty::ctxt)
     };
     intravisit::walk_crate(&mut checker, tcx.map.krate());
 
-    let used_features = checker.used_features;
-    return used_features;
+    checker.used_features
 }
 
 struct Checker<'a, 'tcx: 'a> {
@@ -327,7 +341,11 @@ struct Checker<'a, 'tcx: 'a> {
 }
 
 impl<'a, 'tcx> Checker<'a, 'tcx> {
-    fn check(&mut self, id: DefId, span: Span, stab: &Option<&Stability>) {
+    fn check(&mut self, id: DefId, span: Span,
+             stab: &Option<&Stability>, _depr: &Option<Deprecation>) {
+        if !is_staged_api(self.tcx, id) {
+            return;
+        }
         // Only the cross-crate scenario matters when checking unstable APIs
         let cross_crate = !id.is_local();
         if !cross_crate {
@@ -363,17 +381,19 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
                 // This is an 'unmarked' API, which should not exist
                 // in the standard library.
                 if self.tcx.sess.features.borrow().unmarked_api {
-                    self.tcx.sess.span_warn(span, "use of unmarked library feature");
-                    self.tcx.sess.span_note(span, "this is either a bug in the library you are \
+                    self.tcx.sess.struct_span_warn(span, "use of unmarked library feature")
+                                 .span_note(span, "this is either a bug in the library you are \
                                                    using or a bug in the compiler - please \
-                                                   report it in both places");
+                                                   report it in both places")
+                                 .emit()
                 } else {
-                    self.tcx.sess.span_err(span, "use of unmarked library feature");
-                    self.tcx.sess.span_note(span, "this is either a bug in the library you are \
+                    self.tcx.sess.struct_span_err(span, "use of unmarked library feature")
+                                 .span_note(span, "this is either a bug in the library you are \
                                                    using or a bug in the compiler - please \
-                                                   report it in both places");
-                    self.tcx.sess.span_note(span, "use #![feature(unmarked_api)] in the \
-                                                   crate attributes to override this");
+                                                   report it in both places")
+                                 .span_note(span, "use #![feature(unmarked_api)] in the \
+                                                   crate attributes to override this")
+                                 .emit()
                 }
             }
         }
@@ -395,31 +415,31 @@ impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> {
         if item.span == DUMMY_SP && item.name.as_str() == "__test" { return }
 
         check_item(self.tcx, item, true,
-                   &mut |id, sp, stab| self.check(id, sp, stab));
+                   &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
         intravisit::walk_item(self, item);
     }
 
     fn visit_expr(&mut self, ex: &hir::Expr) {
         check_expr(self.tcx, ex,
-                   &mut |id, sp, stab| self.check(id, sp, stab));
+                   &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
         intravisit::walk_expr(self, ex);
     }
 
     fn visit_path(&mut self, path: &hir::Path, id: ast::NodeId) {
         check_path(self.tcx, path, id,
-                   &mut |id, sp, stab| self.check(id, sp, stab));
+                   &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
         intravisit::walk_path(self, path)
     }
 
     fn visit_path_list_item(&mut self, prefix: &hir::Path, item: &hir::PathListItem) {
         check_path_list_item(self.tcx, item,
-                   &mut |id, sp, stab| self.check(id, sp, stab));
+                   &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
         intravisit::walk_path_list_item(self, prefix, item)
     }
 
     fn visit_pat(&mut self, pat: &hir::Pat) {
         check_pat(self.tcx, pat,
-                  &mut |id, sp, stab| self.check(id, sp, stab));
+                  &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
         intravisit::walk_pat(self, pat)
     }
 
@@ -441,7 +461,7 @@ impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> {
 
 /// Helper for discovering nodes to check for stability
 pub fn check_item(tcx: &ty::ctxt, item: &hir::Item, warn_about_defns: bool,
-                  cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
+                  cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
     match item.node {
         hir::ItemExternCrate(_) => {
             // compiler-generated `extern crate` items have a dummy span.
@@ -478,7 +498,7 @@ pub fn check_item(tcx: &ty::ctxt, item: &hir::Item, warn_about_defns: bool,
 
 /// Helper for discovering nodes to check for stability
 pub fn check_expr(tcx: &ty::ctxt, e: &hir::Expr,
-                  cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
+                  cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
     let span;
     let id = match e.node {
         hir::ExprMethodCall(i, _, _) => {
@@ -539,7 +559,7 @@ pub fn check_expr(tcx: &ty::ctxt, e: &hir::Expr,
 }
 
 pub fn check_path(tcx: &ty::ctxt, path: &hir::Path, id: ast::NodeId,
-                  cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
+                  cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
     match tcx.def_map.borrow().get(&id).map(|d| d.full_def()) {
         Some(def::DefPrimTy(..)) => {}
         Some(def::DefSelfTy(..)) => {}
@@ -551,7 +571,7 @@ pub fn check_path(tcx: &ty::ctxt, path: &hir::Path, id: ast::NodeId,
 }
 
 pub fn check_path_list_item(tcx: &ty::ctxt, item: &hir::PathListItem,
-                  cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
+                  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::DefPrimTy(..)) => {}
         Some(def) => {
@@ -562,7 +582,7 @@ pub fn check_path_list_item(tcx: &ty::ctxt, item: &hir::PathListItem,
 }
 
 pub fn check_pat(tcx: &ty::ctxt, pat: &hir::Pat,
-                 cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
+                 cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
     debug!("check_pat(pat = {:?})", pat);
     if is_internal(tcx, pat.span) { return; }
 
@@ -591,21 +611,21 @@ pub fn check_pat(tcx: &ty::ctxt, pat: &hir::Pat,
 }
 
 fn maybe_do_stability_check(tcx: &ty::ctxt, id: DefId, span: Span,
-                            cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
-    if !is_staged_api(tcx, id) {
-        debug!("maybe_do_stability_check: \
-                skipping id={:?} since it is not staged_api", id);
-        return;
-    }
+                            cb: &mut FnMut(DefId, Span,
+                                           &Option<&Stability>, &Option<Deprecation>)) {
     if is_internal(tcx, span) {
         debug!("maybe_do_stability_check: \
                 skipping span={:?} since it is internal", span);
         return;
     }
-    let ref stability = lookup(tcx, id);
+    let (stability, deprecation) = if is_staged_api(tcx, id) {
+        (lookup_stability(tcx, id), None)
+    } else {
+        (None, lookup_deprecation(tcx, id))
+    };
     debug!("maybe_do_stability_check: \
             inspecting id={:?} span={:?} of stability={:?}", id, span, stability);
-    cb(id, span, stability);
+    cb(id, span, &stability, &deprecation);
 }
 
 fn is_internal(tcx: &ty::ctxt, span: Span) -> bool {
@@ -627,47 +647,42 @@ fn is_staged_api(tcx: &ty::ctxt, id: DefId) -> bool {
 
 /// Lookup the stability for a node, loading external crate
 /// metadata as necessary.
-pub fn lookup<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
-    if let Some(st) = tcx.stability.borrow().map.get(&id) {
+pub fn lookup_stability<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
+    if let Some(st) = tcx.stability.borrow().stab_map.get(&id) {
         return *st;
     }
 
-    let st = lookup_uncached(tcx, id);
-    tcx.stability.borrow_mut().map.insert(id, st);
+    let st = lookup_stability_uncached(tcx, id);
+    tcx.stability.borrow_mut().stab_map.insert(id, st);
     st
 }
 
-fn lookup_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
-    debug!("lookup(id={:?})", id);
-
-    // is this definition the implementation of a trait method?
-    match tcx.trait_item_of_item(id) {
-        Some(ty::MethodTraitItemId(trait_method_id)) if trait_method_id != id => {
-            debug!("lookup: trait_method_id={:?}", trait_method_id);
-            return lookup(tcx, trait_method_id)
-        }
-        _ => {}
+pub fn lookup_deprecation<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<Deprecation> {
+    if let Some(depr) = tcx.stability.borrow().depr_map.get(&id) {
+        return depr.clone();
     }
 
-    let item_stab = if id.is_local() {
+    let depr = lookup_deprecation_uncached(tcx, id);
+    tcx.stability.borrow_mut().depr_map.insert(id, depr.clone());
+    depr
+}
+
+fn lookup_stability_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
+    debug!("lookup(id={:?})", id);
+    if id.is_local() {
         None // The stability cache is filled partially lazily
     } else {
         tcx.sess.cstore.stability(id).map(|st| tcx.intern_stability(st))
-    };
-
-    item_stab.or_else(|| {
-        if tcx.is_impl(id) {
-            if let Some(trait_id) = tcx.trait_id_of_impl(id) {
-                // FIXME (#18969): for the time being, simply use the
-                // stability of the trait to determine the stability of any
-                // unmarked impls for it. See FIXME above for more details.
+    }
+}
 
-                debug!("lookup: trait_id={:?}", trait_id);
-                return lookup(tcx, trait_id);
-            }
-        }
-        None
-    })
+fn lookup_deprecation_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<Deprecation> {
+    debug!("lookup(id={:?})", id);
+    if id.is_local() {
+        None // The stability cache is filled partially lazily
+    } else {
+        tcx.sess.cstore.deprecation(id)
+    }
 }
 
 /// Given the list of enabled features that were not language features (i.e. that
index c44891de0a055356b81400c8d3239b5892f2c211..61f7b2db4c43233769af36b0b8b5b185a49297b8 100644 (file)
 pub use self::ParamSpace::*;
 pub use self::RegionSubsts::*;
 
-use middle::ty::{self, Ty, HasTypeFlags, RegionEscape};
+use middle::cstore;
+use middle::ty::{self, Ty};
 use middle::ty::fold::{TypeFoldable, TypeFolder};
 
+use serialize::{Encodable, Encoder, Decodable, Decoder};
 use std::fmt;
 use std::iter::IntoIterator;
 use std::slice::Iter;
@@ -153,6 +155,35 @@ impl<'tcx> Substs<'tcx> {
     }
 }
 
+impl<'tcx> Encodable for Substs<'tcx> {
+
+    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        cstore::tls::with_encoding_context(s, |ecx, rbml_w| {
+            ecx.encode_substs(rbml_w, self);
+            Ok(())
+        })
+    }
+}
+
+impl<'tcx> Decodable for Substs<'tcx> {
+    fn decode<D: Decoder>(d: &mut D) -> Result<Substs<'tcx>, D::Error> {
+        cstore::tls::with_decoding_context(d, |dcx, rbml_r| {
+            Ok(dcx.decode_substs(rbml_r))
+        })
+    }
+}
+
+impl<'tcx> Decodable for &'tcx Substs<'tcx> {
+    fn decode<D: Decoder>(d: &mut D) -> Result<&'tcx Substs<'tcx>, D::Error> {
+        let substs = cstore::tls::with_decoding_context(d, |dcx, rbml_r| {
+            let substs = dcx.decode_substs(rbml_r);
+            dcx.tcx().mk_substs(substs)
+        });
+
+        Ok(substs)
+    }
+}
+
 impl RegionSubsts {
     pub fn map<F>(self, op: F) -> RegionSubsts where
         F: FnOnce(VecPerParamSpace<ty::Region>) -> VecPerParamSpace<ty::Region>,
@@ -643,7 +674,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
                 self.ty_for_param(p, t)
             }
             _ => {
-                ty::fold::super_fold_ty(self, t)
+                t.super_fold_with(self)
             }
         };
 
index 76428ca6d9035e897119079fde2d716859e66b73..0f95aa74b6fd731f5f93720b23e09067219ffff5 100644 (file)
@@ -27,11 +27,12 @@ use syntax::codemap::{DUMMY_SP, Span};
 #[derive(Copy, Clone)]
 struct InferIsLocal(bool);
 
-/// True if there exist types that satisfy both of the two given impls.
-pub fn overlapping_impls(infcx: &InferCtxt,
-                         impl1_def_id: DefId,
-                         impl2_def_id: DefId)
-                         -> bool
+/// If there are types that satisfy both impls, returns a `TraitRef`
+/// with those types substituted (by updating the given `infcx`)
+pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>,
+                                    impl1_def_id: DefId,
+                                    impl2_def_id: DefId)
+                                    -> Option<ty::TraitRef<'tcx>>
 {
     debug!("impl_can_satisfy(\
            impl1_def_id={:?}, \
@@ -40,16 +41,15 @@ pub fn overlapping_impls(infcx: &InferCtxt,
            impl2_def_id);
 
     let selcx = &mut SelectionContext::intercrate(infcx);
-    infcx.probe(|_| {
-        overlap(selcx, impl1_def_id, impl2_def_id)
-    })
+    overlap(selcx, impl1_def_id, impl2_def_id)
 }
 
-/// Can both impl `a` and impl `b` be satisfied by a common type (including `where` clauses)?
-fn overlap(selcx: &mut SelectionContext,
-           a_def_id: DefId,
-           b_def_id: DefId)
-           -> bool
+/// Can both impl `a` and impl `b` be satisfied by a common type (including
+/// `where` clauses)? If so, returns a `TraitRef` that unifies the two impls.
+fn overlap<'cx, 'tcx>(selcx: &mut SelectionContext<'cx, 'tcx>,
+                      a_def_id: DefId,
+                      b_def_id: DefId)
+                      -> Option<ty::TraitRef<'tcx>>
 {
     debug!("overlap(a_def_id={:?}, b_def_id={:?})",
            a_def_id,
@@ -63,9 +63,9 @@ fn overlap(selcx: &mut SelectionContext,
                                                                 b_def_id,
                                                                 util::fresh_type_vars_for_impl);
 
-    debug!("overlap: a_trait_ref={:?}", a_trait_ref);
+    debug!("overlap: a_trait_ref={:?} a_obligations={:?}", a_trait_ref, a_obligations);
 
-    debug!("overlap: b_trait_ref={:?}", b_trait_ref);
+    debug!("overlap: b_trait_ref={:?} b_obligations={:?}", b_trait_ref, b_obligations);
 
     // Do `a` and `b` unify? If not, no overlap.
     if let Err(_) = infer::mk_eq_trait_refs(selcx.infcx(),
@@ -73,7 +73,7 @@ fn overlap(selcx: &mut SelectionContext,
                                             TypeOrigin::Misc(DUMMY_SP),
                                             a_trait_ref,
                                             b_trait_ref) {
-        return false;
+        return None;
     }
 
     debug!("overlap: unification check succeeded");
@@ -88,10 +88,10 @@ fn overlap(selcx: &mut SelectionContext,
 
     if let Some(failing_obligation) = opt_failing_obligation {
         debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
-        return false
+        return None
     }
 
-    true
+    Some(selcx.infcx().resolve_type_vars_if_possible(&a_trait_ref))
 }
 
 pub fn trait_ref_is_knowable<'tcx>(tcx: &ty::ctxt<'tcx>, trait_ref: &ty::TraitRef<'tcx>) -> bool
@@ -330,8 +330,11 @@ fn ty_is_local_constructor<'tcx>(tcx: &ty::ctxt<'tcx>,
             tt.principal_def_id().is_local()
         }
 
-        ty::TyClosure(..) |
         ty::TyError => {
+            true
+        }
+
+        ty::TyClosure(..) => {
             tcx.sess.bug(
                 &format!("ty_is_local invoked on unexpected type: {:?}",
                         ty))
index 0931c138e46f4107058e6df7ab788fac5045150f..883c5e7bb40eb2713f37ea37fb5afc5d3761e9fe 100644 (file)
@@ -26,17 +26,18 @@ use super::{
 use fmt_macros::{Parser, Piece, Position};
 use middle::def_id::DefId;
 use middle::infer::InferCtxt;
-use middle::ty::{self, ToPredicate, HasTypeFlags, ToPolyTraitRef, TraitRef, Ty};
-use middle::ty::fold::TypeFoldable;
+use middle::ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TypeFoldable};
+use middle::ty::fast_reject;
 use util::nodemap::{FnvHashMap, FnvHashSet};
 
+use std::cmp;
 use std::fmt;
-use syntax::codemap::Span;
 use syntax::attr::{AttributeMethods, AttrMetaMethods};
+use syntax::codemap::Span;
+use syntax::errors::DiagnosticBuilder;
 
 #[derive(Debug, PartialEq, Eq, Hash)]
 pub struct TraitErrorKey<'tcx> {
-    is_warning: bool,
     span: Span,
     predicate: ty::Predicate<'tcx>
 }
@@ -47,7 +48,6 @@ impl<'tcx> TraitErrorKey<'tcx> {
         let predicate =
             infcx.resolve_type_vars_if_possible(&e.obligation.predicate);
         TraitErrorKey {
-            is_warning: is_warning(&e.obligation),
             span: e.obligation.cause.span,
             predicate: infcx.tcx.erase_regions(&predicate)
         }
@@ -83,10 +83,6 @@ fn report_fulfillment_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
     }
 }
 
-fn is_warning<T>(obligation: &Obligation<T>) -> bool {
-    obligation.cause.code.is_rfc1214()
-}
-
 pub fn report_projection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                                          obligation: &PredicateObligation<'tcx>,
                                          error: &MismatchedProjectionTypes<'tcx>)
@@ -100,12 +96,12 @@ pub fn report_projection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
     // then $X will be unified with TyError, but the error still needs to be
     // reported.
     if !infcx.tcx.sess.has_errors() || !predicate.references_error() {
-        span_err_or_warn!(
-            is_warning(obligation), infcx.tcx.sess, obligation.cause.span, E0271,
+        let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0271,
             "type mismatch resolving `{}`: {}",
             predicate,
             error.err);
-        note_obligation_cause(infcx, obligation);
+        note_obligation_cause(infcx, &mut err, obligation);
+        err.emit();
     }
 }
 
@@ -188,18 +184,19 @@ fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
 pub fn report_overflow_error<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
                                           obligation: &Obligation<'tcx, T>)
                                           -> !
-    where T: fmt::Display + TypeFoldable<'tcx> + HasTypeFlags
+    where T: fmt::Display + TypeFoldable<'tcx>
 {
     let predicate =
         infcx.resolve_type_vars_if_possible(&obligation.predicate);
-    span_err!(infcx.tcx.sess, obligation.cause.span, E0275,
-              "overflow evaluating the requirement `{}`",
-              predicate);
+    let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0275,
+                                   "overflow evaluating the requirement `{}`",
+                                   predicate);
 
-    suggest_new_overflow_limit(infcx.tcx, obligation.cause.span);
+    suggest_new_overflow_limit(infcx.tcx, &mut err, obligation.cause.span);
 
-    note_obligation_cause(infcx, obligation);
+    note_obligation_cause(infcx, &mut err, obligation);
 
+    err.emit();
     infcx.tcx.sess.abort_if_errors();
     unreachable!();
 }
@@ -208,12 +205,11 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                                         obligation: &PredicateObligation<'tcx>,
                                         error: &SelectionError<'tcx>)
 {
-    let is_warning = is_warning(obligation);
     match *error {
         SelectionError::Unimplemented => {
             if let ObligationCauseCode::CompareImplMethodObligation = obligation.cause.code {
-                span_err_or_warn!(
-                    is_warning, infcx.tcx.sess, obligation.cause.span, E0276,
+                span_err!(
+                    infcx.tcx.sess, obligation.cause.span, E0276,
                     "the requirement `{}` appears on the impl \
                      method but not on the corresponding trait method",
                     obligation.predicate);
@@ -225,8 +221,8 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
 
                         if !infcx.tcx.sess.has_errors() || !trait_predicate.references_error() {
                             let trait_ref = trait_predicate.to_poly_trait_ref();
-                            span_err_or_warn!(
-                                is_warning, infcx.tcx.sess, obligation.cause.span, E0277,
+                            let mut err = struct_span_err!(
+                                infcx.tcx.sess, obligation.cause.span, E0277,
                                 "the trait `{}` is not implemented for the type `{}`",
                                 trait_ref, trait_ref.self_ty());
 
@@ -235,55 +231,100 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                             let custom_note = report_on_unimplemented(infcx, &trait_ref.0,
                                                                       obligation.cause.span);
                             if let Some(s) = custom_note {
-                                infcx.tcx.sess.fileline_note(obligation.cause.span, &s);
+                                err.fileline_note(obligation.cause.span, &s);
+                            } else {
+                                let simp = fast_reject::simplify_type(infcx.tcx,
+                                                                      trait_ref.self_ty(),
+                                                                      true);
+                                let mut impl_candidates = Vec::new();
+                                let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id());
+
+                                match simp {
+                                    Some(simp) => trait_def.for_each_impl(infcx.tcx, |def_id| {
+                                        let imp = infcx.tcx.impl_trait_ref(def_id).unwrap();
+                                        let imp_simp = fast_reject::simplify_type(infcx.tcx,
+                                                                                  imp.self_ty(),
+                                                                                  true);
+                                        if let Some(imp_simp) = imp_simp {
+                                            if simp != imp_simp {
+                                                return;
+                                            }
+                                        }
+                                        impl_candidates.push(imp);
+                                    }),
+                                    None => trait_def.for_each_impl(infcx.tcx, |def_id| {
+                                        impl_candidates.push(
+                                            infcx.tcx.impl_trait_ref(def_id).unwrap());
+                                    })
+                                };
+
+                                if impl_candidates.len() > 0 {
+                                    err.fileline_help(
+                                        obligation.cause.span,
+                                        &format!("the following implementations were found:"));
+
+                                    let end = cmp::min(4, impl_candidates.len());
+                                    for candidate in &impl_candidates[0..end] {
+                                        err.fileline_help(obligation.cause.span,
+                                                          &format!("  {:?}", candidate));
+                                    }
+                                    if impl_candidates.len() > 4 {
+                                        err.fileline_help(obligation.cause.span,
+                                                          &format!("and {} others",
+                                                                   impl_candidates.len()-4));
+                                    }
+                                }
                             }
-                            note_obligation_cause(infcx, obligation);
+                            note_obligation_cause(infcx, &mut err, obligation);
+                            err.emit();
                         }
-                    }
-
+                    },
                     ty::Predicate::Equate(ref predicate) => {
                         let predicate = infcx.resolve_type_vars_if_possible(predicate);
                         let err = infcx.equality_predicate(obligation.cause.span,
                                                            &predicate).err().unwrap();
-                        span_err_or_warn!(
-                            is_warning, infcx.tcx.sess, obligation.cause.span, E0278,
+                        let mut err = struct_span_err!(
+                            infcx.tcx.sess, obligation.cause.span, E0278,
                             "the requirement `{}` is not satisfied (`{}`)",
                             predicate,
                             err);
-                        note_obligation_cause(infcx, obligation);
+                        note_obligation_cause(infcx, &mut err, obligation);
+                        err.emit();
                     }
 
                     ty::Predicate::RegionOutlives(ref predicate) => {
                         let predicate = infcx.resolve_type_vars_if_possible(predicate);
                         let err = infcx.region_outlives_predicate(obligation.cause.span,
                                                                   &predicate).err().unwrap();
-                        span_err_or_warn!(
-                            is_warning, infcx.tcx.sess, obligation.cause.span, E0279,
+                        let mut err = struct_span_err!(
+                            infcx.tcx.sess, obligation.cause.span, E0279,
                             "the requirement `{}` is not satisfied (`{}`)",
                             predicate,
                             err);
-                        note_obligation_cause(infcx, obligation);
+                        note_obligation_cause(infcx, &mut err, obligation);
+                        err.emit();
                     }
 
                     ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => {
                         let predicate =
                             infcx.resolve_type_vars_if_possible(&obligation.predicate);
-                        span_err_or_warn!(
-                            is_warning, infcx.tcx.sess, obligation.cause.span, E0280,
+                        let mut err = struct_span_err!(
+                            infcx.tcx.sess, obligation.cause.span, E0280,
                             "the requirement `{}` is not satisfied",
                             predicate);
-                        note_obligation_cause(infcx, obligation);
+                        note_obligation_cause(infcx, &mut err, obligation);
+                        err.emit();
                     }
 
                     ty::Predicate::ObjectSafe(trait_def_id) => {
                         let violations = object_safety_violations(
                             infcx.tcx, trait_def_id);
-                        report_object_safety_error(infcx.tcx,
-                                                   obligation.cause.span,
-                                                   trait_def_id,
-                                                   violations,
-                                                   is_warning);
-                        note_obligation_cause(infcx, obligation);
+                        let mut err = report_object_safety_error(infcx.tcx,
+                                                                 obligation.cause.span,
+                                                                 trait_def_id,
+                                                                 violations);
+                        note_obligation_cause(infcx, &mut err, obligation);
+                        err.emit();
                     }
 
                     ty::Predicate::WellFormed(ty) => {
@@ -304,23 +345,25 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
             let expected_trait_ref = infcx.resolve_type_vars_if_possible(&*expected_trait_ref);
             let actual_trait_ref = infcx.resolve_type_vars_if_possible(&*actual_trait_ref);
             if !actual_trait_ref.self_ty().references_error() {
-                span_err_or_warn!(
-                    is_warning, infcx.tcx.sess, obligation.cause.span, E0281,
+                let mut err = struct_span_err!(
+                    infcx.tcx.sess, obligation.cause.span, E0281,
                     "type mismatch: the type `{}` implements the trait `{}`, \
                      but the trait `{}` is required ({})",
                     expected_trait_ref.self_ty(),
                     expected_trait_ref,
                     actual_trait_ref,
                     e);
-                note_obligation_cause(infcx, obligation);
+                note_obligation_cause(infcx, &mut err, obligation);
+                err.emit();
             }
         }
 
         TraitNotObjectSafe(did) => {
             let violations = object_safety_violations(infcx.tcx, did);
-            report_object_safety_error(infcx.tcx, obligation.cause.span, did,
-                                       violations, is_warning);
-            note_obligation_cause(infcx, obligation);
+            let mut err = report_object_safety_error(infcx.tcx, obligation.cause.span, did,
+                                                     violations);
+            note_obligation_cause(infcx, &mut err, obligation);
+            err.emit();
         }
     }
 }
@@ -328,11 +371,11 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
 pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>,
                                         span: Span,
                                         trait_def_id: DefId,
-                                        violations: Vec<ObjectSafetyViolation>,
-                                        is_warning: bool)
+                                        violations: Vec<ObjectSafetyViolation>)
+                                        -> DiagnosticBuilder<'tcx>
 {
-    span_err_or_warn!(
-        is_warning, tcx.sess, span, E0038,
+    let mut err = struct_span_err!(
+        tcx.sess, span, E0038,
         "the trait `{}` cannot be made into an object",
         tcx.item_path_str(trait_def_id));
 
@@ -343,13 +386,13 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>,
         }
         match violation {
             ObjectSafetyViolation::SizedSelf => {
-                tcx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     "the trait cannot require that `Self : Sized`");
             }
 
             ObjectSafetyViolation::SupertraitSelf => {
-                tcx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     "the trait cannot use `Self` as a type parameter \
                      in the supertrait listing");
@@ -357,7 +400,7 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>,
 
             ObjectSafetyViolation::Method(method,
                                           MethodViolationCode::StaticMethod) => {
-                tcx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     &format!("method `{}` has no receiver",
                              method.name));
@@ -365,7 +408,7 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>,
 
             ObjectSafetyViolation::Method(method,
                                           MethodViolationCode::ReferencesSelf) => {
-                tcx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     &format!("method `{}` references the `Self` type \
                               in its arguments or return type",
@@ -374,13 +417,14 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>,
 
             ObjectSafetyViolation::Method(method,
                                           MethodViolationCode::Generic) => {
-                tcx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     &format!("method `{}` has generic type parameters",
                              method.name));
             }
         }
     }
+    err
 }
 
 pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
@@ -402,7 +446,17 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
             let self_ty = trait_ref.self_ty();
             let all_types = &trait_ref.substs().types;
             if all_types.references_error() {
-            } else if all_types.needs_infer() {
+            } else {
+                // Typically, this ambiguity should only happen if
+                // there are unresolved type inference variables
+                // (otherwise it would suggest a coherence
+                // failure). But given #21974 that is not necessarily
+                // the case -- we can have multiple where clauses that
+                // are only distinguished by a region, which results
+                // in an ambiguity even when all types are fully
+                // known, since we don't dispatch based on region
+                // relationships.
+
                 // This is kind of a hack: it frequently happens that some earlier
                 // error prevents types from being fully inferred, and then we get
                 // a bunch of uninteresting errors saying something like "<generic
@@ -424,22 +478,14 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                     {
                         need_type_info(infcx, obligation.cause.span, self_ty);
                     } else {
-                        span_err!(infcx.tcx.sess, obligation.cause.span, E0283,
-                                "type annotations required: cannot resolve `{}`",
-                                predicate);
-                        note_obligation_cause(infcx, obligation);
+                        let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0283,
+                                                       "type annotations required: \
+                                                        cannot resolve `{}`",
+                                                       predicate);
+                        note_obligation_cause(infcx, &mut err, obligation);
+                        err.emit();
                     }
                 }
-            } else if !infcx.tcx.sess.has_errors() {
-                // Ambiguity. Coherence should have reported an error.
-                infcx.tcx.sess.span_bug(
-                    obligation.cause.span,
-                    &format!(
-                        "coherence failed to report ambiguity: \
-                         cannot locate the impl of the trait `{}` for \
-                         the type `{}`",
-                        trait_ref,
-                        self_ty));
             }
         }
 
@@ -453,10 +499,11 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
 
         _ => {
             if !infcx.tcx.sess.has_errors() {
-                span_err!(infcx.tcx.sess, obligation.cause.span, E0284,
-                        "type annotations required: cannot resolve `{}`",
-                        predicate);
-                note_obligation_cause(infcx, obligation);
+                let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0284,
+                                               "type annotations required: cannot resolve `{}`",
+                                               predicate);
+                note_obligation_cause(infcx, &mut err, obligation);
+                err.emit();
             }
         }
     }
@@ -473,16 +520,19 @@ fn need_type_info<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
 }
 
 fn note_obligation_cause<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
+                                      err: &mut DiagnosticBuilder,
                                       obligation: &Obligation<'tcx, T>)
     where T: fmt::Display
 {
     note_obligation_cause_code(infcx,
+                               err,
                                &obligation.predicate,
                                obligation.cause.span,
                                &obligation.cause.code);
 }
 
 fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
+                                           err: &mut DiagnosticBuilder,
                                            predicate: &T,
                                            cause_span: Span,
                                            cause_code: &ObligationCauseCode<'tcx>)
@@ -491,64 +541,60 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
     let tcx = infcx.tcx;
     match *cause_code {
         ObligationCauseCode::MiscObligation => { }
-        ObligationCauseCode::RFC1214(ref subcode) => {
-            tcx.sess.note_rfc_1214(cause_span);
-            note_obligation_cause_code(infcx, predicate, cause_span, subcode);
-        }
         ObligationCauseCode::SliceOrArrayElem => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "slice and array elements must have `Sized` type");
         }
         ObligationCauseCode::ProjectionWf(data) => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("required so that the projection `{}` is well-formed",
                          data));
         }
         ObligationCauseCode::ReferenceOutlivesReferent(ref_ty) => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("required so that reference `{}` does not outlive its referent",
                          ref_ty));
         }
         ObligationCauseCode::ItemObligation(item_def_id) => {
             let item_name = tcx.item_path_str(item_def_id);
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("required by `{}`", item_name));
         }
         ObligationCauseCode::ObjectCastObligation(object_ty) => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!(
                     "required for the cast to the object type `{}`",
                     infcx.ty_to_string(object_ty)));
         }
         ObligationCauseCode::RepeatVec => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "the `Copy` trait is required because the \
                  repeated element will be copied");
         }
         ObligationCauseCode::VariableType(_) => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "all local variables must have a statically known size");
         }
         ObligationCauseCode::ReturnType => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "the return type of a function must have a \
                  statically known size");
         }
         ObligationCauseCode::AssignmentLhsSized => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "the left-hand-side of an assignment must have a statically known size");
         }
         ObligationCauseCode::StructInitializerSized => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "structs must have a statically known size to be initialized");
         }
@@ -556,7 +602,7 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
             let def_id = tcx.lang_items.from_builtin_kind(builtin_bound).unwrap();
             let trait_name = tcx.item_path_str(def_id);
             let name = tcx.local_var_name_str(var_id);
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("the closure that captures `{}` requires that all captured variables \
                           implement the trait `{}`",
@@ -564,37 +610,45 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
                          trait_name));
         }
         ObligationCauseCode::FieldSized => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "only the last field of a struct or enum variant \
                  may have a dynamically sized type");
         }
         ObligationCauseCode::SharedStatic => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "shared static variables must have a type that implements `Sync`");
         }
         ObligationCauseCode::BuiltinDerivedObligation(ref data) => {
             let parent_trait_ref = infcx.resolve_type_vars_if_possible(&data.parent_trait_ref);
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("required because it appears within the type `{}`",
                          parent_trait_ref.0.self_ty()));
             let parent_predicate = parent_trait_ref.to_predicate();
-            note_obligation_cause_code(infcx, &parent_predicate, cause_span, &*data.parent_code);
+            note_obligation_cause_code(infcx,
+                                       err,
+                                       &parent_predicate,
+                                       cause_span,
+                                       &*data.parent_code);
         }
         ObligationCauseCode::ImplDerivedObligation(ref data) => {
             let parent_trait_ref = infcx.resolve_type_vars_if_possible(&data.parent_trait_ref);
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("required because of the requirements on the impl of `{}` for `{}`",
                          parent_trait_ref,
                          parent_trait_ref.0.self_ty()));
             let parent_predicate = parent_trait_ref.to_predicate();
-            note_obligation_cause_code(infcx, &parent_predicate, cause_span, &*data.parent_code);
+            note_obligation_cause_code(infcx,
+                                       err,
+                                       &parent_predicate,
+                                       cause_span,
+                                       &*data.parent_code);
         }
         ObligationCauseCode::CompareImplMethodObligation => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("the requirement `{}` appears on the impl method \
                           but not on the corresponding trait method",
@@ -603,10 +657,10 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
     }
 }
 
-fn suggest_new_overflow_limit(tcx: &ty::ctxt, span: Span) {
+fn suggest_new_overflow_limit(tcx: &ty::ctxt, err:&mut DiagnosticBuilder, span: Span) {
     let current_limit = tcx.sess.recursion_limit.get();
     let suggested_limit = current_limit * 2;
-    tcx.sess.fileline_note(
+    err.fileline_note(
         span,
         &format!(
             "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
index d4e6f693d965d79c7e008948975e912924bdaca1..4f8f6b846a6f419d272526b7ea20719c608fa8a5 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use middle::infer::InferCtxt;
-use middle::ty::{self, RegionEscape, Ty, HasTypeFlags};
+use middle::ty::{self, Ty, TypeFoldable};
 
 use syntax::ast;
 use util::common::ErrorReported;
@@ -21,16 +21,14 @@ use super::CodeSelectionError;
 use super::is_object_safe;
 use super::FulfillmentError;
 use super::ObligationCause;
-use super::ObligationCauseCode;
 use super::PredicateObligation;
 use super::project;
-use super::RFC1214Warning;
 use super::select::SelectionContext;
 use super::Unimplemented;
 use super::util::predicate_for_builtin_bound;
 
 pub struct FulfilledPredicates<'tcx> {
-    set: FnvHashSet<(RFC1214Warning, ty::Predicate<'tcx>)>
+    set: FnvHashSet<ty::Predicate<'tcx>>
 }
 
 /// The fulfillment context is used to drive trait resolution.  It
@@ -194,9 +192,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
 
         assert!(!obligation.has_escaping_regions());
 
-        let w = RFC1214Warning(obligation.cause.code.is_rfc1214());
-
-        if self.is_duplicate_or_add(infcx.tcx, w, &obligation.predicate) {
+        if self.is_duplicate_or_add(infcx.tcx, &obligation.predicate) {
             debug!("register_predicate({:?}) -- already seen, skip", obligation);
             return;
         }
@@ -261,7 +257,6 @@ impl<'tcx> FulfillmentContext<'tcx> {
 
     fn is_duplicate_or_add(&mut self,
                            tcx: &ty::ctxt<'tcx>,
-                           w: RFC1214Warning,
                            predicate: &ty::Predicate<'tcx>)
                            -> bool {
         // This is a kind of dirty hack to allow us to avoid "rederiving"
@@ -276,12 +271,10 @@ impl<'tcx> FulfillmentContext<'tcx> {
         // evaluating the 'nested obligations'.  This cache lets us
         // skip those.
 
-        let will_warn_due_to_rfc1214 = w.0;
-        let errors_will_be_reported = self.errors_will_be_reported && !will_warn_due_to_rfc1214;
-        if errors_will_be_reported && predicate.is_global() {
-            tcx.fulfilled_predicates.borrow_mut().is_duplicate_or_add(w, predicate)
+        if self.errors_will_be_reported && predicate.is_global() {
+            tcx.fulfilled_predicates.borrow_mut().is_duplicate_or_add(predicate)
         } else {
-            self.duplicate_set.is_duplicate_or_add(w, predicate)
+            self.duplicate_set.is_duplicate_or_add(predicate)
         }
     }
 
@@ -496,12 +489,8 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
         }
 
         ty::Predicate::WellFormed(ty) => {
-            let rfc1214 = match obligation.cause.code {
-                ObligationCauseCode::RFC1214(_) => true,
-                _ => false,
-            };
             match ty::wf::obligations(selcx.infcx(), obligation.cause.body_id,
-                                      ty, obligation.cause.span, rfc1214) {
+                                      ty, obligation.cause.span) {
                 Some(obligations) => {
                     new_obligations.extend(obligations);
                     true
@@ -539,13 +528,11 @@ impl<'tcx> FulfilledPredicates<'tcx> {
         }
     }
 
-    pub fn is_duplicate(&self, w: RFC1214Warning, p: &ty::Predicate<'tcx>) -> bool {
-        let key = (w, p.clone());
-        self.set.contains(&key)
+    pub fn is_duplicate(&self, key: &ty::Predicate<'tcx>) -> bool {
+        self.set.contains(key)
     }
 
-    fn is_duplicate_or_add(&mut self, w: RFC1214Warning, p: &ty::Predicate<'tcx>) -> bool {
-        let key = (w, p.clone());
-        !self.set.insert(key)
+    fn is_duplicate_or_add(&mut self, key: &ty::Predicate<'tcx>) -> bool {
+        !self.set.insert(key.clone())
     }
 }
index 691bac0cef865f82c6cde00f92074961e1018226..6cf841cc47756557c91f67111dd2bf8686d212dc 100644 (file)
@@ -15,11 +15,12 @@ pub use self::FulfillmentErrorCode::*;
 pub use self::Vtable::*;
 pub use self::ObligationCauseCode::*;
 
+use dep_graph::DepNode;
 use middle::def_id::DefId;
 use middle::free_region::FreeRegionMap;
 use middle::subst;
-use middle::ty::{self, HasTypeFlags, Ty};
-use middle::ty::fold::TypeFoldable;
+use middle::ty::{self, Ty, TypeFoldable};
+use middle::ty::fast_reject;
 use middle::infer::{self, fixup_err_to_string, InferCtxt};
 
 use std::rc::Rc;
@@ -106,9 +107,6 @@ pub enum ObligationCauseCode<'tcx> {
     /// Not well classified or should be obvious from span.
     MiscObligation,
 
-    /// Obligation that triggers warning until RFC 1214 is fully in place.
-    RFC1214(Rc<ObligationCauseCode<'tcx>>),
-
     /// This is the trait reference from the given projection
     SliceOrArrayElem,
 
@@ -471,7 +469,7 @@ pub fn fully_normalize<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
                                   cause: ObligationCause<'tcx>,
                                   value: &T)
                                   -> Result<T, Vec<FulfillmentError<'tcx>>>
-    where T : TypeFoldable<'tcx> + HasTypeFlags
+    where T : TypeFoldable<'tcx>
 {
     debug!("normalize_param_env(value={:?})", value);
 
@@ -554,24 +552,6 @@ impl<'tcx> ObligationCause<'tcx> {
     }
 }
 
-/// This marker is used in some caches to record whether the
-/// predicate, if it is found to be false, will yield a warning (due
-/// to RFC1214) or an error. We separate these two cases in the cache
-/// so that if we see the same predicate twice, first resulting in a
-/// warning, and next resulting in an error, we still report the
-/// error, rather than considering it a duplicate.
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub struct RFC1214Warning(bool);
-
-impl<'tcx> ObligationCauseCode<'tcx> {
-    pub fn is_rfc1214(&self) -> bool {
-        match *self {
-            ObligationCauseCode::RFC1214(..) => true,
-            _ => false,
-        }
-    }
-}
-
 impl<'tcx, N> Vtable<'tcx, N> {
     pub fn nested_obligations(self) -> Vec<N> {
         match self {
@@ -620,6 +600,18 @@ impl<'tcx> FulfillmentError<'tcx> {
 }
 
 impl<'tcx> TraitObligation<'tcx> {
+    /// Creates the dep-node for selecting/evaluating this trait reference.
+    fn dep_node(&self, tcx: &ty::ctxt<'tcx>) -> DepNode {
+        let simplified_ty =
+            fast_reject::simplify_type(tcx,
+                                       self.predicate.skip_binder().self_ty(), // (*)
+                                       true);
+
+        // (*) skip_binder is ok because `simplify_type` doesn't care about regions
+
+        DepNode::TraitSelect(self.predicate.def_id(), simplified_ty)
+    }
+
     fn self_ty(&self) -> ty::Binder<Ty<'tcx>> {
         ty::Binder(self.predicate.skip_binder().self_ty())
     }
index 934c35fa20bc8c1e7eb90f12725e062991dc5c2c..0e4a42bd15134050e47e9b39bcffe3b9e272e96e 100644 (file)
@@ -23,7 +23,7 @@ use super::elaborate_predicates;
 use middle::def_id::DefId;
 use middle::subst::{self, SelfSpace, TypeSpace};
 use middle::traits;
-use middle::ty::{self, HasTypeFlags, ToPolyTraitRef, Ty};
+use middle::ty::{self, ToPolyTraitRef, Ty, TypeFoldable};
 use std::rc::Rc;
 use syntax::ast;
 
@@ -192,7 +192,8 @@ fn generics_require_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>,
     };
 
     // Search for a predicate like `Self : Sized` amongst the trait bounds.
-    let free_substs = tcx.construct_free_substs(generics, ast::DUMMY_NODE_ID);
+    let free_substs = tcx.construct_free_substs(generics,
+                                                tcx.region_maps.node_extent(ast::DUMMY_NODE_ID));
     let predicates = predicates.instantiate(tcx, &free_substs).predicates.into_vec();
     elaborate_predicates(tcx, predicates)
         .any(|predicate| {
@@ -253,13 +254,13 @@ fn virtual_call_violation_for_method<'tcx>(tcx: &ty::ctxt<'tcx>,
     // autorefs) to `&self`. For now, we only accept `self`, `&self`
     // and `Box<Self>`.
     match method.explicit_self {
-        ty::StaticExplicitSelfCategory => {
+        ty::ExplicitSelfCategory::Static => {
             return Some(MethodViolationCode::StaticMethod);
         }
 
-        ty::ByValueExplicitSelfCategory |
-        ty::ByReferenceExplicitSelfCategory(..) |
-        ty::ByBoxExplicitSelfCategory => {
+        ty::ExplicitSelfCategory::ByValue |
+        ty::ExplicitSelfCategory::ByReference(..) |
+        ty::ExplicitSelfCategory::ByBox => {
         }
     }
 
index 28910ab1a044bc7fc487062a94539291110f67c3..e9d7b330d07acc17f15bbc29f093e63dd3812872 100644 (file)
@@ -23,7 +23,7 @@ use super::util;
 
 use middle::infer::{self, TypeOrigin};
 use middle::subst::Subst;
-use middle::ty::{self, ToPredicate, RegionEscape, HasTypeFlags, ToPolyTraitRef, Ty};
+use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty};
 use middle::ty::fold::{TypeFoldable, TypeFolder};
 use syntax::parse::token;
 use util::common::FN_OUTPUT_NAME;
@@ -202,7 +202,7 @@ pub fn normalize<'a,'b,'tcx,T>(selcx: &'a mut SelectionContext<'b,'tcx>,
                                cause: ObligationCause<'tcx>,
                                value: &T)
                                -> Normalized<'tcx, T>
-    where T : TypeFoldable<'tcx> + HasTypeFlags
+    where T : TypeFoldable<'tcx>
 {
     normalize_with_depth(selcx, cause, 0, value)
 }
@@ -213,7 +213,7 @@ pub fn normalize_with_depth<'a,'b,'tcx,T>(selcx: &'a mut SelectionContext<'b,'tc
                                           depth: usize,
                                           value: &T)
                                           -> Normalized<'tcx, T>
-    where T : TypeFoldable<'tcx> + HasTypeFlags
+    where T : TypeFoldable<'tcx>
 {
     let mut normalizer = AssociatedTypeNormalizer::new(selcx, cause, depth);
     let result = normalizer.fold(value);
@@ -245,7 +245,7 @@ impl<'a,'b,'tcx> AssociatedTypeNormalizer<'a,'b,'tcx> {
         }
     }
 
-    fn fold<T:TypeFoldable<'tcx> + HasTypeFlags>(&mut self, value: &T) -> T {
+    fn fold<T:TypeFoldable<'tcx>>(&mut self, value: &T) -> T {
         let value = self.selcx.infcx().resolve_type_vars_if_possible(value);
 
         if !value.has_projection_types() {
@@ -273,7 +273,7 @@ impl<'a,'b,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'b,'tcx> {
         // normalize it when we instantiate those bound regions (which
         // should occur eventually).
 
-        let ty = ty::fold::super_fold_ty(self, ty);
+        let ty = ty.super_fold_with(self);
         match ty.sty {
             ty::TyProjection(ref data) if !data.has_escaping_regions() => { // (*)
 
index 0b0f6c0b998fc584dfe5cb6fe3ae0d9f7682a521..f6d0da904a40f0bd15b197c854a0777da08aeaff 100644 (file)
@@ -26,7 +26,6 @@ use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation
 use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch};
 use super::{ObjectCastObligation, Obligation};
 use super::TraitNotObjectSafe;
-use super::RFC1214Warning;
 use super::Selection;
 use super::SelectionResult;
 use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure,
@@ -40,9 +39,8 @@ use middle::def_id::DefId;
 use middle::infer;
 use middle::infer::{InferCtxt, TypeFreshener, TypeOrigin};
 use middle::subst::{Subst, Substs, TypeSpace};
-use middle::ty::{self, ToPredicate, RegionEscape, ToPolyTraitRef, Ty, HasTypeFlags};
+use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TypeFoldable};
 use middle::ty::fast_reject;
-use middle::ty::fold::TypeFoldable;
 use middle::ty::relate::TypeRelation;
 
 use std::cell::RefCell;
@@ -212,8 +210,6 @@ enum SelectionCandidate<'tcx> {
     BuiltinObjectCandidate,
 
     BuiltinUnsizeCandidate,
-
-    ErrorCandidate,
 }
 
 struct SelectionCandidateSet<'tcx> {
@@ -311,6 +307,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         debug!("select({:?})", obligation);
         assert!(!obligation.predicate.has_escaping_regions());
 
+        let dep_node = obligation.dep_node(self.tcx());
+        let _task = self.tcx().dep_graph.in_task(dep_node);
+
         let stack = self.push_stack(TraitObligationStackList::empty(), obligation);
         match try!(self.candidate_from_obligation(&stack)) {
             None => {
@@ -412,7 +411,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     /// accurate if inference variables are involved.
     pub fn evaluate_obligation_conservatively(&mut self,
                                               obligation: &PredicateObligation<'tcx>)
-                               -> bool
+                                              -> bool
     {
         debug!("evaluate_obligation_conservatively({:?})",
                obligation);
@@ -463,8 +462,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // have been proven elsewhere. This cache only contains
         // predicates that are global in scope and hence unaffected by
         // the current environment.
-        let w = RFC1214Warning(false);
-        if self.tcx().fulfilled_predicates.borrow().is_duplicate(w, &obligation.predicate) {
+        if self.tcx().fulfilled_predicates.borrow().is_duplicate(&obligation.predicate) {
             return EvaluatedToOk;
         }
 
@@ -485,8 +483,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
             ty::Predicate::WellFormed(ty) => {
                 match ty::wf::obligations(self.infcx, obligation.cause.body_id,
-                                          ty, obligation.cause.span,
-                                          obligation.cause.code.is_rfc1214()) {
+                                          ty, obligation.cause.span) {
                     Some(obligations) =>
                         self.evaluate_predicates_recursively(previous_stack, obligations.iter()),
                     None =>
@@ -754,8 +751,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                                               stack: &TraitObligationStack<'o, 'tcx>)
                                               -> SelectionResult<'tcx, SelectionCandidate<'tcx>>
     {
-        if stack.obligation.predicate.0.self_ty().references_error() {
-            return Ok(Some(ErrorCandidate));
+        if stack.obligation.predicate.references_error() {
+            // If we encounter a `TyError`, we generally prefer the
+            // most "optimistic" result in response -- that is, the
+            // one least likely to report downstream errors. But
+            // because this routine is shared by coherence and by
+            // trait selection, there isn't an obvious "right" choice
+            // here in that respect, so we opt to just return
+            // ambiguity and let the upstream clients sort it out.
+            return Ok(None);
         }
 
         if !self.is_knowable(stack) {
@@ -965,7 +969,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         match *candidate {
             Ok(Some(_)) | Err(_) => true,
             Ok(None) => {
-                cache_fresh_trait_pred.0.input_types().has_infer_types()
+                cache_fresh_trait_pred.0.trait_ref.substs.types.has_infer_types()
             }
         }
     }
@@ -1588,7 +1592,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     true
                 },
                 &ParamCandidate(..) => false,
-                &ErrorCandidate => false // propagate errors
             },
             _ => false
         }
@@ -1999,10 +2002,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     try!(self.confirm_builtin_candidate(obligation, builtin_bound))))
             }
 
-            ErrorCandidate => {
-                Ok(VtableBuiltin(VtableBuiltinData { nested: vec![] }))
-            }
-
             ParamCandidate(param) => {
                 let obligations = self.confirm_param_candidate(obligation, param);
                 Ok(VtableParam(obligations))
@@ -2906,22 +2905,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // chain. Ideally, we should have a way to configure this either
         // by using -Z verbose or just a CLI argument.
         if obligation.recursion_depth >= 0 {
-            let derived_code = match obligation.cause.code {
-                ObligationCauseCode::RFC1214(ref base_code) => {
-                    let derived_cause = DerivedObligationCause {
-                        parent_trait_ref: obligation.predicate.to_poly_trait_ref(),
-                        parent_code: base_code.clone(),
-                    };
-                    ObligationCauseCode::RFC1214(Rc::new(variant(derived_cause)))
-                }
-                _ => {
-                    let derived_cause = DerivedObligationCause {
-                        parent_trait_ref: obligation.predicate.to_poly_trait_ref(),
-                        parent_code: Rc::new(obligation.cause.code.clone())
-                    };
-                    variant(derived_cause)
-                }
+            let derived_cause = DerivedObligationCause {
+                parent_trait_ref: obligation.predicate.to_poly_trait_ref(),
+                parent_code: Rc::new(obligation.cause.code.clone())
             };
+            let derived_code = variant(derived_cause);
             ObligationCause::new(obligation.cause.span, obligation.cause.body_id, derived_code)
         } else {
             obligation.cause.clone()
index 8ce211120019d60c053b1425bc825c95897125cb..453420e2a54dccfcd2e20a062fd4dcdcf48160bc 100644 (file)
@@ -10,8 +10,7 @@
 
 use middle::traits;
 use middle::traits::project::Normalized;
-use middle::ty::{HasTypeFlags, TypeFlags, RegionEscape};
-use middle::ty::fold::{TypeFoldable, TypeFolder};
+use middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
 
 use std::fmt;
 
@@ -131,130 +130,89 @@ impl<'tcx> fmt::Debug for traits::MismatchedProjectionTypes<'tcx> {
     }
 }
 
-impl<'tcx, P: RegionEscape> RegionEscape for traits::Obligation<'tcx,P> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.predicate.has_regions_escaping_depth(depth)
-    }
-}
-
-impl<'tcx, T: HasTypeFlags> HasTypeFlags for traits::Obligation<'tcx, T> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.predicate.has_type_flags(flags)
-    }
-}
-
-impl<'tcx, T: HasTypeFlags> HasTypeFlags for Normalized<'tcx, T> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.value.has_type_flags(flags) ||
-            self.obligations.has_type_flags(flags)
-    }
-}
-
-impl<'tcx, N: HasTypeFlags> HasTypeFlags for traits::VtableImplData<'tcx, N> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.substs.has_type_flags(flags) ||
-            self.nested.has_type_flags(flags)
-    }
-}
-
-impl<'tcx, N: HasTypeFlags> HasTypeFlags for traits::VtableClosureData<'tcx, N> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.substs.has_type_flags(flags) ||
-            self.nested.has_type_flags(flags)
-    }
-}
-
-impl<'tcx, N: HasTypeFlags> HasTypeFlags for traits::VtableDefaultImplData<N> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.nested.has_type_flags(flags)
-    }
-}
-
-impl<'tcx, N: HasTypeFlags> HasTypeFlags for traits::VtableBuiltinData<N> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.nested.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for traits::VtableObjectData<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.upcast_trait_ref.has_type_flags(flags)
-    }
-}
-
-impl<'tcx, N: HasTypeFlags> HasTypeFlags for traits::Vtable<'tcx, N> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        match *self {
-            traits::VtableImpl(ref v) => v.has_type_flags(flags),
-            traits::VtableDefaultImpl(ref t) => t.has_type_flags(flags),
-            traits::VtableClosure(ref d) => d.has_type_flags(flags),
-            traits::VtableFnPointer(ref d) => d.has_type_flags(flags),
-            traits::VtableParam(ref n) => n.has_type_flags(flags),
-            traits::VtableBuiltin(ref d) => d.has_type_flags(flags),
-            traits::VtableObject(ref d) => d.has_type_flags(flags)
-        }
-    }
-}
-
 impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx, O>
 {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::Obligation<'tcx, O> {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         traits::Obligation {
             cause: self.cause.clone(),
             recursion_depth: self.recursion_depth,
             predicate: self.predicate.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.predicate.visit_with(visitor)
+    }
 }
 
 impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableImplData<'tcx, N> {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableImplData<'tcx, N> {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         traits::VtableImplData {
             impl_def_id: self.impl_def_id,
             substs: self.substs.fold_with(folder),
             nested: self.nested.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.substs.visit_with(visitor) || self.nested.visit_with(visitor)
+    }
 }
 
 impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableClosureData<'tcx, N> {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableClosureData<'tcx, N> {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         traits::VtableClosureData {
             closure_def_id: self.closure_def_id,
             substs: self.substs.fold_with(folder),
             nested: self.nested.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.substs.visit_with(visitor) || self.nested.visit_with(visitor)
+    }
 }
 
 impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableDefaultImplData<N> {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableDefaultImplData<N> {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         traits::VtableDefaultImplData {
             trait_def_id: self.trait_def_id,
             nested: self.nested.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.nested.visit_with(visitor)
+    }
 }
 
 impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableBuiltinData<N> {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableBuiltinData<N> {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         traits::VtableBuiltinData {
             nested: self.nested.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.nested.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for traits::VtableObjectData<'tcx> {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableObjectData<'tcx> {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         traits::VtableObjectData {
             upcast_trait_ref: self.upcast_trait_ref.fold_with(folder),
             vtable_base: self.vtable_base
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.upcast_trait_ref.visit_with(visitor)
+    }
 }
 
 impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::Vtable<'tcx, N> {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         match *self {
             traits::VtableImpl(ref v) => traits::VtableImpl(v.fold_with(folder)),
             traits::VtableDefaultImpl(ref t) => traits::VtableDefaultImpl(t.fold_with(folder)),
@@ -269,13 +227,29 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N>
             traits::VtableObject(ref d) => traits::VtableObject(d.fold_with(folder)),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        match *self {
+            traits::VtableImpl(ref v) => v.visit_with(visitor),
+            traits::VtableDefaultImpl(ref t) => t.visit_with(visitor),
+            traits::VtableClosure(ref d) => d.visit_with(visitor),
+            traits::VtableFnPointer(ref d) => d.visit_with(visitor),
+            traits::VtableParam(ref n) => n.visit_with(visitor),
+            traits::VtableBuiltin(ref d) => d.visit_with(visitor),
+            traits::VtableObject(ref d) => d.visit_with(visitor),
+        }
+    }
 }
 
 impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Normalized<'tcx, T> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Normalized<'tcx, T> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         Normalized {
             value: self.value.fold_with(folder),
             obligations: self.obligations.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.value.visit_with(visitor) || self.obligations.visit_with(visitor)
+    }
 }
index c44ebcfdb693c36c87f2cdfd94c742af11a4ba53..6cab0baa55325ecebc5adf11ced7205208edbabb 100644 (file)
@@ -11,8 +11,7 @@
 pub use self::AutoAdjustment::*;
 pub use self::AutoRef::*;
 
-use middle::ty::{self, Ty, TypeAndMut};
-use middle::ty::HasTypeFlags;
+use middle::ty::{self, Ty, TypeAndMut, TypeFoldable};
 use middle::ty::LvaluePreference::{NoPreference};
 
 use syntax::ast;
index afe88f70d945055a4ed325a317c18bc919489345..619201a4a9feb016742b05116520c29ca9974f7d 100644 (file)
@@ -10,7 +10,7 @@
 
 use middle::def_id::{DefId};
 use middle::ty::{self, Ty};
-use util::common::{memoized};
+use util::common::MemoizationMap;
 use util::nodemap::FnvHashMap;
 
 use std::fmt;
@@ -141,9 +141,7 @@ impl fmt::Debug for TypeContents {
 
 impl<'tcx> ty::TyS<'tcx> {
     pub fn type_contents(&'tcx self, cx: &ty::ctxt<'tcx>) -> TypeContents {
-        return memoized(&cx.tc_cache, self, |ty| {
-            tc_ty(cx, ty, &mut FnvHashMap())
-        });
+        return cx.tc_cache.memoize(self, || tc_ty(cx, self, &mut FnvHashMap()));
 
         fn tc_ty<'tcx>(cx: &ty::ctxt<'tcx>,
                        ty: Ty<'tcx>,
index cee651743ca866d08cb3120cc8b560f1a761da6a..d1504d25288a8d93c4db24aee8e3ccdd5ed74006 100644 (file)
@@ -13,6 +13,7 @@
 // FIXME: (@jroesch) @eddyb should remove this when he renames ctxt
 #![allow(non_camel_case_types)]
 
+use dep_graph::{DepGraph, DepTrackingMap};
 use front::map as ast_map;
 use session::Session;
 use lint;
@@ -29,10 +30,12 @@ use middle::traits;
 use middle::ty::{self, TraitRef, Ty, TypeAndMut};
 use middle::ty::{TyS, TypeVariants};
 use middle::ty::{AdtDef, ClosureSubsts, ExistentialBounds, Region};
-use middle::ty::{FreevarMap, GenericPredicates};
+use middle::ty::{FreevarMap};
 use middle::ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, TraitTy};
 use middle::ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid};
 use middle::ty::TypeVariants::*;
+use middle::ty::maps;
+use util::common::MemoizationMap;
 use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet};
 use util::nodemap::FnvHashMap;
 
@@ -224,6 +227,8 @@ pub struct ctxt<'tcx> {
     region_interner: RefCell<FnvHashMap<&'tcx Region, &'tcx Region>>,
     stability_interner: RefCell<FnvHashMap<&'tcx attr::Stability, &'tcx attr::Stability>>,
 
+    pub dep_graph: DepGraph,
+
     /// Common types, pre-interned for your convenience.
     pub types: CommonTypes<'tcx>,
 
@@ -245,21 +250,22 @@ pub struct ctxt<'tcx> {
     pub tables: RefCell<Tables<'tcx>>,
 
     /// Maps from a trait item to the trait item "descriptor"
-    pub impl_or_trait_items: RefCell<DefIdMap<ty::ImplOrTraitItem<'tcx>>>,
+    pub impl_or_trait_items: RefCell<DepTrackingMap<maps::ImplOrTraitItems<'tcx>>>,
 
     /// Maps from a trait def-id to a list of the def-ids of its trait items
-    pub trait_item_def_ids: RefCell<DefIdMap<Rc<Vec<ty::ImplOrTraitItemId>>>>,
+    pub trait_item_def_ids: RefCell<DepTrackingMap<maps::TraitItemDefIds<'tcx>>>,
 
-    /// A cache for the trait_items() routine
-    pub trait_items_cache: RefCell<DefIdMap<Rc<Vec<ty::ImplOrTraitItem<'tcx>>>>>,
+    /// A cache for the trait_items() routine; note that the routine
+    /// itself pushes the `TraitItems` dependency node.
+    trait_items_cache: RefCell<DepTrackingMap<maps::TraitItems<'tcx>>>,
 
-    pub impl_trait_refs: RefCell<DefIdMap<Option<TraitRef<'tcx>>>>,
-    pub trait_defs: RefCell<DefIdMap<&'tcx ty::TraitDef<'tcx>>>,
-    pub adt_defs: RefCell<DefIdMap<ty::AdtDefMaster<'tcx>>>,
+    pub impl_trait_refs: RefCell<DepTrackingMap<maps::ImplTraitRefs<'tcx>>>,
+    pub trait_defs: RefCell<DepTrackingMap<maps::TraitDefs<'tcx>>>,
+    pub adt_defs: RefCell<DepTrackingMap<maps::AdtDefs<'tcx>>>,
 
     /// Maps from the def-id of an item (trait/struct/enum/fn) to its
     /// associated predicates.
-    pub predicates: RefCell<DefIdMap<GenericPredicates<'tcx>>>,
+    pub predicates: RefCell<DepTrackingMap<maps::Predicates<'tcx>>>,
 
     /// Maps from the def-id of a trait to the list of
     /// super-predicates. This is a subset of the full list of
@@ -267,21 +273,40 @@ pub struct ctxt<'tcx> {
     /// evaluate them even during type conversion, often before the
     /// full predicates are available (note that supertraits have
     /// additional acyclicity requirements).
-    pub super_predicates: RefCell<DefIdMap<GenericPredicates<'tcx>>>,
+    pub super_predicates: RefCell<DepTrackingMap<maps::Predicates<'tcx>>>,
 
     pub map: ast_map::Map<'tcx>,
+
+    // Records the free variables refrenced by every closure
+    // expression. Do not track deps for this, just recompute it from
+    // scratch every time.
     pub freevars: RefCell<FreevarMap>,
-    pub tcache: RefCell<DefIdMap<ty::TypeScheme<'tcx>>>,
+
+    // Records the type of every item.
+    pub tcache: RefCell<DepTrackingMap<maps::Tcache<'tcx>>>,
+
+    // Internal cache for metadata decoding. No need to track deps on this.
     pub rcache: RefCell<FnvHashMap<ty::CReaderCacheKey, Ty<'tcx>>>,
+
+    // Cache for the type-contents routine. FIXME -- track deps?
     pub tc_cache: RefCell<FnvHashMap<Ty<'tcx>, ty::contents::TypeContents>>,
+
+    // Cache for various types within a method body and so forth.
+    //
+    // FIXME this should be made local to typeck, but it is currently used by one lint
     pub ast_ty_to_ty_cache: RefCell<NodeMap<Ty<'tcx>>>,
+
+    // FIXME no dep tracking, but we should be able to remove this
     pub ty_param_defs: RefCell<NodeMap<ty::TypeParameterDef<'tcx>>>,
+
+    // FIXME dep tracking -- should be harmless enough
     pub normalized_cache: RefCell<FnvHashMap<Ty<'tcx>, Ty<'tcx>>>,
+
     pub lang_items: middle::lang_items::LanguageItems,
 
     /// Maps from def-id of a type or region parameter to its
     /// (inferred) variance.
-    pub item_variance_map: RefCell<DefIdMap<Rc<ty::ItemVariances>>>,
+    pub item_variance_map: RefCell<DepTrackingMap<maps::ItemVariances<'tcx>>>,
 
     /// True if the variance has been computed yet; false otherwise.
     pub variance_computed: Cell<bool>,
@@ -289,13 +314,13 @@ pub struct ctxt<'tcx> {
     /// Maps a DefId of a type to a list of its inherent impls.
     /// Contains implementations of methods that are inherent to a type.
     /// Methods in these implementations don't need to be exported.
-    pub inherent_impls: RefCell<DefIdMap<Rc<Vec<DefId>>>>,
+    pub inherent_impls: RefCell<DepTrackingMap<maps::InherentImpls<'tcx>>>,
 
     /// Maps a DefId of an impl to a list of its items.
     /// Note that this contains all of the impls that we know about,
     /// including ones in other crates. It's not clear that this is the best
     /// way to do it.
-    pub impl_items: RefCell<DefIdMap<Vec<ty::ImplOrTraitItemId>>>,
+    pub impl_items: RefCell<DepTrackingMap<maps::ImplItems<'tcx>>>,
 
     /// Set of used unsafe nodes (functions or blocks). Unsafe nodes not
     /// present in this set can be warned about.
@@ -309,6 +334,7 @@ pub struct ctxt<'tcx> {
     /// The set of external nominal types whose implementations have been read.
     /// This is used for lazy resolution of methods.
     pub populated_external_types: RefCell<DefIdSet>,
+
     /// The set of external primitive types whose implementations have been read.
     /// FIXME(arielb1): why is this separate from populated_external_types?
     pub populated_external_primitive_impls: RefCell<DefIdSet>,
@@ -344,7 +370,7 @@ pub struct ctxt<'tcx> {
     pub fulfilled_predicates: RefCell<traits::FulfilledPredicates<'tcx>>,
 
     /// Caches the representation hints for struct definitions.
-    pub repr_hint_cache: RefCell<DefIdMap<Rc<Vec<attr::ReprAttr>>>>,
+    repr_hint_cache: RefCell<DepTrackingMap<maps::ReprHints<'tcx>>>,
 
     /// Maps Expr NodeId's to their constant qualification.
     pub const_qualif_map: RefCell<NodeMap<middle::check_const::ConstQualif>>,
@@ -483,7 +509,7 @@ impl<'tcx> ctxt<'tcx> {
     {
         let interner = RefCell::new(FnvHashMap());
         let common_types = CommonTypes::new(&arenas.type_, &interner);
-
+        let dep_graph = DepGraph::new(s.opts.incremental_compilation);
         tls::enter(ctxt {
             arenas: arenas,
             interner: interner,
@@ -491,35 +517,36 @@ impl<'tcx> ctxt<'tcx> {
             bare_fn_interner: RefCell::new(FnvHashMap()),
             region_interner: RefCell::new(FnvHashMap()),
             stability_interner: RefCell::new(FnvHashMap()),
+            dep_graph: dep_graph.clone(),
             types: common_types,
             named_region_map: named_region_map,
             region_maps: region_maps,
             free_region_maps: RefCell::new(FnvHashMap()),
-            item_variance_map: RefCell::new(DefIdMap()),
+            item_variance_map: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             variance_computed: Cell::new(false),
             sess: s,
             def_map: def_map,
             tables: RefCell::new(Tables::empty()),
-            impl_trait_refs: RefCell::new(DefIdMap()),
-            trait_defs: RefCell::new(DefIdMap()),
-            adt_defs: RefCell::new(DefIdMap()),
-            predicates: RefCell::new(DefIdMap()),
-            super_predicates: RefCell::new(DefIdMap()),
+            impl_trait_refs: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            trait_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            adt_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            predicates: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            super_predicates: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             fulfilled_predicates: RefCell::new(traits::FulfilledPredicates::new()),
             map: map,
             freevars: RefCell::new(freevars),
-            tcache: RefCell::new(DefIdMap()),
+            tcache: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             rcache: RefCell::new(FnvHashMap()),
             tc_cache: RefCell::new(FnvHashMap()),
             ast_ty_to_ty_cache: RefCell::new(NodeMap()),
-            impl_or_trait_items: RefCell::new(DefIdMap()),
-            trait_item_def_ids: RefCell::new(DefIdMap()),
-            trait_items_cache: RefCell::new(DefIdMap()),
+            impl_or_trait_items: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            trait_item_def_ids: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            trait_items_cache: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             ty_param_defs: RefCell::new(NodeMap()),
             normalized_cache: RefCell::new(FnvHashMap()),
             lang_items: lang_items,
-            inherent_impls: RefCell::new(DefIdMap()),
-            impl_items: RefCell::new(DefIdMap()),
+            inherent_impls: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
+            impl_items: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             used_unsafe: RefCell::new(NodeSet()),
             used_mut_nodes: RefCell::new(NodeSet()),
             populated_external_types: RefCell::new(DefIdSet()),
@@ -531,7 +558,7 @@ impl<'tcx> ctxt<'tcx> {
             stability: RefCell::new(stability),
             selection_cache: traits::SelectionCache::new(),
             evaluation_cache: traits::EvaluationCache::new(),
-            repr_hint_cache: RefCell::new(DefIdMap()),
+            repr_hint_cache: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             const_qualif_map: RefCell::new(NodeMap()),
             custom_coerce_unsized_kinds: RefCell::new(DefIdMap()),
             cast_kinds: RefCell::new(NodeMap()),
@@ -1000,4 +1027,26 @@ impl<'tcx> ctxt<'tcx> {
     pub fn mk_param_from_def(&self, def: &ty::TypeParameterDef) -> Ty<'tcx> {
         self.mk_param(def.space, def.index, def.name)
     }
+
+    pub fn trait_items(&self, trait_did: DefId) -> Rc<Vec<ty::ImplOrTraitItem<'tcx>>> {
+        self.trait_items_cache.memoize(trait_did, || {
+            let def_ids = self.trait_item_def_ids(trait_did);
+            Rc::new(def_ids.iter()
+                           .map(|d| self.impl_or_trait_item(d.def_id()))
+                           .collect())
+        })
+    }
+
+    /// Obtain the representation annotation for a struct definition.
+    pub fn lookup_repr_hints(&self, did: DefId) -> Rc<Vec<attr::ReprAttr>> {
+        self.repr_hint_cache.memoize(did, || {
+            Rc::new(if did.is_local() {
+                self.get_attrs(did).iter().flat_map(|meta| {
+                    attr::find_repr_attrs(self.sess.diagnostic(), meta).into_iter()
+                }).collect()
+            } else {
+                self.sess.cstore.repr_attrs(did)
+            })
+        })
+    }
 }
index d9033eaa29f6b8ab3d132f3f79e894e15c28bd7c..ab48fd7fb8665f2727903f471f5e872ff5b6f5a8 100644 (file)
@@ -17,6 +17,7 @@ use std::fmt;
 use syntax::abi;
 use syntax::ast::{self, Name};
 use syntax::codemap::Span;
+use syntax::errors::DiagnosticBuilder;
 
 use rustc_front::hir;
 
@@ -252,27 +253,30 @@ impl<'tcx> ty::TyS<'tcx> {
 }
 
 impl<'tcx> ty::ctxt<'tcx> {
-    pub fn note_and_explain_type_err(&self, err: &TypeError<'tcx>, sp: Span) {
+    pub fn note_and_explain_type_err(&self,
+                                     db: &mut DiagnosticBuilder,
+                                     err: &TypeError<'tcx>,
+                                     sp: Span) {
         use self::TypeError::*;
 
         match err.clone() {
             RegionsDoesNotOutlive(subregion, superregion) => {
-                self.note_and_explain_region("", subregion, "...");
-                self.note_and_explain_region("...does not necessarily outlive ",
+                self.note_and_explain_region(db, "", subregion, "...");
+                self.note_and_explain_region(db, "...does not necessarily outlive ",
                                            superregion, "");
             }
             RegionsNotSame(region1, region2) => {
-                self.note_and_explain_region("", region1, "...");
-                self.note_and_explain_region("...is not the same lifetime as ",
+                self.note_and_explain_region(db, "", region1, "...");
+                self.note_and_explain_region(db, "...is not the same lifetime as ",
                                            region2, "");
             }
             RegionsNoOverlap(region1, region2) => {
-                self.note_and_explain_region("", region1, "...");
-                self.note_and_explain_region("...does not overlap ",
+                self.note_and_explain_region(db, "", region1, "...");
+                self.note_and_explain_region(db, "...does not overlap ",
                                            region2, "");
             }
             RegionsInsufficientlyPolymorphic(_, conc_region) => {
-                self.note_and_explain_region("concrete lifetime that was found is ",
+                self.note_and_explain_region(db, "concrete lifetime that was found is ",
                                            conc_region, "");
             }
             RegionsOverlyPolymorphic(_, ty::ReVar(_)) => {
@@ -280,42 +284,40 @@ impl<'tcx> ty::ctxt<'tcx> {
                 // inference variables, it's not very illuminating.
             }
             RegionsOverlyPolymorphic(_, conc_region) => {
-                self.note_and_explain_region("expected concrete lifetime is ",
+                self.note_and_explain_region(db, "expected concrete lifetime is ",
                                            conc_region, "");
             }
             Sorts(values) => {
                 let expected_str = values.expected.sort_string(self);
                 let found_str = values.found.sort_string(self);
                 if expected_str == found_str && expected_str == "closure" {
-                    self.sess.span_note(sp,
+                    db.span_note(sp,
                         "no two closures, even if identical, have the same type");
-                    self.sess.span_help(sp,
+                    db.span_help(sp,
                         "consider boxing your closure and/or using it as a trait object");
                 }
             },
             TyParamDefaultMismatch(values) => {
                 let expected = values.expected;
                 let found = values.found;
-                self.sess.span_note(sp,
-                                    &format!("conflicting type parameter defaults `{}` and `{}`",
-                                             expected.ty,
-                                             found.ty));
+                db.span_note(sp, &format!("conflicting type parameter defaults `{}` and `{}`",
+                                          expected.ty,
+                                          found.ty));
 
                 match
                     self.map.as_local_node_id(expected.def_id)
                             .and_then(|node_id| self.map.opt_span(node_id))
                 {
                     Some(span) => {
-                        self.sess.span_note(span, "a default was defined here...");
+                        db.span_note(span, "a default was defined here...");
                     }
                     None => {
-                        self.sess.note(
-                            &format!("a default is defined on `{}`",
-                                     self.item_path_str(expected.def_id)));
+                        db.note(&format!("a default is defined on `{}`",
+                                         self.item_path_str(expected.def_id)));
                     }
                 }
 
-                self.sess.span_note(
+                db.span_note(
                     expected.origin_span,
                     "...that was applied to an unconstrained type variable here");
 
@@ -324,18 +326,16 @@ impl<'tcx> ty::ctxt<'tcx> {
                             .and_then(|node_id| self.map.opt_span(node_id))
                 {
                     Some(span) => {
-                        self.sess.span_note(span, "a second default was defined here...");
+                        db.span_note(span, "a second default was defined here...");
                     }
                     None => {
-                        self.sess.note(
-                            &format!("a second default is defined on `{}`",
-                                     self.item_path_str(found.def_id)));
+                        db.note(&format!("a second default is defined on `{}`",
+                                         self.item_path_str(found.def_id)));
                     }
                 }
 
-                self.sess.span_note(
-                    found.origin_span,
-                    "...that also applies to the same type variable here");
+                db.span_note(found.origin_span,
+                             "...that also applies to the same type variable here");
             }
             _ => {}
         }
index 77608f4012845d687c7054bc0fc4562202d83f8f..a06e8a72c44ee685b4588b622f4afa395e3d2f32 100644 (file)
@@ -15,7 +15,7 @@ use syntax::ast;
 use self::SimplifiedType::*;
 
 /// See `simplify_type
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum SimplifiedType {
     BoolSimplifiedType,
     CharSimplifiedType,
index 94a50e3cac7f75e107ca416c40aab08fa71db2ba..a0b03fe8126dd18aabbff7dca7cab7be8a5450a4 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use middle::subst;
-use middle::ty::{self, HasTypeFlags, Ty, TypeFlags};
+use middle::ty::{self, Ty, TypeFlags, TypeFoldable};
 
 pub struct FlagComputation {
     pub flags: TypeFlags,
index 605e78e92811514357f2e9597eae9dccd317c96f..da0245a8d25205cb5f3c87dd964e993399ccfce4 100644 (file)
 //! instance of a "folder" (a type which implements `TypeFolder`). Then
 //! the setup is intended to be:
 //!
-//!     T.fold_with(F) --calls--> F.fold_T(T) --calls--> super_fold_T(F, T)
+//!   T.fold_with(F) --calls--> F.fold_T(T) --calls--> T.super_fold_with(F)
 //!
 //! This way, when you define a new folder F, you can override
-//! `fold_T()` to customize the behavior, and invoke `super_fold_T()`
+//! `fold_T()` to customize the behavior, and invoke `T.super_fold_with()`
 //! to get the original behavior. Meanwhile, to actually fold
 //! something, you can just write `T.fold_with(F)`, which is
 //! convenient. (Note that `fold_with` will also transparently handle
 //! things like a `Vec<T>` where T is foldable and so on.)
 //!
 //! In this ideal setup, the only function that actually *does*
-//! anything is `super_fold_T`, which traverses the type `T`. Moreover,
-//! `super_fold_T` should only ever call `T.fold_with()`.
+//! anything is `T.super_fold_with()`, which traverses the type `T`.
+//! Moreover, `T.super_fold_with()` should only ever call `T.fold_with()`.
 //!
 //! In some cases, we follow a degenerate pattern where we do not have
-//! a `fold_T` nor `super_fold_T` method. Instead, `T.fold_with`
-//! traverses the structure directly. This is suboptimal because the
-//! behavior cannot be overridden, but it's much less work to implement.
-//! If you ever *do* need an override that doesn't exist, it's not hard
-//! to convert the degenerate pattern into the proper thing.
+//! a `fold_T` method. Instead, `T.fold_with` traverses the structure directly.
+//! This is suboptimal because the behavior cannot be overridden, but it's
+//! much less work to implement. If you ever *do* need an override that
+//! doesn't exist, it's not hard to convert the degenerate pattern into the
+//! proper thing.
+//!
+//! A `TypeFoldable` T can also be visited by a `TypeVisitor` V using similar setup:
+//!   T.visit_with(V) --calls--> V.visit_T(T) --calls--> T.super_visit_with(V).
+//! These methods return true to indicate that the visitor has found what it is looking for
+//! and does not need to visit anything else.
 
 use middle::region;
 use middle::subst;
 use middle::ty::adjustment;
-use middle::ty::{self, Binder, Ty, RegionEscape};
+use middle::ty::{self, Binder, Ty, TypeFlags};
 
 use std::fmt;
 use util::nodemap::{FnvHashMap, FnvHashSet};
 
-///////////////////////////////////////////////////////////////////////////
-// Two generic traits
-
 /// The TypeFoldable trait is implemented for every type that can be folded.
 /// Basically, every type that has a corresponding method in TypeFolder.
 pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self;
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self;
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        self.super_fold_with(folder)
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool;
+    fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.super_visit_with(visitor)
+    }
+
+    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
+        self.visit_with(&mut HasEscapingRegionsVisitor { depth: depth })
+    }
+    fn has_escaping_regions(&self) -> bool {
+        self.has_regions_escaping_depth(0)
+    }
+
+    fn has_type_flags(&self, flags: TypeFlags) -> bool {
+        self.visit_with(&mut HasTypeFlagsVisitor { flags: flags })
+    }
+    fn has_projection_types(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_PROJECTION)
+    }
+    fn references_error(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_TY_ERR)
+    }
+    fn has_param_types(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_PARAMS)
+    }
+    fn has_self_ty(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_SELF)
+    }
+    fn has_infer_types(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_TY_INFER)
+    }
+    fn needs_infer(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_RE_INFER)
+    }
+    fn needs_subst(&self) -> bool {
+        self.has_type_flags(TypeFlags::NEEDS_SUBST)
+    }
+    fn has_closure_types(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_TY_CLOSURE)
+    }
+    fn has_erasable_regions(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND |
+                            TypeFlags::HAS_RE_INFER |
+                            TypeFlags::HAS_FREE_REGIONS)
+    }
+    /// 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
+    /// false.
+    fn is_global(&self) -> bool {
+        !self.has_type_flags(TypeFlags::HAS_LOCAL_NAMES)
+    }
 }
 
 /// The TypeFolder trait defines the actual *folding*. There is a
@@ -74,248 +131,77 @@ pub trait TypeFolder<'tcx> : Sized {
         where T : TypeFoldable<'tcx>
     {
         // FIXME(#20526) this should replace `enter_region_binder`/`exit_region_binder`.
-        super_fold_binder(self, t)
+        t.super_fold_with(self)
     }
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        super_fold_ty(self, t)
+        t.super_fold_with(self)
     }
 
     fn fold_mt(&mut self, t: &ty::TypeAndMut<'tcx>) -> ty::TypeAndMut<'tcx> {
-        super_fold_mt(self, t)
+        t.super_fold_with(self)
     }
 
     fn fold_trait_ref(&mut self, t: &ty::TraitRef<'tcx>) -> ty::TraitRef<'tcx> {
-        super_fold_trait_ref(self, t)
+        t.super_fold_with(self)
     }
 
     fn fold_substs(&mut self,
                    substs: &subst::Substs<'tcx>)
                    -> subst::Substs<'tcx> {
-        super_fold_substs(self, substs)
+        substs.super_fold_with(self)
     }
 
     fn fold_fn_sig(&mut self,
                    sig: &ty::FnSig<'tcx>)
                    -> ty::FnSig<'tcx> {
-        super_fold_fn_sig(self, sig)
+        sig.super_fold_with(self)
     }
 
     fn fold_output(&mut self,
                       output: &ty::FnOutput<'tcx>)
                       -> ty::FnOutput<'tcx> {
-        super_fold_output(self, output)
+        output.super_fold_with(self)
     }
 
     fn fold_bare_fn_ty(&mut self,
                        fty: &ty::BareFnTy<'tcx>)
                        -> ty::BareFnTy<'tcx>
     {
-        super_fold_bare_fn_ty(self, fty)
+        fty.super_fold_with(self)
     }
 
     fn fold_closure_ty(&mut self,
                        fty: &ty::ClosureTy<'tcx>)
                        -> ty::ClosureTy<'tcx> {
-        super_fold_closure_ty(self, fty)
+        fty.super_fold_with(self)
     }
 
     fn fold_region(&mut self, r: ty::Region) -> ty::Region {
-        r
+        r.super_fold_with(self)
     }
 
     fn fold_existential_bounds(&mut self, s: &ty::ExistentialBounds<'tcx>)
                                -> ty::ExistentialBounds<'tcx> {
-        super_fold_existential_bounds(self, s)
+        s.super_fold_with(self)
     }
 
     fn fold_autoref(&mut self, ar: &adjustment::AutoRef<'tcx>)
                     -> adjustment::AutoRef<'tcx> {
-        super_fold_autoref(self, ar)
+        ar.super_fold_with(self)
     }
-
-    fn fold_item_substs(&mut self, i: ty::ItemSubsts<'tcx>) -> ty::ItemSubsts<'tcx> {
-        super_fold_item_substs(self, i)
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// "super" routines: these are the default implementations for TypeFolder.
-//
-// They should invoke `foo.fold_with()` to do recursive folding.
-
-pub fn super_fold_binder<'tcx, T, U>(this: &mut T,
-                                     binder: &Binder<U>)
-                                     -> Binder<U>
-    where T : TypeFolder<'tcx>, U : TypeFoldable<'tcx>
-{
-    this.enter_region_binder();
-    let result = Binder(binder.0.fold_with(this));
-    this.exit_region_binder();
-    result
 }
 
-pub fn super_fold_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                ty: Ty<'tcx>)
-                                                -> Ty<'tcx> {
-    let sty = match ty.sty {
-        ty::TyBox(typ) => {
-            ty::TyBox(typ.fold_with(this))
-        }
-        ty::TyRawPtr(ref tm) => {
-            ty::TyRawPtr(tm.fold_with(this))
-        }
-        ty::TyArray(typ, sz) => {
-            ty::TyArray(typ.fold_with(this), sz)
-        }
-        ty::TySlice(typ) => {
-            ty::TySlice(typ.fold_with(this))
-        }
-        ty::TyEnum(tid, ref substs) => {
-            let substs = substs.fold_with(this);
-            ty::TyEnum(tid, this.tcx().mk_substs(substs))
-        }
-        ty::TyTrait(box ty::TraitTy { ref principal, ref bounds }) => {
-            ty::TyTrait(box ty::TraitTy {
-                principal: principal.fold_with(this),
-                bounds: bounds.fold_with(this),
-            })
-        }
-        ty::TyTuple(ref ts) => {
-            ty::TyTuple(ts.fold_with(this))
-        }
-        ty::TyBareFn(opt_def_id, ref f) => {
-            let bfn = f.fold_with(this);
-            ty::TyBareFn(opt_def_id, this.tcx().mk_bare_fn(bfn))
-        }
-        ty::TyRef(r, ref tm) => {
-            let r = r.fold_with(this);
-            ty::TyRef(this.tcx().mk_region(r), tm.fold_with(this))
-        }
-        ty::TyStruct(did, ref substs) => {
-            let substs = substs.fold_with(this);
-            ty::TyStruct(did, this.tcx().mk_substs(substs))
-        }
-        ty::TyClosure(did, ref substs) => {
-            let s = substs.fold_with(this);
-            ty::TyClosure(did, s)
-        }
-        ty::TyProjection(ref data) => {
-            ty::TyProjection(data.fold_with(this))
-        }
-        ty::TyBool | ty::TyChar | ty::TyStr |
-        ty::TyInt(_) | ty::TyUint(_) | ty::TyFloat(_) |
-        ty::TyError | ty::TyInfer(_) |
-        ty::TyParam(..) => {
-            ty.sty.clone()
-        }
-    };
-    this.tcx().mk_ty(sty)
-}
-
-pub fn super_fold_substs<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                    substs: &subst::Substs<'tcx>)
-                                                    -> subst::Substs<'tcx> {
-    let regions = match substs.regions {
-        subst::ErasedRegions => {
-            subst::ErasedRegions
-        }
-        subst::NonerasedRegions(ref regions) => {
-            subst::NonerasedRegions(regions.fold_with(this))
-        }
-    };
-
-    subst::Substs { regions: regions,
-                    types: substs.types.fold_with(this) }
-}
-
-pub fn super_fold_fn_sig<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                    sig: &ty::FnSig<'tcx>)
-                                                    -> ty::FnSig<'tcx>
-{
-    ty::FnSig { inputs: sig.inputs.fold_with(this),
-                output: sig.output.fold_with(this),
-                variadic: sig.variadic }
-}
-
-pub fn super_fold_output<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                    output: &ty::FnOutput<'tcx>)
-                                                    -> ty::FnOutput<'tcx> {
-    match *output {
-        ty::FnConverging(ref ty) => ty::FnConverging(ty.fold_with(this)),
-        ty::FnDiverging => ty::FnDiverging
-    }
-}
-
-pub fn super_fold_bare_fn_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                        fty: &ty::BareFnTy<'tcx>)
-                                                        -> ty::BareFnTy<'tcx>
-{
-    ty::BareFnTy { sig: fty.sig.fold_with(this),
-                   abi: fty.abi,
-                   unsafety: fty.unsafety }
-}
-
-pub fn super_fold_closure_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                        fty: &ty::ClosureTy<'tcx>)
-                                                        -> ty::ClosureTy<'tcx>
-{
-    ty::ClosureTy {
-        sig: fty.sig.fold_with(this),
-        unsafety: fty.unsafety,
-        abi: fty.abi,
-    }
-}
-
-pub fn super_fold_trait_ref<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                       t: &ty::TraitRef<'tcx>)
-                                                       -> ty::TraitRef<'tcx>
-{
-    let substs = t.substs.fold_with(this);
-    ty::TraitRef {
-        def_id: t.def_id,
-        substs: this.tcx().mk_substs(substs),
-    }
-}
-
-pub fn super_fold_mt<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                mt: &ty::TypeAndMut<'tcx>)
-                                                -> ty::TypeAndMut<'tcx> {
-    ty::TypeAndMut {ty: mt.ty.fold_with(this),
-            mutbl: mt.mutbl}
-}
-
-pub fn super_fold_existential_bounds<'tcx, T: TypeFolder<'tcx>>(
-    this: &mut T,
-    bounds: &ty::ExistentialBounds<'tcx>)
-    -> ty::ExistentialBounds<'tcx>
-{
-    ty::ExistentialBounds {
-        region_bound: bounds.region_bound.fold_with(this),
-        builtin_bounds: bounds.builtin_bounds,
-        projection_bounds: bounds.projection_bounds.fold_with(this),
-    }
-}
+pub trait TypeVisitor<'tcx> : Sized {
+    fn enter_region_binder(&mut self) { }
+    fn exit_region_binder(&mut self) { }
 
-pub fn super_fold_autoref<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                     autoref: &adjustment::AutoRef<'tcx>)
-                                                     -> adjustment::AutoRef<'tcx>
-{
-    match *autoref {
-        adjustment::AutoPtr(r, m) => {
-            let r = r.fold_with(this);
-            adjustment::AutoPtr(this.tcx().mk_region(r), m)
-        }
-        adjustment::AutoUnsafe(m) => adjustment::AutoUnsafe(m)
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+        t.super_visit_with(self)
     }
-}
 
-pub fn super_fold_item_substs<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                         substs: ty::ItemSubsts<'tcx>)
-                                                         -> ty::ItemSubsts<'tcx>
-{
-    ty::ItemSubsts {
-        substs: substs.substs.fold_with(this),
+    fn visit_region(&mut self, r: ty::Region) -> bool {
+        r.super_visit_with(self)
     }
 }
 
@@ -333,7 +219,7 @@ impl<'a, 'tcx, F> TypeFolder<'tcx> for BottomUpFolder<'a, 'tcx, F> where
     fn tcx(&self) -> &ty::ctxt<'tcx> { self.tcx }
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        let t1 = super_fold_ty(self, ty);
+        let t1 = ty.super_fold_with(self);
         (self.fldop)(t1)
     }
 }
@@ -495,7 +381,7 @@ impl<'tcx> ty::ctxt<'tcx> {
     }
 
     pub fn no_late_bound_regions<T>(&self, value: &Binder<T>) -> Option<T>
-        where T : TypeFoldable<'tcx> + RegionEscape
+        where T : TypeFoldable<'tcx>
     {
         if value.0.has_escaping_regions() {
             None
@@ -561,7 +447,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionReplacer<'a, 'tcx>
             return t;
         }
 
-        super_fold_ty(self, t)
+        t.super_fold_with(self)
     }
 
     fn fold_region(&mut self, r: ty::Region) -> ty::Region {
@@ -612,7 +498,7 @@ impl<'tcx> ty::ctxt<'tcx> {
                     Some(u) => return u
                 }
 
-                let t_norm = ty::fold::super_fold_ty(self, ty);
+                let t_norm = ty.super_fold_with(self);
                 self.tcx().normalized_cache.borrow_mut().insert(ty, t_norm);
                 return t_norm;
             }
@@ -621,7 +507,7 @@ impl<'tcx> ty::ctxt<'tcx> {
                 where T : TypeFoldable<'tcx>
             {
                 let u = self.tcx().anonymize_late_bound_regions(t);
-                ty::fold::super_fold_binder(self, &u)
+                u.super_fold_with(self)
             }
 
             fn fold_region(&mut self, r: ty::Region) -> ty::Region {
@@ -678,3 +564,75 @@ pub fn shift_regions<'tcx, T:TypeFoldable<'tcx>>(tcx: &ty::ctxt<'tcx>,
         shift_region(region, amount)
     }))
 }
+
+/// An "escaping region" is a bound region whose binder is not part of `t`.
+///
+/// So, for example, consider a type like the following, which has two binders:
+///
+///    for<'a> fn(x: for<'b> fn(&'a isize, &'b isize))
+///    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope
+///                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~  inner scope
+///
+/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the
+/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner
+/// fn type*, that type has an escaping region: `'a`.
+///
+/// Note that what I'm calling an "escaping region" is often just called a "free region". However,
+/// we already use the term "free region". It refers to the regions that we use to represent bound
+/// regions on a fn definition while we are typechecking its body.
+///
+/// To clarify, conceptually there is no particular difference between an "escaping" region and a
+/// "free" region. However, there is a big difference in practice. Basically, when "entering" a
+/// binding level, one is generally required to do some sort of processing to a bound region, such
+/// as replacing it with a fresh/skolemized region, or making an entry in the environment to
+/// represent the scope to which it is attached, etc. An escaping region represents a bound region
+/// for which this processing has not yet been done.
+struct HasEscapingRegionsVisitor {
+    depth: u32,
+}
+
+impl<'tcx> TypeVisitor<'tcx> for HasEscapingRegionsVisitor {
+    fn enter_region_binder(&mut self) {
+        self.depth += 1;
+    }
+
+    fn exit_region_binder(&mut self) {
+        self.depth -= 1;
+    }
+
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+        t.region_depth > self.depth
+    }
+
+    fn visit_region(&mut self, r: ty::Region) -> bool {
+        r.escapes_depth(self.depth)
+    }
+}
+
+struct HasTypeFlagsVisitor {
+    flags: ty::TypeFlags,
+}
+
+impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
+    fn visit_ty(&mut self, t: Ty) -> bool {
+        t.flags.get().intersects(self.flags)
+    }
+
+    fn visit_region(&mut self, r: ty::Region) -> bool {
+        if self.flags.intersects(ty::TypeFlags::HAS_LOCAL_NAMES) {
+            // does this represent a region that cannot be named
+            // in a global way? used in fulfillment caching.
+            match r {
+                ty::ReStatic | ty::ReEmpty => {}
+                _ => return true,
+            }
+        }
+        if self.flags.intersects(ty::TypeFlags::HAS_RE_INFER) {
+            match r {
+                ty::ReVar(_) | ty::ReSkolemized(..) => { return true }
+                _ => {}
+            }
+        }
+        false
+    }
+}
index 73d567d0acf40aeb846b8a236456e1147b0bb128..ffc12aa5aea198e37dde94deefcd2b52df5de614 100644 (file)
@@ -8,7 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use dep_graph::DepNode;
 use middle::ty::{Ty, TyS};
+use middle::ty::tls;
 
 use rustc_data_structures::ivar;
 
@@ -27,6 +29,10 @@ use core::nonzero::NonZero;
 ///     (B) no aliases to this value with a 'tcx longer than this
 ///         value's 'lt exist
 ///
+/// Dependency tracking: each ivar does not know what node in the
+/// dependency graph it is associated with, so when you get/fulfill
+/// you must supply a `DepNode` id. This should always be the same id!
+///
 /// NonZero is used rather than Unique because Unique isn't Copy.
 pub struct TyIVar<'tcx, 'lt: 'tcx>(ivar::Ivar<NonZero<*const TyS<'static>>>,
                                    PhantomData<fn(TyS<'lt>)->TyS<'tcx>>);
@@ -40,19 +46,28 @@ impl<'tcx, 'lt> TyIVar<'tcx, 'lt> {
     }
 
     #[inline]
-    pub fn get(&self) -> Option<Ty<'tcx>> {
+    pub fn get(&self, dep_node: DepNode) -> Option<Ty<'tcx>> {
+        tls::with(|tcx| tcx.dep_graph.read(dep_node));
+        self.untracked_get()
+    }
+
+    #[inline]
+    fn untracked_get(&self) -> Option<Ty<'tcx>> {
         match self.0.get() {
             None => None,
             // valid because of invariant (A)
             Some(v) => Some(unsafe { &*(*v as *const TyS<'tcx>) })
         }
     }
+
     #[inline]
-    pub fn unwrap(&self) -> Ty<'tcx> {
-        self.get().unwrap()
+    pub fn unwrap(&self, dep_node: DepNode) -> Ty<'tcx> {
+        self.get(dep_node).unwrap()
     }
 
-    pub fn fulfill(&self, value: Ty<'lt>) {
+    pub fn fulfill(&self, dep_node: DepNode, value: Ty<'lt>) {
+        tls::with(|tcx| tcx.dep_graph.write(dep_node));
+
         // Invariant (A) is fulfilled, because by (B), every alias
         // of this has a 'tcx longer than 'lt.
         let value: *const TyS<'lt> = value;
@@ -64,7 +79,7 @@ impl<'tcx, 'lt> TyIVar<'tcx, 'lt> {
 
 impl<'tcx, 'lt> fmt::Debug for TyIVar<'tcx, 'lt> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self.get() {
+        match self.untracked_get() {
             Some(val) => write!(f, "TyIVar({:?})", val),
             None => f.write_str("TyIVar(<unfulfilled>)")
         }
diff --git a/src/librustc/middle/ty/maps.rs b/src/librustc/middle/ty/maps.rs
new file mode 100644 (file)
index 0000000..7d5276f
--- /dev/null
@@ -0,0 +1,44 @@
+// 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 dep_graph::{DepNode, DepTrackingMapConfig};
+use middle::def_id::DefId;
+use middle::ty;
+use std::marker::PhantomData;
+use std::rc::Rc;
+use syntax::attr;
+
+macro_rules! dep_map_ty {
+    ($ty_name:ident : $node_name:ident ($key:ty) -> $value:ty) => {
+        pub struct $ty_name<'tcx> {
+            data: PhantomData<&'tcx ()>
+        }
+
+        impl<'tcx> DepTrackingMapConfig for $ty_name<'tcx> {
+            type Key = $key;
+            type Value = $value;
+            fn to_dep_node(key: &$key) -> DepNode { DepNode::$node_name(*key) }
+        }
+    }
+}
+
+dep_map_ty! { ImplOrTraitItems: ImplOrTraitItems(DefId) -> ty::ImplOrTraitItem<'tcx> }
+dep_map_ty! { Tcache: ItemSignature(DefId) -> ty::TypeScheme<'tcx> }
+dep_map_ty! { Predicates: ItemSignature(DefId) -> ty::GenericPredicates<'tcx> }
+dep_map_ty! { SuperPredicates: ItemSignature(DefId) -> ty::GenericPredicates<'tcx> }
+dep_map_ty! { TraitItemDefIds: TraitItemDefIds(DefId) -> Rc<Vec<ty::ImplOrTraitItemId>> }
+dep_map_ty! { ImplTraitRefs: ItemSignature(DefId) -> Option<ty::TraitRef<'tcx>> }
+dep_map_ty! { TraitDefs: ItemSignature(DefId) -> &'tcx ty::TraitDef<'tcx> }
+dep_map_ty! { AdtDefs: ItemSignature(DefId) -> ty::AdtDefMaster<'tcx> }
+dep_map_ty! { ItemVariances: ItemSignature(DefId) -> Rc<ty::ItemVariances> }
+dep_map_ty! { InherentImpls: InherentImpls(DefId) -> Rc<Vec<DefId>> }
+dep_map_ty! { ImplItems: ImplItems(DefId) -> Vec<ty::ImplOrTraitItemId> }
+dep_map_ty! { TraitItems: TraitItems(DefId) -> Rc<Vec<ty::ImplOrTraitItem<'tcx>>> }
+dep_map_ty! { ReprHints: ReprHints(DefId) -> Rc<Vec<attr::ReprAttr>> }
index 71ae8e40b45f0ecbe7c331cb1602ee5626ecd906..b902a46fea314083d6a0e4d70c42d218b100a43c 100644 (file)
@@ -12,31 +12,34 @@ pub use self::ImplOrTraitItemId::*;
 pub use self::ClosureKind::*;
 pub use self::Variance::*;
 pub use self::DtorKind::*;
-pub use self::ExplicitSelfCategory::*;
 pub use self::ImplOrTraitItemContainer::*;
 pub use self::BorrowKind::*;
 pub use self::ImplOrTraitItem::*;
 pub use self::IntVarValue::*;
 pub use self::LvaluePreference::*;
+pub use self::fold::TypeFoldable;
 
+use dep_graph::{self, DepNode};
 use front::map as ast_map;
 use front::map::LinkedPath;
 use middle;
-use middle::cstore::{CrateStore, LOCAL_CRATE};
+use middle::cstore::{self, CrateStore, LOCAL_CRATE};
 use middle::def::{self, ExportMap};
 use middle::def_id::DefId;
 use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem};
-use middle::subst::{self, ParamSpace, Subst, Substs, VecPerParamSpace};
+use middle::region::{CodeExtent};
+use middle::subst::{self, Subst, Substs, VecPerParamSpace};
 use middle::traits;
 use middle::ty;
 use middle::ty::fold::TypeFolder;
 use middle::ty::walk::TypeWalker;
-use util::common::memoized;
-use util::nodemap::{NodeMap, NodeSet, DefIdMap};
+use util::common::MemoizationMap;
+use util::nodemap::{NodeMap, NodeSet};
 use util::nodemap::FnvHashMap;
 
+use serialize::{Encodable, Encoder, Decodable, Decoder};
 use std::borrow::{Borrow, Cow};
-use std::cell::{Cell, RefCell};
+use std::cell::Cell;
 use std::hash::{Hash, Hasher};
 use std::iter;
 use std::rc::Rc;
@@ -45,12 +48,12 @@ use std::vec::IntoIter;
 use std::collections::{HashMap, HashSet};
 use syntax::ast::{self, CrateNum, Name, NodeId};
 use syntax::attr::{self, AttrMetaMethods};
-use syntax::codemap::Span;
-use syntax::parse::token::{InternedString, special_idents};
+use syntax::codemap::{DUMMY_SP, Span};
+use syntax::parse::token::InternedString;
 
 use rustc_front::hir;
 use rustc_front::hir::{ItemImpl, ItemTrait};
-use rustc_front::hir::{MutImmutable, MutMutable, Visibility};
+use rustc_front::intravisit::Visitor;
 
 pub use self::sty::{Binder, DebruijnIndex};
 pub use self::sty::{BuiltinBound, BuiltinBounds, ExistentialBounds};
@@ -75,14 +78,18 @@ pub use self::contents::TypeContents;
 pub use self::context::{ctxt, tls};
 pub use self::context::{CtxtArenas, Lift, Tables};
 
+pub use self::trait_def::{TraitDef, TraitFlags};
+
 pub mod adjustment;
 pub mod cast;
 pub mod error;
 pub mod fast_reject;
 pub mod fold;
 pub mod _match;
+pub mod maps;
 pub mod outlives;
 pub mod relate;
+pub mod trait_def;
 pub mod walk;
 pub mod wf;
 pub mod util;
@@ -479,6 +486,24 @@ impl<'tcx> Hash for TyS<'tcx> {
 
 pub type Ty<'tcx> = &'tcx TyS<'tcx>;
 
+impl<'tcx> Encodable for Ty<'tcx> {
+    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        cstore::tls::with_encoding_context(s, |ecx, rbml_w| {
+            ecx.encode_ty(rbml_w, *self);
+            Ok(())
+        })
+    }
+}
+
+impl<'tcx> Decodable for Ty<'tcx> {
+    fn decode<D: Decoder>(d: &mut D) -> Result<Ty<'tcx>, D::Error> {
+        cstore::tls::with_decoding_context(d, |dcx, rbml_r| {
+            Ok(dcx.decode_ty(rbml_r))
+        })
+    }
+}
+
+
 /// Upvars do not get their own node-id. Instead, we use the pair of
 /// the original var id (that is, the root variable that is referenced
 /// by the upvar) and the id of the closure expression.
@@ -614,7 +639,6 @@ pub struct RegionParameterDef {
 impl RegionParameterDef {
     pub fn to_early_bound_region(&self) -> ty::Region {
         ty::ReEarlyBound(ty::EarlyBoundRegion {
-            def_id: self.def_id,
             space: self.space,
             index: self.index,
             name: self.name,
@@ -1098,7 +1122,7 @@ pub struct ParameterEnvironment<'a, 'tcx:'a> {
     /// FIXME(#3696). It would be nice to refactor so that free
     /// regions don't have this implicit scope and instead introduce
     /// relationships in the environment.
-    pub free_id: ast::NodeId,
+    pub free_id_outlive: CodeExtent,
 }
 
 impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
@@ -1113,7 +1137,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
             caller_bounds: caller_bounds,
             selection_cache: traits::SelectionCache::new(),
             evaluation_cache: traits::EvaluationCache::new(),
-            free_id: self.free_id,
+            free_id_outlive: self.free_id_outlive,
         }
     }
 
@@ -1131,7 +1155,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
                         cx.construct_parameter_environment(impl_item.span,
                                                            &scheme.generics,
                                                            &predicates,
-                                                           id)
+                                                           cx.region_maps.item_extent(id))
                     }
                     hir::ImplItemKind::Const(_, _) => {
                         let def_id = cx.map.local_def_id(id);
@@ -1140,7 +1164,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
                         cx.construct_parameter_environment(impl_item.span,
                                                            &scheme.generics,
                                                            &predicates,
-                                                           id)
+                                                           cx.region_maps.item_extent(id))
                     }
                     hir::ImplItemKind::Method(_, ref body) => {
                         let method_def_id = cx.map.local_def_id(id);
@@ -1152,7 +1176,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
                                     impl_item.span,
                                     method_generics,
                                     method_bounds,
-                                    body.id)
+                                    cx.region_maps.call_site_extent(id, body.id))
                             }
                             _ => {
                                 cx.sess
@@ -1175,7 +1199,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
                         cx.construct_parameter_environment(trait_item.span,
                                                            &trait_def.generics,
                                                            &predicates,
-                                                           id)
+                                                           cx.region_maps.item_extent(id))
                     }
                     hir::ConstTraitItem(..) => {
                         let def_id = cx.map.local_def_id(id);
@@ -1184,23 +1208,29 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
                         cx.construct_parameter_environment(trait_item.span,
                                                            &scheme.generics,
                                                            &predicates,
-                                                           id)
+                                                           cx.region_maps.item_extent(id))
                     }
                     hir::MethodTraitItem(_, ref body) => {
-                        // for the body-id, use the id of the body
-                        // block, unless this is a trait method with
-                        // no default, then fallback to the method id.
-                        let body_id = body.as_ref().map(|b| b.id).unwrap_or(id);
+                        // Use call-site for extent (unless this is a
+                        // trait method with no default; then fallback
+                        // to the method id).
                         let method_def_id = cx.map.local_def_id(id);
                         match cx.impl_or_trait_item(method_def_id) {
                             MethodTraitItem(ref method_ty) => {
                                 let method_generics = &method_ty.generics;
                                 let method_bounds = &method_ty.predicates;
+                                let extent = if let Some(ref body) = *body {
+                                    // default impl: use call_site extent as free_id_outlive bound.
+                                    cx.region_maps.call_site_extent(id, body.id)
+                                } else {
+                                    // no default impl: use item extent as free_id_outlive bound.
+                                    cx.region_maps.item_extent(id)
+                                };
                                 cx.construct_parameter_environment(
                                     trait_item.span,
                                     method_generics,
                                     method_bounds,
-                                    body_id)
+                                    extent)
                             }
                             _ => {
                                 cx.sess
@@ -1223,7 +1253,8 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
                         cx.construct_parameter_environment(item.span,
                                                            &fn_scheme.generics,
                                                            &fn_predicates,
-                                                           body.id)
+                                                           cx.region_maps.call_site_extent(id,
+                                                                                           body.id))
                     }
                     hir::ItemEnum(..) |
                     hir::ItemStruct(..) |
@@ -1236,7 +1267,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
                         cx.construct_parameter_environment(item.span,
                                                            &scheme.generics,
                                                            &predicates,
-                                                           id)
+                                                           cx.region_maps.item_extent(id))
                     }
                     hir::ItemTrait(..) => {
                         let def_id = cx.map.local_def_id(id);
@@ -1245,7 +1276,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
                         cx.construct_parameter_environment(item.span,
                                                            &trait_def.generics,
                                                            &predicates,
-                                                           id)
+                                                           cx.region_maps.item_extent(id))
                     }
                     _ => {
                         cx.sess.span_bug(item.span,
@@ -1293,161 +1324,6 @@ pub struct TypeScheme<'tcx> {
     pub ty: Ty<'tcx>,
 }
 
-bitflags! {
-    flags TraitFlags: u32 {
-        const NO_TRAIT_FLAGS        = 0,
-        const HAS_DEFAULT_IMPL      = 1 << 0,
-        const IS_OBJECT_SAFE        = 1 << 1,
-        const OBJECT_SAFETY_VALID   = 1 << 2,
-        const IMPLS_VALID           = 1 << 3,
-    }
-}
-
-/// As `TypeScheme` but for a trait ref.
-pub struct TraitDef<'tcx> {
-    pub unsafety: hir::Unsafety,
-
-    /// If `true`, then this trait had the `#[rustc_paren_sugar]`
-    /// attribute, indicating that it should be used with `Foo()`
-    /// sugar. This is a temporary thing -- eventually any trait wil
-    /// be usable with the sugar (or without it).
-    pub paren_sugar: bool,
-
-    /// Generic type definitions. Note that `Self` is listed in here
-    /// as having a single bound, the trait itself (e.g., in the trait
-    /// `Eq`, there is a single bound `Self : Eq`). This is so that
-    /// default methods get to assume that the `Self` parameters
-    /// implements the trait.
-    pub generics: Generics<'tcx>,
-
-    pub trait_ref: TraitRef<'tcx>,
-
-    /// A list of the associated types defined in this trait. Useful
-    /// for resolving `X::Foo` type markers.
-    pub associated_type_names: Vec<Name>,
-
-    // Impls of this trait. To allow for quicker lookup, the impls are indexed
-    // by a simplified version of their Self type: impls with a simplifiable
-    // Self are stored in nonblanket_impls keyed by it, while all other impls
-    // are stored in blanket_impls.
-
-    /// Impls of the trait.
-    pub nonblanket_impls: RefCell<
-        FnvHashMap<fast_reject::SimplifiedType, Vec<DefId>>
-    >,
-
-    /// Blanket impls associated with the trait.
-    pub blanket_impls: RefCell<Vec<DefId>>,
-
-    /// Various flags
-    pub flags: Cell<TraitFlags>
-}
-
-impl<'tcx> TraitDef<'tcx> {
-    // returns None if not yet calculated
-    pub fn object_safety(&self) -> Option<bool> {
-        if self.flags.get().intersects(TraitFlags::OBJECT_SAFETY_VALID) {
-            Some(self.flags.get().intersects(TraitFlags::IS_OBJECT_SAFE))
-        } else {
-            None
-        }
-    }
-
-    pub fn set_object_safety(&self, is_safe: bool) {
-        assert!(self.object_safety().map(|cs| cs == is_safe).unwrap_or(true));
-        self.flags.set(
-            self.flags.get() | if is_safe {
-                TraitFlags::OBJECT_SAFETY_VALID | TraitFlags::IS_OBJECT_SAFE
-            } else {
-                TraitFlags::OBJECT_SAFETY_VALID
-            }
-        );
-    }
-
-    /// Records a trait-to-implementation mapping.
-    pub fn record_impl(&self,
-                       tcx: &ctxt<'tcx>,
-                       impl_def_id: DefId,
-                       impl_trait_ref: TraitRef<'tcx>) {
-        debug!("TraitDef::record_impl for {:?}, from {:?}",
-               self, impl_trait_ref);
-
-        // We don't want to borrow_mut after we already populated all impls,
-        // so check if an impl is present with an immutable borrow first.
-        if let Some(sty) = fast_reject::simplify_type(tcx,
-                                                      impl_trait_ref.self_ty(), false) {
-            if let Some(is) = self.nonblanket_impls.borrow().get(&sty) {
-                if is.contains(&impl_def_id) {
-                    return // duplicate - skip
-                }
-            }
-
-            self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id)
-        } else {
-            if self.blanket_impls.borrow().contains(&impl_def_id) {
-                return // duplicate - skip
-            }
-            self.blanket_impls.borrow_mut().push(impl_def_id)
-        }
-    }
-
-
-    pub fn for_each_impl<F: FnMut(DefId)>(&self, tcx: &ctxt<'tcx>, mut f: F)  {
-        tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
-
-        for &impl_def_id in self.blanket_impls.borrow().iter() {
-            f(impl_def_id);
-        }
-
-        for v in self.nonblanket_impls.borrow().values() {
-            for &impl_def_id in v {
-                f(impl_def_id);
-            }
-        }
-    }
-
-    /// Iterate over every impl that could possibly match the
-    /// self-type `self_ty`.
-    pub fn for_each_relevant_impl<F: FnMut(DefId)>(&self,
-                                                   tcx: &ctxt<'tcx>,
-                                                   self_ty: Ty<'tcx>,
-                                                   mut f: F)
-    {
-        tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
-
-        for &impl_def_id in self.blanket_impls.borrow().iter() {
-            f(impl_def_id);
-        }
-
-        // simplify_type(.., false) basically replaces type parameters and
-        // projections with infer-variables. This is, of course, done on
-        // the impl trait-ref when it is instantiated, but not on the
-        // predicate trait-ref which is passed here.
-        //
-        // for example, if we match `S: Copy` against an impl like
-        // `impl<T:Copy> Copy for Option<T>`, we replace the type variable
-        // in `Option<T>` with an infer variable, to `Option<_>` (this
-        // doesn't actually change fast_reject output), but we don't
-        // replace `S` with anything - this impl of course can't be
-        // selected, and as there are hundreds of similar impls,
-        // considering them would significantly harm performance.
-        if let Some(simp) = fast_reject::simplify_type(tcx, self_ty, true) {
-            if let Some(impls) = self.nonblanket_impls.borrow().get(&simp) {
-                for &impl_def_id in impls {
-                    f(impl_def_id);
-                }
-            }
-        } else {
-            for v in self.nonblanket_impls.borrow().values() {
-                for &impl_def_id in v {
-                    f(impl_def_id);
-                }
-            }
-        }
-    }
-
-}
-
 bitflags! {
     flags AdtFlags: u32 {
         const NO_ADT_FLAGS        = 0,
@@ -1477,6 +1353,7 @@ pub struct VariantDefData<'tcx, 'container: 'tcx> {
     pub name: Name, // struct's name if this is a struct
     pub disr_val: Disr,
     pub fields: Vec<FieldDefData<'tcx, 'container>>,
+    pub kind: VariantKind,
 }
 
 pub struct FieldDefData<'tcx, 'container: 'tcx> {
@@ -1489,6 +1366,8 @@ pub struct FieldDefData<'tcx, 'container: 'tcx> {
     pub vis: hir::Visibility,
     /// TyIVar is used here to allow for variance (see the doc at
     /// AdtDefData).
+    ///
+    /// Note: direct accesses to `ty` must also add dep edges.
     ty: ivar::TyIVar<'tcx, 'container>
 }
 
@@ -1529,6 +1408,23 @@ impl<'tcx, 'container> Hash for AdtDefData<'tcx, 'container> {
     }
 }
 
+impl<'tcx> Encodable for AdtDef<'tcx> {
+    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        self.did.encode(s)
+    }
+}
+
+impl<'tcx> Decodable for AdtDef<'tcx> {
+    fn decode<D: Decoder>(d: &mut D) -> Result<AdtDef<'tcx>, D::Error> {
+        let def_id: DefId = try!{ Decodable::decode(d) };
+
+        cstore::tls::with_decoding_context(d, |dcx, _| {
+            let def_id = dcx.translate_def_id(def_id);
+            Ok(dcx.tcx().lookup_adt_def(def_id))
+        })
+    }
+}
+
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub enum AdtKind { Struct, Enum }
@@ -1712,13 +1608,7 @@ impl<'tcx, 'container> VariantDefData<'tcx, 'container> {
     }
 
     pub fn kind(&self) -> VariantKind {
-        match self.fields.get(0) {
-            None => VariantKind::Unit,
-            Some(&FieldDefData { name, .. }) if name == special_idents::unnamed_field.name => {
-                VariantKind::Tuple
-            }
-            Some(_) => VariantKind::Struct
-        }
+        self.kind
     }
 
     pub fn is_tuple_struct(&self) -> bool {
@@ -1762,11 +1652,11 @@ impl<'tcx, 'container> FieldDefData<'tcx, 'container> {
     }
 
     pub fn unsubst_ty(&self) -> Ty<'tcx> {
-        self.ty.unwrap()
+        self.ty.unwrap(DepNode::FieldTy(self.did))
     }
 
     pub fn fulfill_ty(&self, ty: Ty<'container>) {
-        self.ty.fulfill(ty);
+        self.ty.fulfill(DepNode::FieldTy(self.did), ty);
     }
 }
 
@@ -1889,24 +1779,20 @@ impl LvaluePreference {
 /// into the map by the `typeck::collect` phase.  If the def-id is external,
 /// then we have to go consult the crate loading code (and cache the result for
 /// the future).
-fn lookup_locally_or_in_crate_store<V, F>(descr: &str,
+fn lookup_locally_or_in_crate_store<M, F>(descr: &str,
                                           def_id: DefId,
-                                          map: &RefCell<DefIdMap<V>>,
-                                          load_external: F) -> V where
-    V: Clone,
-    F: FnOnce() -> V,
+                                          map: &M,
+                                          load_external: F)
+                                          -> M::Value where
+    M: MemoizationMap<Key=DefId>,
+    F: FnOnce() -> M::Value,
 {
-    match map.borrow().get(&def_id).cloned() {
-        Some(v) => { return v; }
-        None => { }
-    }
-
-    if def_id.is_local() {
-        panic!("No def'n found for {:?} in tcx.{}", def_id, descr);
-    }
-    let v = load_external();
-    map.borrow_mut().insert(def_id, v.clone());
-    v
+    map.memoize(def_id, || {
+        if def_id.is_local() {
+            panic!("No def'n found for {:?} in tcx.{}", def_id, descr);
+        }
+        load_external()
+    })
 }
 
 impl BorrowKind {
@@ -2064,14 +1950,17 @@ impl<'tcx> ctxt<'tcx> {
                     }) => {
                         true
                     }
-
+                    Some(&def::PathResolution { base_def: def::DefErr, .. })=> true,
                     Some(..) => false,
-
                     None => self.sess.span_bug(expr.span, &format!(
                         "no def for path {}", expr.id))
                 }
             }
 
+            hir::ExprType(ref e, _) => {
+                self.expr_is_lval(e)
+            }
+
             hir::ExprUnary(hir::UnDeref, _) |
             hir::ExprField(..) |
             hir::ExprTupField(..) |
@@ -2179,22 +2068,6 @@ impl<'tcx> ctxt<'tcx> {
         }
     }
 
-    pub fn trait_items(&self, trait_did: DefId) -> Rc<Vec<ImplOrTraitItem<'tcx>>> {
-        let mut trait_items = self.trait_items_cache.borrow_mut();
-        match trait_items.get(&trait_did).cloned() {
-            Some(trait_items) => trait_items,
-            None => {
-                let def_ids = self.trait_item_def_ids(trait_did);
-                let items: Rc<Vec<ImplOrTraitItem>> =
-                    Rc::new(def_ids.iter()
-                                   .map(|d| self.impl_or_trait_item(d.def_id()))
-                                   .collect());
-                trait_items.insert(trait_did, items.clone());
-                items
-            }
-        }
-    }
-
     pub fn trait_impl_polarity(&self, id: DefId) -> Option<hir::ImplPolarity> {
         if let Some(id) = self.map.as_local_node_id(id) {
             match self.map.find(id) {
@@ -2212,7 +2085,7 @@ impl<'tcx> ctxt<'tcx> {
     }
 
     pub fn custom_coerce_unsized_kind(&self, did: DefId) -> adjustment::CustomCoerceUnsized {
-        memoized(&self.custom_coerce_unsized_kinds, did, |did: DefId| {
+        self.custom_coerce_unsized_kinds.memoize(did, || {
             let (kind, src) = if did.krate != LOCAL_CRATE {
                 (self.sess.cstore.custom_coerce_unsized_kind(did), "external")
             } else {
@@ -2350,6 +2223,39 @@ impl<'tcx> ctxt<'tcx> {
             || self.sess.cstore.item_super_predicates(self, did))
     }
 
+    /// If `type_needs_drop` returns true, then `ty` is definitely
+    /// non-copy and *might* have a destructor attached; if it returns
+    /// false, then `ty` definitely has no destructor (i.e. no drop glue).
+    ///
+    /// (Note that this implies that if `ty` has a destructor attached,
+    /// then `type_needs_drop` will definitely return `true` for `ty`.)
+    pub fn type_needs_drop_given_env<'a>(&self,
+                                         ty: Ty<'tcx>,
+                                         param_env: &ty::ParameterEnvironment<'a,'tcx>) -> bool {
+        // Issue #22536: We first query type_moves_by_default.  It sees a
+        // normalized version of the type, and therefore will definitely
+        // know whether the type implements Copy (and thus needs no
+        // cleanup/drop/zeroing) ...
+        let implements_copy = !ty.moves_by_default(param_env, DUMMY_SP);
+
+        if implements_copy { return false; }
+
+        // ... (issue #22536 continued) but as an optimization, still use
+        // prior logic of asking if the `needs_drop` bit is set; we need
+        // not zero non-Copy types if they have no destructor.
+
+        // FIXME(#22815): Note that calling `ty::type_contents` is a
+        // conservative heuristic; it may report that `needs_drop` is set
+        // when actual type does not actually have a destructor associated
+        // with it. But since `ty` absolutely did not have the `Copy`
+        // bound attached (see above), it is sound to treat it as having a
+        // destructor (e.g. zero its memory on move).
+
+        let contents = ty.type_contents(self);
+        debug!("type_needs_drop ty={:?} contents={:?}", ty, contents);
+        contents.needs_drop(self)
+    }
+
     /// Get the attributes of a definition.
     pub fn get_attrs(&self, did: DefId) -> Cow<'tcx, [ast::Attribute]> {
         if let Some(id) = self.map.as_local_node_id(did) {
@@ -2375,19 +2281,6 @@ impl<'tcx> ctxt<'tcx> {
             || self.lookup_repr_hints(did).contains(&attr::ReprSimd)
     }
 
-    /// Obtain the representation annotation for a struct definition.
-    pub fn lookup_repr_hints(&self, did: DefId) -> Rc<Vec<attr::ReprAttr>> {
-        memoized(&self.repr_hint_cache, did, |did: DefId| {
-            Rc::new(if did.is_local() {
-                self.get_attrs(did).iter().flat_map(|meta| {
-                    attr::find_repr_attrs(self.sess.diagnostic(), meta).into_iter()
-                }).collect()
-            } else {
-                self.sess.cstore.repr_attrs(did)
-            })
-        })
-    }
-
     pub fn item_variances(&self, item_id: DefId) -> Rc<ItemVariances> {
         lookup_locally_or_in_crate_store(
             "item_variance_map", item_id, &self.item_variance_map,
@@ -2414,6 +2307,10 @@ impl<'tcx> ctxt<'tcx> {
             return
         }
 
+        // The primitive is not local, hence we are reading this out
+        // of metadata.
+        let _ignore = self.dep_graph.in_ignore();
+
         if self.populated_external_primitive_impls.borrow().contains(&primitive_def_id) {
             return
         }
@@ -2436,6 +2333,10 @@ impl<'tcx> ctxt<'tcx> {
             return
         }
 
+        // The type is not local, hence we are reading this out of
+        // metadata and don't need to track edges.
+        let _ignore = self.dep_graph.in_ignore();
+
         if self.populated_external_types.borrow().contains(&type_id) {
             return
         }
@@ -2461,6 +2362,10 @@ impl<'tcx> ctxt<'tcx> {
             return
         }
 
+        // The type is not local, hence we are reading this out of
+        // metadata and don't need to track edges.
+        let _ignore = self.dep_graph.in_ignore();
+
         let def = self.lookup_trait_def(trait_id);
         if def.flags.get().intersects(TraitFlags::IMPLS_VALID) {
             return;
@@ -2576,18 +2481,17 @@ impl<'tcx> ctxt<'tcx> {
     /// are no free type/lifetime parameters in scope.
     pub fn empty_parameter_environment<'a>(&'a self)
                                            -> ParameterEnvironment<'a,'tcx> {
+
+        // for an empty parameter environment, there ARE no free
+        // regions, so it shouldn't matter what we use for the free id
+        let free_id_outlive = self.region_maps.node_extent(ast::DUMMY_NODE_ID);
         ty::ParameterEnvironment { tcx: self,
                                    free_substs: Substs::empty(),
                                    caller_bounds: Vec::new(),
                                    implicit_region_bound: ty::ReEmpty,
                                    selection_cache: traits::SelectionCache::new(),
                                    evaluation_cache: traits::EvaluationCache::new(),
-
-                                   // for an empty parameter
-                                   // environment, there ARE no free
-                                   // regions, so it shouldn't matter
-                                   // what we use for the free id
-                                   free_id: ast::DUMMY_NODE_ID }
+                                   free_id_outlive: free_id_outlive }
     }
 
     /// Constructs and returns a substitution that can be applied to move from
@@ -2596,7 +2500,7 @@ impl<'tcx> ctxt<'tcx> {
     /// free parameters. Since we currently represent bound/free type
     /// parameters in the same way, this only has an effect on regions.
     pub fn construct_free_substs(&self, generics: &Generics<'tcx>,
-                                 free_id: NodeId) -> Substs<'tcx> {
+                                 free_id_outlive: CodeExtent) -> Substs<'tcx> {
         // map T => T
         let mut types = VecPerParamSpace::empty();
         for def in generics.types.as_slice() {
@@ -2605,8 +2509,6 @@ impl<'tcx> ctxt<'tcx> {
             types.push(def.space, self.mk_param_from_def(def));
         }
 
-        let free_id_outlive = self.region_maps.item_extent(free_id);
-
         // map bound 'a => free 'a
         let mut regions = VecPerParamSpace::empty();
         for def in generics.regions.as_slice() {
@@ -2623,20 +2525,21 @@ impl<'tcx> ctxt<'tcx> {
         }
     }
 
-    /// See `ParameterEnvironment` struct def'n for details
+    /// See `ParameterEnvironment` struct def'n for details.
+    /// If you were using `free_id: NodeId`, you might try `self.region_maps.item_extent(free_id)`
+    /// for the `free_id_outlive` parameter. (But note that that is not always quite right.)
     pub fn construct_parameter_environment<'a>(&'a self,
                                                span: Span,
                                                generics: &ty::Generics<'tcx>,
                                                generic_predicates: &ty::GenericPredicates<'tcx>,
-                                               free_id: NodeId)
+                                               free_id_outlive: CodeExtent)
                                                -> ParameterEnvironment<'a, 'tcx>
     {
         //
         // Construct the free substs.
         //
 
-        let free_substs = self.construct_free_substs(generics, free_id);
-        let free_id_outlive = self.region_maps.item_extent(free_id);
+        let free_substs = self.construct_free_substs(generics, free_id_outlive);
 
         //
         // Compute the bounds on Self and the type parameters.
@@ -2646,12 +2549,6 @@ impl<'tcx> ctxt<'tcx> {
         let bounds = self.liberate_late_bound_regions(free_id_outlive, &ty::Binder(bounds));
         let predicates = bounds.predicates.into_vec();
 
-        debug!("construct_parameter_environment: free_id={:?} free_subst={:?} predicates={:?}",
-               free_id,
-               free_substs,
-               predicates);
-
-        //
         // Finally, we have to normalize the bounds in the environment, in
         // case they contain any associated type projections. This process
         // can yield errors if the put in illegal associated types, like
@@ -2672,10 +2569,10 @@ impl<'tcx> ctxt<'tcx> {
             caller_bounds: predicates,
             selection_cache: traits::SelectionCache::new(),
             evaluation_cache: traits::EvaluationCache::new(),
-            free_id: free_id,
+            free_id_outlive: free_id_outlive,
         };
 
-        let cause = traits::ObligationCause::misc(span, free_id);
+        let cause = traits::ObligationCause::misc(span, free_id_outlive.node_id(&self.region_maps));
         traits::normalize_param_env_or_error(unnormalized_env, cause)
     }
 
@@ -2691,15 +2588,24 @@ impl<'tcx> ctxt<'tcx> {
     pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture> {
         Some(self.tables.borrow().upvar_capture_map.get(&upvar_id).unwrap().clone())
     }
+
+
+    pub fn visit_all_items_in_krate<V,F>(&self,
+                                         dep_node_fn: F,
+                                         visitor: &mut V)
+        where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx>
+    {
+        dep_graph::visit_all_items_in_krate(self, dep_node_fn, visitor);
+    }
 }
 
 /// The category of explicit self.
 #[derive(Clone, Copy, Eq, PartialEq, Debug)]
 pub enum ExplicitSelfCategory {
-    StaticExplicitSelfCategory,
-    ByValueExplicitSelfCategory,
-    ByReferenceExplicitSelfCategory(Region, hir::Mutability),
-    ByBoxExplicitSelfCategory,
+    Static,
+    ByValue,
+    ByReference(Region, hir::Mutability),
+    ByBox,
 }
 
 /// A free variable referred to in a function.
@@ -2757,73 +2663,3 @@ impl<'tcx> ctxt<'tcx> {
         trait_ref.substs.clone().with_method(meth_tps, meth_regions)
     }
 }
-
-/// An "escaping region" is a bound region whose binder is not part of `t`.
-///
-/// So, for example, consider a type like the following, which has two binders:
-///
-///    for<'a> fn(x: for<'b> fn(&'a isize, &'b isize))
-///    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope
-///                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~  inner scope
-///
-/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the
-/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner
-/// fn type*, that type has an escaping region: `'a`.
-///
-/// Note that what I'm calling an "escaping region" is often just called a "free region". However,
-/// we already use the term "free region". It refers to the regions that we use to represent bound
-/// regions on a fn definition while we are typechecking its body.
-///
-/// To clarify, conceptually there is no particular difference between an "escaping" region and a
-/// "free" region. However, there is a big difference in practice. Basically, when "entering" a
-/// binding level, one is generally required to do some sort of processing to a bound region, such
-/// as replacing it with a fresh/skolemized region, or making an entry in the environment to
-/// represent the scope to which it is attached, etc. An escaping region represents a bound region
-/// for which this processing has not yet been done.
-pub trait RegionEscape {
-    fn has_escaping_regions(&self) -> bool {
-        self.has_regions_escaping_depth(0)
-    }
-
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool;
-}
-
-pub trait HasTypeFlags {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool;
-    fn has_projection_types(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_PROJECTION)
-    }
-    fn references_error(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_TY_ERR)
-    }
-    fn has_param_types(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_PARAMS)
-    }
-    fn has_self_ty(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_SELF)
-    }
-    fn has_infer_types(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_TY_INFER)
-    }
-    fn needs_infer(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_RE_INFER)
-    }
-    fn needs_subst(&self) -> bool {
-        self.has_type_flags(TypeFlags::NEEDS_SUBST)
-    }
-    fn has_closure_types(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_TY_CLOSURE)
-    }
-    fn has_erasable_regions(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND |
-                            TypeFlags::HAS_RE_INFER |
-                            TypeFlags::HAS_FREE_REGIONS)
-    }
-    /// 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
-    /// false.
-    fn is_global(&self) -> bool {
-        !self.has_type_flags(TypeFlags::HAS_LOCAL_NAMES)
-    }
-}
index ea092ed977edaa11f044c618c8a20402a8e01d64..fc20c1bcb85feec89e73ba01e7100ce15a6f56a2 100644 (file)
@@ -13,7 +13,7 @@
 // RFC for reference.
 
 use middle::infer::InferCtxt;
-use middle::ty::{self, RegionEscape, Ty};
+use middle::ty::{self, Ty, TypeFoldable};
 
 #[derive(Debug)]
 pub enum Component<'tcx> {
@@ -53,12 +53,6 @@ pub enum Component<'tcx> {
     // them. This gives us room to improve the regionck reasoning in
     // the future without breaking backwards compat.
     EscapingProjection(Vec<Component<'tcx>>),
-
-    // This is a temporary marker indicating "outlives components"
-    // that are due to the new rules introduced by RFC 1214.  For the
-    // time being, violations of these requirements generally induce
-    // warnings, not errors.
-    RFC1214(Vec<Component<'tcx>>),
 }
 
 /// Returns all the things that must outlive `'a` for the condition
@@ -124,20 +118,6 @@ fn compute_components<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
             }
         }
 
-        // Bare functions and traits are both binders. In the RFC,
-        // this means we would add the bound regions to the "bound
-        // regions list".  In our representation, no such list is
-        // maintained explicitly, because bound regions themselves can
-        // be readily identified. However, because the outlives
-        // relation did not used to be applied to fn/trait-object
-        // arguments, we wrap the resulting components in an RFC1214
-        // wrapper so we can issue warnings.
-        ty::TyBareFn(..) | ty::TyTrait(..) => {
-            // OutlivesFunction, OutlivesObject, OutlivesFragment
-            let subcomponents = capture_components(infcx, ty);
-            out.push(Component::RFC1214(subcomponents));
-        }
-
         // OutlivesTypeParameterEnv -- the actual checking that `X:'a`
         // is implied by the environment is done in regionck.
         ty::TyParam(p) => {
@@ -202,7 +182,15 @@ fn compute_components<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
         ty::TyRawPtr(..) |      // ...
         ty::TyRef(..) |         // OutlivesReference
         ty::TyTuple(..) |       // ...
+        ty::TyBareFn(..) |      // OutlivesFunction (*)
+        ty::TyTrait(..) |       // OutlivesObject, OutlivesFragment (*)
         ty::TyError => {
+            // (*) Bare functions and traits are both binders. In the
+            // RFC, this means we would add the bound regions to the
+            // "bound regions list".  In our representation, no such
+            // list is maintained explicitly, because bound regions
+            // themselves can be readily identified.
+
             push_region_constraints(out, ty.regions());
             for subty in ty.walk_shallow() {
                 compute_components(infcx, subty, out);
index ff0a9789cf1f8cad49abd0d1654c9a1a7ec40b82..46bc13bd5988be5e27302c6207b0492c8b6dd371 100644 (file)
@@ -15,9 +15,8 @@
 
 use middle::def_id::DefId;
 use middle::subst::{ErasedRegions, NonerasedRegions, ParamSpace, Substs};
-use middle::ty::{self, HasTypeFlags, Ty};
+use middle::ty::{self, Ty, TypeFoldable};
 use middle::ty::error::{ExpectedFound, TypeError};
-use middle::ty::fold::TypeFoldable;
 use std::rc::Rc;
 use syntax::abi;
 use rustc_front::hir as ast;
@@ -80,7 +79,7 @@ pub trait TypeRelation<'a,'tcx> : Sized {
         where T: Relate<'a,'tcx>;
 }
 
-pub trait Relate<'a,'tcx>: TypeFoldable<'tcx> + HasTypeFlags {
+pub trait Relate<'a,'tcx>: TypeFoldable<'tcx> {
     fn relate<R:TypeRelation<'a,'tcx>>(relation: &mut R,
                                        a: &Self,
                                        b: &Self)
index 41303b46dfd09edf37c27c7e0ce95babdbc7a728..01b2bd36b4f07a7f7e4b3ae73b0b5083132106a6 100644 (file)
 
 use middle::subst::{self, VecPerParamSpace};
 use middle::traits;
-use middle::ty::{self, TraitRef, Ty, TypeAndMut};
-use middle::ty::{HasTypeFlags, Lift, TypeFlags, RegionEscape};
-use middle::ty::fold::{TypeFoldable, TypeFolder};
+use middle::ty::{self, Lift, TraitRef, Ty};
+use middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
 
 use std::rc::Rc;
 use syntax::abi;
-use syntax::owned_slice::OwnedSlice;
+use syntax::ptr::P;
 
 use rustc_front::hir;
 
-// FIXME(#20298) -- all of these traits basically walk various
-// structures to test whether types/regions are reachable with various
-// properties. It should be possible to express them in terms of one
-// common "walker" trait or something.
-
-impl<'tcx> RegionEscape for Ty<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.region_depth > depth
-    }
-}
-
-impl<'tcx> RegionEscape for ty::TraitTy<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.principal.has_regions_escaping_depth(depth) ||
-            self.bounds.has_regions_escaping_depth(depth)
-    }
-}
-
-impl<'tcx> RegionEscape for ty::ExistentialBounds<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.region_bound.has_regions_escaping_depth(depth) ||
-            self.projection_bounds.has_regions_escaping_depth(depth)
-    }
-}
-
-impl<'tcx> RegionEscape for ty::InstantiatedPredicates<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.predicates.has_regions_escaping_depth(depth)
-    }
-}
-
-impl<'tcx> RegionEscape for subst::Substs<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.types.has_regions_escaping_depth(depth) ||
-            self.regions.has_regions_escaping_depth(depth)
-    }
-}
-
-impl<'tcx> RegionEscape for ty::ClosureSubsts<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.func_substs.has_regions_escaping_depth(depth) ||
-            self.upvar_tys.iter().any(|t| t.has_regions_escaping_depth(depth))
-    }
-}
-
-impl<T:RegionEscape> RegionEscape for Vec<T> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.iter().any(|t| t.has_regions_escaping_depth(depth))
-    }
-}
-
-impl<'tcx> RegionEscape for ty::FnSig<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.inputs.has_regions_escaping_depth(depth) ||
-            self.output.has_regions_escaping_depth(depth)
-    }
-}
-
-impl<'tcx,T:RegionEscape> RegionEscape for VecPerParamSpace<T> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.iter_enumerated().any(|(space, _, t)| {
-            if space == subst::FnSpace {
-                t.has_regions_escaping_depth(depth+1)
-            } else {
-                t.has_regions_escaping_depth(depth)
-            }
-        })
-    }
-}
-
-impl<'tcx> RegionEscape for ty::TypeScheme<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.ty.has_regions_escaping_depth(depth)
-    }
-}
-
-impl RegionEscape for ty::Region {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.escapes_depth(depth)
-    }
-}
-
-impl<'tcx> RegionEscape for ty::GenericPredicates<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.predicates.has_regions_escaping_depth(depth)
-    }
-}
-
-impl<'tcx> RegionEscape for ty::Predicate<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        match *self {
-            ty::Predicate::Trait(ref data) => data.has_regions_escaping_depth(depth),
-            ty::Predicate::Equate(ref data) => data.has_regions_escaping_depth(depth),
-            ty::Predicate::RegionOutlives(ref data) => data.has_regions_escaping_depth(depth),
-            ty::Predicate::TypeOutlives(ref data) => data.has_regions_escaping_depth(depth),
-            ty::Predicate::Projection(ref data) => data.has_regions_escaping_depth(depth),
-            ty::Predicate::WellFormed(ty) => ty.has_regions_escaping_depth(depth),
-            ty::Predicate::ObjectSafe(_trait_def_id) => false,
-        }
-    }
-}
-
-impl<'tcx> RegionEscape for TraitRef<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.substs.types.iter().any(|t| t.has_regions_escaping_depth(depth)) ||
-            self.substs.regions.has_regions_escaping_depth(depth)
-    }
-}
-
-impl<'tcx> RegionEscape for subst::RegionSubsts {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        match *self {
-            subst::ErasedRegions => false,
-            subst::NonerasedRegions(ref r) => {
-                r.iter().any(|t| t.has_regions_escaping_depth(depth))
-            }
-        }
-    }
-}
-
-impl<'tcx,T:RegionEscape> RegionEscape for ty::Binder<T> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.0.has_regions_escaping_depth(depth + 1)
-    }
-}
-
-impl<'tcx> RegionEscape for ty::FnOutput<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        match *self {
-            ty::FnConverging(t) => t.has_regions_escaping_depth(depth),
-            ty::FnDiverging => false
-        }
-    }
-}
-
-impl<'tcx> RegionEscape for ty::EquatePredicate<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.0.has_regions_escaping_depth(depth) || self.1.has_regions_escaping_depth(depth)
-    }
-}
-
-impl<'tcx> RegionEscape for ty::TraitPredicate<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.trait_ref.has_regions_escaping_depth(depth)
-    }
-}
-
-impl<T:RegionEscape,U:RegionEscape> RegionEscape for ty::OutlivesPredicate<T,U> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.0.has_regions_escaping_depth(depth) || self.1.has_regions_escaping_depth(depth)
-    }
-}
-
-impl<'tcx> RegionEscape for ty::ProjectionPredicate<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.projection_ty.has_regions_escaping_depth(depth) ||
-            self.ty.has_regions_escaping_depth(depth)
-    }
-}
-
-impl<'tcx> RegionEscape for ty::ProjectionTy<'tcx> {
-    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
-        self.trait_ref.has_regions_escaping_depth(depth)
-    }
-}
-
-impl HasTypeFlags for () {
-    fn has_type_flags(&self, _flags: TypeFlags) -> bool {
-        false
-    }
-}
-
-impl<'tcx,T:HasTypeFlags> HasTypeFlags for Vec<T> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self[..].has_type_flags(flags)
-    }
-}
-
-impl<'tcx,T:HasTypeFlags> HasTypeFlags for [T] {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.iter().any(|p| p.has_type_flags(flags))
-    }
-}
-
-impl<'tcx,T:HasTypeFlags> HasTypeFlags for VecPerParamSpace<T> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.iter().any(|p| p.has_type_flags(flags))
-    }
-}
-
-impl HasTypeFlags for abi::Abi {
-    fn has_type_flags(&self, _flags: TypeFlags) -> bool {
-        false
-    }
-}
-
-impl HasTypeFlags for hir::Unsafety {
-    fn has_type_flags(&self, _flags: TypeFlags) -> bool {
-        false
-    }
-}
-
-impl HasTypeFlags for ty::BuiltinBounds {
-    fn has_type_flags(&self, _flags: TypeFlags) -> bool {
-        false
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::ClosureTy<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.sig.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::ClosureUpvar<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.ty.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::ExistentialBounds<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.projection_bounds.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::InstantiatedPredicates<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.predicates.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::Predicate<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        match *self {
-            ty::Predicate::Trait(ref data) => data.has_type_flags(flags),
-            ty::Predicate::Equate(ref data) => data.has_type_flags(flags),
-            ty::Predicate::RegionOutlives(ref data) => data.has_type_flags(flags),
-            ty::Predicate::TypeOutlives(ref data) => data.has_type_flags(flags),
-            ty::Predicate::Projection(ref data) => data.has_type_flags(flags),
-            ty::Predicate::WellFormed(data) => data.has_type_flags(flags),
-            ty::Predicate::ObjectSafe(_trait_def_id) => false,
-        }
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::TraitPredicate<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.trait_ref.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::EquatePredicate<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.0.has_type_flags(flags) || self.1.has_type_flags(flags)
-    }
-}
-
-impl HasTypeFlags for ty::Region {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        if flags.intersects(TypeFlags::HAS_LOCAL_NAMES) {
-            // does this represent a region that cannot be named in a global
-            // way? used in fulfillment caching.
-            match *self {
-                ty::ReStatic | ty::ReEmpty => {}
-                _ => return true
-            }
-        }
-        if flags.intersects(TypeFlags::HAS_RE_INFER) {
-            match *self {
-                ty::ReVar(_) | ty::ReSkolemized(..) => { return true }
-                _ => {}
-            }
-        }
-        false
-    }
-}
-
-impl<T:HasTypeFlags,U:HasTypeFlags> HasTypeFlags for ty::OutlivesPredicate<T,U> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.0.has_type_flags(flags) || self.1.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::ProjectionPredicate<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.projection_ty.has_type_flags(flags) || self.ty.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::ProjectionTy<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.trait_ref.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for Ty<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.flags.get().intersects(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for TypeAndMut<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.ty.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for TraitRef<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.substs.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for subst::Substs<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.types.has_type_flags(flags) || match self.regions {
-            subst::ErasedRegions => false,
-            subst::NonerasedRegions(ref r) => r.has_type_flags(flags)
-        }
-    }
-}
-
-impl<'tcx,T> HasTypeFlags for Option<T>
-    where T : HasTypeFlags
-{
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.iter().any(|t| t.has_type_flags(flags))
-    }
-}
-
-impl<'tcx,T> HasTypeFlags for Rc<T>
-    where T : HasTypeFlags
-{
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        (**self).has_type_flags(flags)
-    }
-}
-
-impl<'tcx,T> HasTypeFlags for Box<T>
-    where T : HasTypeFlags
-{
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        (**self).has_type_flags(flags)
-    }
-}
-
-impl<T> HasTypeFlags for ty::Binder<T>
-    where T : HasTypeFlags
-{
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.0.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::FnOutput<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        match *self {
-            ty::FnConverging(t) => t.has_type_flags(flags),
-            ty::FnDiverging => false,
-        }
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::FnSig<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.inputs.iter().any(|t| t.has_type_flags(flags)) ||
-            self.output.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::BareFnTy<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.sig.has_type_flags(flags)
-    }
-}
-
-impl<'tcx> HasTypeFlags for ty::ClosureSubsts<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.func_substs.has_type_flags(flags) ||
-            self.upvar_tys.iter().any(|t| t.has_type_flags(flags))
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////
 // Lift implementations
 
@@ -508,9 +123,13 @@ macro_rules! CopyImpls {
     ($($ty:ty),+) => {
         $(
             impl<'tcx> TypeFoldable<'tcx> for $ty {
-                fn fold_with<F:TypeFolder<'tcx>>(&self, _: &mut F) -> $ty {
+                fn super_fold_with<F:TypeFolder<'tcx>>(&self, _: &mut F) -> $ty {
                     *self
                 }
+
+                fn super_visit_with<F: TypeVisitor<'tcx>>(&self, _: &mut F) -> bool {
+                    false
+                }
             }
         )+
     }
@@ -519,50 +138,88 @@ macro_rules! CopyImpls {
 CopyImpls! { (), hir::Unsafety, abi::Abi }
 
 impl<'tcx, T:TypeFoldable<'tcx>, U:TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T, U) {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> (T, U) {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> (T, U) {
         (self.0.fold_with(folder), self.1.fold_with(folder))
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.0.visit_with(visitor) || self.1.visit_with(visitor)
+    }
 }
 
 impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Option<T> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Option<T> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         self.as_ref().map(|t| t.fold_with(folder))
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.iter().any(|t| t.visit_with(visitor))
+    }
 }
 
 impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc<T> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Rc<T> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         Rc::new((**self).fold_with(folder))
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        (**self).visit_with(visitor)
+    }
 }
 
 impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<T> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Box<T> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         let content: T = (**self).fold_with(folder);
         box content
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        (**self).visit_with(visitor)
+    }
 }
 
 impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Vec<T> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Vec<T> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         self.iter().map(|t| t.fold_with(folder)).collect()
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.iter().any(|t| t.visit_with(visitor))
+    }
 }
 
 impl<'tcx, T:TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder<T> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Binder<T> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        folder.enter_region_binder();
+        let result = ty::Binder(self.0.fold_with(folder));
+        folder.exit_region_binder();
+        result
+    }
+
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         folder.fold_binder(self)
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        visitor.enter_region_binder();
+        if self.0.visit_with(visitor) { return true }
+        visitor.exit_region_binder();
+        false
+    }
 }
 
-impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for OwnedSlice<T> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> OwnedSlice<T> {
+impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for P<[T]> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         self.iter().map(|t| t.fold_with(folder)).collect()
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.iter().any(|t| t.visit_with(visitor))
+    }
 }
 
 impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for VecPerParamSpace<T> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> VecPerParamSpace<T> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
 
         // Things in the Fn space take place under an additional level
         // of region binding relative to the other spaces. This is
@@ -582,100 +239,325 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for VecPerParamSpace<T> {
         }
         result
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        let mut entered_region_binder = false;
+        let result = self.iter_enumerated().any(|(space, index, t)| {
+            if space == subst::FnSpace && index == 0 {
+                visitor.enter_region_binder();
+                entered_region_binder = true;
+            }
+            t.visit_with(visitor)
+        });
+        if entered_region_binder {
+            visitor.exit_region_binder();
+        }
+        result
+    }
+}
+
+impl<'tcx> TypeFoldable<'tcx> for ty::TraitTy<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        ty::TraitTy {
+            principal: self.principal.fold_with(folder),
+            bounds: self.bounds.fold_with(folder),
+        }
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.principal.visit_with(visitor) || self.bounds.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Ty<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        let sty = match self.sty {
+            ty::TyBox(typ) => ty::TyBox(typ.fold_with(folder)),
+            ty::TyRawPtr(ref tm) => ty::TyRawPtr(tm.fold_with(folder)),
+            ty::TyArray(typ, sz) => ty::TyArray(typ.fold_with(folder), sz),
+            ty::TySlice(typ) => ty::TySlice(typ.fold_with(folder)),
+            ty::TyEnum(tid, ref substs) => {
+                let substs = substs.fold_with(folder);
+                ty::TyEnum(tid, folder.tcx().mk_substs(substs))
+            }
+            ty::TyTrait(ref trait_ty) => ty::TyTrait(trait_ty.fold_with(folder)),
+            ty::TyTuple(ref ts) => ty::TyTuple(ts.fold_with(folder)),
+            ty::TyBareFn(opt_def_id, ref f) => {
+                let bfn = f.fold_with(folder);
+                ty::TyBareFn(opt_def_id, folder.tcx().mk_bare_fn(bfn))
+            }
+            ty::TyRef(r, ref tm) => {
+                let r = r.fold_with(folder);
+                ty::TyRef(folder.tcx().mk_region(r), tm.fold_with(folder))
+            }
+            ty::TyStruct(did, ref substs) => {
+                let substs = substs.fold_with(folder);
+                ty::TyStruct(did, folder.tcx().mk_substs(substs))
+            }
+            ty::TyClosure(did, ref substs) => {
+                ty::TyClosure(did, substs.fold_with(folder))
+            }
+            ty::TyProjection(ref data) => ty::TyProjection(data.fold_with(folder)),
+            ty::TyBool | ty::TyChar | ty::TyStr | ty::TyInt(_) |
+            ty::TyUint(_) | ty::TyFloat(_) | ty::TyError | ty::TyInfer(_) |
+            ty::TyParam(..) => self.sty.clone(),
+        };
+        folder.tcx().mk_ty(sty)
+    }
+
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         folder.fold_ty(*self)
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        match self.sty {
+            ty::TyBox(typ) => typ.visit_with(visitor),
+            ty::TyRawPtr(ref tm) => tm.visit_with(visitor),
+            ty::TyArray(typ, _sz) => typ.visit_with(visitor),
+            ty::TySlice(typ) => typ.visit_with(visitor),
+            ty::TyEnum(_tid, ref substs) => substs.visit_with(visitor),
+            ty::TyTrait(ref trait_ty) => trait_ty.visit_with(visitor),
+            ty::TyTuple(ref ts) => ts.visit_with(visitor),
+            ty::TyBareFn(_opt_def_id, ref f) => f.visit_with(visitor),
+            ty::TyRef(r, ref tm) => r.visit_with(visitor) || tm.visit_with(visitor),
+            ty::TyStruct(_did, ref substs) => substs.visit_with(visitor),
+            ty::TyClosure(_did, ref substs) => substs.visit_with(visitor),
+            ty::TyProjection(ref data) => data.visit_with(visitor),
+            ty::TyBool | ty::TyChar | ty::TyStr | ty::TyInt(_) |
+            ty::TyUint(_) | ty::TyFloat(_) | ty::TyError | ty::TyInfer(_) |
+            ty::TyParam(..) => false,
+        }
+    }
+
+    fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        visitor.visit_ty(self)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::BareFnTy<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::BareFnTy<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        ty::BareFnTy { sig: self.sig.fold_with(folder),
+                       abi: self.abi,
+                       unsafety: self.unsafety }
+    }
+
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         folder.fold_bare_fn_ty(self)
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.sig.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::ClosureTy<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ClosureTy<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+       ty::ClosureTy {
+            sig: self.sig.fold_with(folder),
+            unsafety: self.unsafety,
+            abi: self.abi,
+        }
+    }
+
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         folder.fold_closure_ty(self)
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.sig.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::TypeAndMut<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::TypeAndMut<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        ty::TypeAndMut { ty: self.ty.fold_with(folder), mutbl: self.mutbl }
+    }
+
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         folder.fold_mt(self)
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.ty.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::FnOutput<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnOutput<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        match *self {
+            ty::FnConverging(ref ty) => ty::FnConverging(ty.fold_with(folder)),
+            ty::FnDiverging => ty::FnDiverging
+        }
+    }
+
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         folder.fold_output(self)
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        match *self {
+            ty::FnConverging(ref ty) => ty.visit_with(visitor),
+            ty::FnDiverging => false,
+        }
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnSig<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        ty::FnSig { inputs: self.inputs.fold_with(folder),
+                    output: self.output.fold_with(folder),
+                    variadic: self.variadic }
+    }
+
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         folder.fold_fn_sig(self)
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.inputs.visit_with(visitor) || self.output.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::TraitRef<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::TraitRef<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        let substs = self.substs.fold_with(folder);
+        ty::TraitRef {
+            def_id: self.def_id,
+            substs: folder.tcx().mk_substs(substs),
+        }
+    }
+
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         folder.fold_trait_ref(self)
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.substs.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::Region {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Region {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _folder: &mut F) -> Self {
+        *self
+    }
+
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         folder.fold_region(*self)
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> bool {
+        false
+    }
+
+    fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        visitor.visit_region(*self)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for subst::Substs<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> subst::Substs<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        let regions = match self.regions {
+            subst::ErasedRegions => subst::ErasedRegions,
+            subst::NonerasedRegions(ref regions) => {
+                subst::NonerasedRegions(regions.fold_with(folder))
+            }
+        };
+
+        subst::Substs { regions: regions,
+                        types: self.types.fold_with(folder) }
+    }
+
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         folder.fold_substs(self)
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.types.visit_with(visitor) || match self.regions {
+            subst::ErasedRegions => false,
+            subst::NonerasedRegions(ref regions) => regions.visit_with(visitor),
+        }
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::ClosureSubsts<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ClosureSubsts<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         let func_substs = self.func_substs.fold_with(folder);
         ty::ClosureSubsts {
             func_substs: folder.tcx().mk_substs(func_substs),
             upvar_tys: self.upvar_tys.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.func_substs.visit_with(visitor) || self.upvar_tys.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::ItemSubsts<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ItemSubsts<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::ItemSubsts {
             substs: self.substs.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.substs.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::AutoRef<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::adjustment::AutoRef<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        match *self {
+            ty::adjustment::AutoPtr(r, m) => {
+                let r = r.fold_with(folder);
+                ty::adjustment::AutoPtr(folder.tcx().mk_region(r), m)
+            }
+            ty::adjustment::AutoUnsafe(m) => ty::adjustment::AutoUnsafe(m)
+        }
+    }
+
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         folder.fold_autoref(self)
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        match *self {
+            ty::adjustment::AutoPtr(r, _m) => r.visit_with(visitor),
+            ty::adjustment::AutoUnsafe(_m) => false,
+        }
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::BuiltinBounds {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, _folder: &mut F) -> ty::BuiltinBounds {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _folder: &mut F) -> Self {
         *self
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> bool {
+        false
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::ExistentialBounds<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ExistentialBounds<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        ty::ExistentialBounds {
+            region_bound: self.region_bound.fold_with(folder),
+            builtin_bounds: self.builtin_bounds,
+            projection_bounds: self.projection_bounds.fold_with(folder),
+        }
+    }
+
+    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         folder.fold_existential_bounds(self)
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.region_bound.visit_with(visitor) || self.projection_bounds.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::TypeParameterDef<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::TypeParameterDef<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::TypeParameterDef {
             name: self.name,
             def_id: self.def_id,
@@ -686,10 +568,15 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TypeParameterDef<'tcx> {
             object_lifetime_default: self.object_lifetime_default.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.default.visit_with(visitor) ||
+            self.object_lifetime_default.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::ObjectLifetimeDefault {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ObjectLifetimeDefault {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         match *self {
             ty::ObjectLifetimeDefault::Ambiguous =>
                 ty::ObjectLifetimeDefault::Ambiguous,
@@ -701,10 +588,17 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ObjectLifetimeDefault {
                 ty::ObjectLifetimeDefault::Specific(r.fold_with(folder)),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        match *self {
+            ty::ObjectLifetimeDefault::Specific(r) => r.visit_with(visitor),
+            _ => false,
+        }
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::RegionParameterDef {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::RegionParameterDef {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::RegionParameterDef {
             name: self.name,
             def_id: self.def_id,
@@ -713,27 +607,39 @@ impl<'tcx> TypeFoldable<'tcx> for ty::RegionParameterDef {
             bounds: self.bounds.fold_with(folder)
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.bounds.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::Generics<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Generics<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::Generics {
             types: self.types.fold_with(folder),
             regions: self.regions.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.types.visit_with(visitor) || self.regions.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::GenericPredicates<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::GenericPredicates<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::GenericPredicates {
             predicates: self.predicates.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.predicates.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Predicate<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         match *self {
             ty::Predicate::Trait(ref a) =>
                 ty::Predicate::Trait(a.fold_with(folder)),
@@ -751,71 +657,111 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
                 ty::Predicate::ObjectSafe(trait_def_id),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        match *self {
+            ty::Predicate::Trait(ref a) => a.visit_with(visitor),
+            ty::Predicate::Equate(ref binder) => binder.visit_with(visitor),
+            ty::Predicate::RegionOutlives(ref binder) => binder.visit_with(visitor),
+            ty::Predicate::TypeOutlives(ref binder) => binder.visit_with(visitor),
+            ty::Predicate::Projection(ref binder) => binder.visit_with(visitor),
+            ty::Predicate::WellFormed(data) => data.visit_with(visitor),
+            ty::Predicate::ObjectSafe(_trait_def_id) => false,
+        }
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::ProjectionPredicate<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ProjectionPredicate<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::ProjectionPredicate {
             projection_ty: self.projection_ty.fold_with(folder),
             ty: self.ty.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.projection_ty.visit_with(visitor) || self.ty.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::ProjectionTy<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ProjectionTy<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::ProjectionTy {
             trait_ref: self.trait_ref.fold_with(folder),
             item_name: self.item_name,
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.trait_ref.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::InstantiatedPredicates<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::InstantiatedPredicates<'tcx> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::InstantiatedPredicates {
             predicates: self.predicates.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.predicates.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::EquatePredicate<'tcx> {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::EquatePredicate<'tcx> {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::EquatePredicate(self.0.fold_with(folder),
                             self.1.fold_with(folder))
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.0.visit_with(visitor) || self.1.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::TraitPredicate<'tcx> {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::TraitPredicate<'tcx> {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::TraitPredicate {
             trait_ref: self.trait_ref.fold_with(folder)
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.trait_ref.visit_with(visitor)
+    }
 }
 
 impl<'tcx,T,U> TypeFoldable<'tcx> for ty::OutlivesPredicate<T,U>
     where T : TypeFoldable<'tcx>,
           U : TypeFoldable<'tcx>,
 {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::OutlivesPredicate<T,U> {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::OutlivesPredicate(self.0.fold_with(folder),
                               self.1.fold_with(folder))
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.0.visit_with(visitor) || self.1.visit_with(visitor)
+    }
 }
 
 impl<'tcx> TypeFoldable<'tcx> for ty::ClosureUpvar<'tcx> {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ClosureUpvar<'tcx> {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::ClosureUpvar {
             def: self.def,
             span: self.span,
             ty: self.ty.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.ty.visit_with(visitor)
+    }
 }
 
 impl<'a, 'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'a, 'tcx> where 'tcx: 'a {
-    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ParameterEnvironment<'a, 'tcx> {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         ty::ParameterEnvironment {
             tcx: self.tcx,
             free_substs: self.free_substs.fold_with(folder),
@@ -823,7 +769,26 @@ impl<'a, 'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'a, 'tcx> where '
             caller_bounds: self.caller_bounds.fold_with(folder),
             selection_cache: traits::SelectionCache::new(),
             evaluation_cache: traits::EvaluationCache::new(),
-            free_id: self.free_id,
+            free_id_outlive: self.free_id_outlive,
+        }
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.free_substs.visit_with(visitor) ||
+            self.implicit_region_bound.visit_with(visitor) ||
+            self.caller_bounds.visit_with(visitor)
+    }
+}
+
+impl<'tcx> TypeFoldable<'tcx> for ty::TypeScheme<'tcx>  {
+    fn super_fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
+        ty::TypeScheme {
+            generics: self.generics.fold_with(folder),
+            ty: self.ty.fold_with(folder),
         }
     }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.generics.visit_with(visitor) || self.ty.visit_with(visitor)
+    }
 }
index 425a324c7e084d23503e353fe5effed6c7fc5093..6d40d377b7852920e860917bdc342ace324976ec 100644 (file)
 
 //! This module contains TypeVariants and its major components
 
+use middle::cstore;
 use middle::def_id::DefId;
 use middle::region;
 use middle::subst::{self, Substs};
 use middle::traits;
-use middle::ty::{self, AdtDef, TypeFlags, Ty, TyS};
-use middle::ty::{RegionEscape, ToPredicate};
+use middle::ty::{self, AdtDef, ToPredicate, TypeFlags, Ty, TyS, TypeFoldable};
 use util::common::ErrorReported;
 
 use collections::enum_set::{self, EnumSet, CLike};
@@ -26,6 +26,8 @@ use syntax::abi;
 use syntax::ast::{self, Name};
 use syntax::parse::token::special_idents;
 
+use serialize::{Decodable, Decoder};
+
 use rustc_front::hir;
 
 use self::FnOutput::*;
@@ -233,7 +235,7 @@ pub enum TypeVariants<'tcx> {
 /// closure C wind up influencing the decisions we ought to make for
 /// closure C (which would then require fixed point iteration to
 /// handle). Plus it fixes an ICE. :P
-#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 pub struct ClosureSubsts<'tcx> {
     /// Lifetime and type parameters from the enclosing function.
     /// These are separated out because trans wants to pass them around
@@ -246,6 +248,23 @@ pub struct ClosureSubsts<'tcx> {
     pub upvar_tys: Vec<Ty<'tcx>>
 }
 
+impl<'tcx> Decodable for &'tcx ClosureSubsts<'tcx> {
+    fn decode<S: Decoder>(s: &mut S) -> Result<&'tcx ClosureSubsts<'tcx>, S::Error> {
+        let closure_substs = try! { Decodable::decode(s) };
+        let dummy_def_id: DefId = unsafe { mem::zeroed() };
+
+        cstore::tls::with_decoding_context(s, |dcx, _| {
+            // Intern the value
+            let ty = dcx.tcx().mk_closure_from_closure_substs(dummy_def_id,
+                                                              Box::new(closure_substs));
+            match ty.sty {
+                TyClosure(_, ref closure_substs) => Ok(&**closure_substs),
+                _ => unreachable!()
+            }
+        })
+    }
+}
+
 #[derive(Clone, PartialEq, Eq, Hash)]
 pub struct TraitTy<'tcx> {
     pub principal: ty::PolyTraitRef<'tcx>,
@@ -434,7 +453,7 @@ pub struct ClosureTy<'tcx> {
     pub sig: PolyFnSig<'tcx>,
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 pub enum FnOutput<'tcx> {
     FnConverging(Ty<'tcx>),
     FnDiverging
@@ -632,7 +651,7 @@ pub struct DebruijnIndex {
 ///
 /// [1] http://smallcultfollowing.com/babysteps/blog/2013/10/29/intermingled-parameter-lists/
 /// [2] http://smallcultfollowing.com/babysteps/blog/2013/11/04/intermingled-parameter-lists/
-#[derive(Clone, PartialEq, Eq, Hash, Copy)]
+#[derive(Clone, PartialEq, Eq, Hash, Copy, RustcEncodable, RustcDecodable)]
 pub enum Region {
     // Region bound in a type or fn declaration which will be
     // substituted 'early' -- that is, at the same time when type
@@ -675,7 +694,6 @@ pub enum Region {
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
 pub struct EarlyBoundRegion {
-    pub def_id: DefId,
     pub space: subst::ParamSpace,
     pub index: u32,
     pub name: Name,
@@ -701,7 +719,7 @@ pub struct RegionVid {
     pub index: u32
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub struct SkolemizedRegionVid {
     pub index: u32
 }
@@ -892,6 +910,13 @@ impl<'tcx> TyS<'tcx> {
         }
     }
 
+    pub fn is_primitive(&self) -> bool {
+        match self.sty {
+            TyBool | TyChar | TyInt(_) | TyUint(_) | TyFloat(_) => true,
+            _ => false,
+        }
+    }
+
     pub fn is_ty_var(&self) -> bool {
         match self.sty {
             TyInfer(TyVar(_)) => true,
diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs
new file mode 100644 (file)
index 0000000..db001ce
--- /dev/null
@@ -0,0 +1,226 @@
+// 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 dep_graph::DepNode;
+use middle::def_id::DefId;
+use middle::ty;
+use middle::ty::fast_reject;
+use middle::ty::Ty;
+use std::borrow::{Borrow};
+use std::cell::{Cell, Ref, RefCell};
+use syntax::ast::Name;
+use rustc_front::hir;
+use util::nodemap::FnvHashMap;
+
+/// As `TypeScheme` but for a trait ref.
+pub struct TraitDef<'tcx> {
+    pub unsafety: hir::Unsafety,
+
+    /// If `true`, then this trait had the `#[rustc_paren_sugar]`
+    /// attribute, indicating that it should be used with `Foo()`
+    /// sugar. This is a temporary thing -- eventually any trait wil
+    /// be usable with the sugar (or without it).
+    pub paren_sugar: bool,
+
+    /// Generic type definitions. Note that `Self` is listed in here
+    /// as having a single bound, the trait itself (e.g., in the trait
+    /// `Eq`, there is a single bound `Self : Eq`). This is so that
+    /// default methods get to assume that the `Self` parameters
+    /// implements the trait.
+    pub generics: ty::Generics<'tcx>,
+
+    pub trait_ref: ty::TraitRef<'tcx>,
+
+    /// A list of the associated types defined in this trait. Useful
+    /// for resolving `X::Foo` type markers.
+    pub associated_type_names: Vec<Name>,
+
+    // Impls of this trait. To allow for quicker lookup, the impls are indexed
+    // by a simplified version of their Self type: impls with a simplifiable
+    // Self are stored in nonblanket_impls keyed by it, while all other impls
+    // are stored in blanket_impls.
+    //
+    // These lists are tracked by `DepNode::TraitImpls`; we don't use
+    // a DepTrackingMap but instead have the `TraitDef` insert the
+    // required reads/writes.
+
+    /// Impls of the trait.
+    nonblanket_impls: RefCell<
+        FnvHashMap<fast_reject::SimplifiedType, Vec<DefId>>
+    >,
+
+    /// Blanket impls associated with the trait.
+    blanket_impls: RefCell<Vec<DefId>>,
+
+    /// Various flags
+    pub flags: Cell<TraitFlags>
+}
+
+impl<'tcx> TraitDef<'tcx> {
+    pub fn new(unsafety: hir::Unsafety,
+               paren_sugar: bool,
+               generics: ty::Generics<'tcx>,
+               trait_ref: ty::TraitRef<'tcx>,
+               associated_type_names: Vec<Name>)
+               -> TraitDef<'tcx> {
+        TraitDef {
+            paren_sugar: paren_sugar,
+            unsafety: unsafety,
+            generics: generics,
+            trait_ref: trait_ref,
+            associated_type_names: associated_type_names,
+            nonblanket_impls: RefCell::new(FnvHashMap()),
+            blanket_impls: RefCell::new(vec![]),
+            flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS)
+        }
+    }
+
+    pub fn def_id(&self) -> DefId {
+        self.trait_ref.def_id
+    }
+
+    // returns None if not yet calculated
+    pub fn object_safety(&self) -> Option<bool> {
+        if self.flags.get().intersects(TraitFlags::OBJECT_SAFETY_VALID) {
+            Some(self.flags.get().intersects(TraitFlags::IS_OBJECT_SAFE))
+        } else {
+            None
+        }
+    }
+
+    pub fn set_object_safety(&self, is_safe: bool) {
+        assert!(self.object_safety().map(|cs| cs == is_safe).unwrap_or(true));
+        self.flags.set(
+            self.flags.get() | if is_safe {
+                TraitFlags::OBJECT_SAFETY_VALID | TraitFlags::IS_OBJECT_SAFE
+            } else {
+                TraitFlags::OBJECT_SAFETY_VALID
+            }
+        );
+    }
+
+    fn write_trait_impls(&self, tcx: &ty::ctxt<'tcx>) {
+        tcx.dep_graph.write(DepNode::TraitImpls(self.trait_ref.def_id));
+    }
+
+    fn read_trait_impls(&self, tcx: &ty::ctxt<'tcx>) {
+        tcx.dep_graph.read(DepNode::TraitImpls(self.trait_ref.def_id));
+    }
+
+    /// Records a trait-to-implementation mapping.
+    pub fn record_impl(&self,
+                       tcx: &ty::ctxt<'tcx>,
+                       impl_def_id: DefId,
+                       impl_trait_ref: ty::TraitRef<'tcx>) {
+        debug!("TraitDef::record_impl for {:?}, from {:?}",
+               self, impl_trait_ref);
+
+        // Record the write into the impl set, but only for local
+        // impls: external impls are handled differently.
+        if impl_def_id.is_local() {
+            self.write_trait_impls(tcx);
+        }
+
+        // We don't want to borrow_mut after we already populated all impls,
+        // so check if an impl is present with an immutable borrow first.
+        if let Some(sty) = fast_reject::simplify_type(tcx,
+                                                      impl_trait_ref.self_ty(), false) {
+            if let Some(is) = self.nonblanket_impls.borrow().get(&sty) {
+                if is.contains(&impl_def_id) {
+                    return // duplicate - skip
+                }
+            }
+
+            self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id)
+        } else {
+            if self.blanket_impls.borrow().contains(&impl_def_id) {
+                return // duplicate - skip
+            }
+            self.blanket_impls.borrow_mut().push(impl_def_id)
+        }
+    }
+
+    pub fn for_each_impl<F: FnMut(DefId)>(&self, tcx: &ty::ctxt<'tcx>, mut f: F)  {
+        self.read_trait_impls(tcx);
+
+        tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
+
+        for &impl_def_id in self.blanket_impls.borrow().iter() {
+            f(impl_def_id);
+        }
+
+        for v in self.nonblanket_impls.borrow().values() {
+            for &impl_def_id in v {
+                f(impl_def_id);
+            }
+        }
+    }
+
+    /// Iterate over every impl that could possibly match the
+    /// self-type `self_ty`.
+    pub fn for_each_relevant_impl<F: FnMut(DefId)>(&self,
+                                                   tcx: &ty::ctxt<'tcx>,
+                                                   self_ty: Ty<'tcx>,
+                                                   mut f: F)
+    {
+        self.read_trait_impls(tcx);
+
+        tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
+
+        for &impl_def_id in self.blanket_impls.borrow().iter() {
+            f(impl_def_id);
+        }
+
+        // simplify_type(.., false) basically replaces type parameters and
+        // projections with infer-variables. This is, of course, done on
+        // the impl trait-ref when it is instantiated, but not on the
+        // predicate trait-ref which is passed here.
+        //
+        // for example, if we match `S: Copy` against an impl like
+        // `impl<T:Copy> Copy for Option<T>`, we replace the type variable
+        // in `Option<T>` with an infer variable, to `Option<_>` (this
+        // doesn't actually change fast_reject output), but we don't
+        // replace `S` with anything - this impl of course can't be
+        // selected, and as there are hundreds of similar impls,
+        // considering them would significantly harm performance.
+        if let Some(simp) = fast_reject::simplify_type(tcx, self_ty, true) {
+            if let Some(impls) = self.nonblanket_impls.borrow().get(&simp) {
+                for &impl_def_id in impls {
+                    f(impl_def_id);
+                }
+            }
+        } else {
+            for v in self.nonblanket_impls.borrow().values() {
+                for &impl_def_id in v {
+                    f(impl_def_id);
+                }
+            }
+        }
+    }
+
+    pub fn borrow_impl_lists<'s>(&'s self, tcx: &ty::ctxt<'tcx>)
+                                 -> (Ref<'s, Vec<DefId>>,
+                                     Ref<'s, FnvHashMap<fast_reject::SimplifiedType, Vec<DefId>>>) {
+        self.read_trait_impls(tcx);
+        (self.blanket_impls.borrow(), self.nonblanket_impls.borrow())
+    }
+
+}
+
+bitflags! {
+    flags TraitFlags: u32 {
+        const NO_TRAIT_FLAGS        = 0,
+        const HAS_DEFAULT_IMPL      = 1 << 0,
+        const IS_OBJECT_SAFE        = 1 << 1,
+        const OBJECT_SAFETY_VALID   = 1 << 2,
+        const IMPLS_VALID           = 1 << 3,
+    }
+}
+
index 0517769356f7511936eaee116cc1b2e7e6b59981..af23efe2bf4bad052e40b32a8e1992768d03883c 100644 (file)
@@ -18,9 +18,8 @@ use middle::subst::{self, Subst, Substs};
 use middle::infer;
 use middle::pat_util;
 use middle::traits;
-use middle::ty::{self, Ty, TypeAndMut, TypeFlags};
+use middle::ty::{self, Ty, TypeAndMut, TypeFlags, TypeFoldable};
 use middle::ty::{Disr, ParameterEnvironment};
-use middle::ty::{HasTypeFlags, RegionEscape};
 use middle::ty::TypeVariants::*;
 use util::num::ToPrimitive;
 
index 20534f72666db656f244e7282ce0e4000429efbd..5f0fc306c24f8897da6299f2b563962bbacc3949 100644 (file)
@@ -13,10 +13,8 @@ use middle::infer::InferCtxt;
 use middle::ty::outlives::{self, Component};
 use middle::subst::Substs;
 use middle::traits;
-use middle::ty::{self, RegionEscape, ToPredicate, Ty};
+use middle::ty::{self, ToPredicate, Ty, TypeFoldable};
 use std::iter::once;
-use std::mem;
-use std::rc::Rc;
 use syntax::ast;
 use syntax::codemap::Span;
 use util::common::ErrorReported;
@@ -30,15 +28,13 @@ use util::common::ErrorReported;
 pub fn obligations<'a,'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                             body_id: ast::NodeId,
                             ty: Ty<'tcx>,
-                            span: Span,
-                            rfc1214: bool)
+                            span: Span)
                             -> Option<Vec<traits::PredicateObligation<'tcx>>>
 {
     let mut wf = WfPredicates { infcx: infcx,
                                 body_id: body_id,
                                 span: span,
-                                out: vec![],
-                                rfc1214: rfc1214 };
+                                out: vec![] };
     if wf.compute(ty) {
         debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
         let result = wf.normalize();
@@ -56,12 +52,10 @@ pub fn obligations<'a,'tcx>(infcx: &InferCtxt<'a, 'tcx>,
 pub fn trait_obligations<'a,'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                                   body_id: ast::NodeId,
                                   trait_ref: &ty::TraitRef<'tcx>,
-                                  span: Span,
-                                  rfc1214: bool)
+                                  span: Span)
                                   -> Vec<traits::PredicateObligation<'tcx>>
 {
-    let mut wf = WfPredicates { infcx: infcx, body_id: body_id, span: span,
-                                out: vec![], rfc1214: rfc1214 };
+    let mut wf = WfPredicates { infcx: infcx, body_id: body_id, span: span, out: vec![] };
     wf.compute_trait_ref(trait_ref);
     wf.normalize()
 }
@@ -69,12 +63,10 @@ pub fn trait_obligations<'a,'tcx>(infcx: &InferCtxt<'a, 'tcx>,
 pub fn predicate_obligations<'a,'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                                       body_id: ast::NodeId,
                                       predicate: &ty::Predicate<'tcx>,
-                                      span: Span,
-                                      rfc1214: bool)
+                                      span: Span)
                                       -> Vec<traits::PredicateObligation<'tcx>>
 {
-    let mut wf = WfPredicates { infcx: infcx, body_id: body_id, span: span,
-                                out: vec![], rfc1214: rfc1214 };
+    let mut wf = WfPredicates { infcx: infcx, body_id: body_id, span: span, out: vec![] };
 
     // (*) ok to skip binders, because wf code is prepared for it
     match *predicate {
@@ -150,7 +142,7 @@ pub fn implied_bounds<'a,'tcx>(
         // than the ultimate set. (Note: normally there won't be
         // unresolved inference variables here anyway, but there might be
         // during typeck under some circumstances.)
-        let obligations = obligations(infcx, body_id, ty, span, false).unwrap_or(vec![]);
+        let obligations = obligations(infcx, body_id, ty, span).unwrap_or(vec![]);
 
         // From the full set of obligations, just filter down to the
         // region relationships.
@@ -223,8 +215,6 @@ fn implied_bounds_from_components<'tcx>(sub_region: ty::Region,
                     vec!(),
                 Component::UnresolvedInferenceVariable(..) =>
                     vec!(),
-                Component::RFC1214(components) =>
-                    implied_bounds_from_components(sub_region, components),
             }
         })
         .collect()
@@ -235,24 +225,11 @@ struct WfPredicates<'a,'tcx:'a> {
     body_id: ast::NodeId,
     span: Span,
     out: Vec<traits::PredicateObligation<'tcx>>,
-    rfc1214: bool
 }
 
 impl<'a,'tcx> WfPredicates<'a,'tcx> {
-    fn rfc1214<R,F:FnOnce(&mut WfPredicates<'a,'tcx>) -> R>(&mut self, f: F) -> R {
-        let b = mem::replace(&mut self.rfc1214, true);
-        let r = f(self);
-        self.rfc1214 = b;
-        r
-    }
-
     fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
-        if !self.rfc1214 {
-            traits::ObligationCause::new(self.span, self.body_id, code)
-        } else {
-            let code = traits::ObligationCauseCode::RFC1214(Rc::new(code));
-            traits::ObligationCause::new(self.span, self.body_id, code)
-        }
+        traits::ObligationCause::new(self.span, self.body_id, code)
     }
 
     fn normalize(&mut self) -> Vec<traits::PredicateObligation<'tcx>> {
@@ -268,14 +245,6 @@ impl<'a,'tcx> WfPredicates<'a,'tcx> {
                 .collect()
     }
 
-    fn compute_rfc1214(&mut self, ty: Ty<'tcx>) {
-        let b = mem::replace(&mut self.rfc1214, true);
-        for subty in ty.walk().skip(1) {
-            self.compute(subty);
-        }
-        self.rfc1214 = b;
-    }
-
     /// Pushes the obligations required for `trait_ref` to be WF into
     /// `self.out`.
     fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>) {
@@ -329,21 +298,19 @@ impl<'a,'tcx> WfPredicates<'a,'tcx> {
 
                 ty::TySlice(subty) |
                 ty::TyArray(subty, _) => {
-                    self.rfc1214(|this| {
-                        if !subty.has_escaping_regions() {
-                            let cause = this.cause(traits::SliceOrArrayElem);
-                            match traits::trait_ref_for_builtin_bound(this.infcx.tcx,
-                                                                      ty::BoundSized,
-                                                                      subty) {
-                                Ok(trait_ref) => {
-                                    this.out.push(
-                                        traits::Obligation::new(cause,
-                                                                trait_ref.to_predicate()));
-                                }
-                                Err(ErrorReported) => { }
+                    if !subty.has_escaping_regions() {
+                        let cause = self.cause(traits::SliceOrArrayElem);
+                        match traits::trait_ref_for_builtin_bound(self.infcx.tcx,
+                                                                  ty::BoundSized,
+                                                                  subty) {
+                            Ok(trait_ref) => {
+                                self.out.push(
+                                    traits::Obligation::new(cause,
+                                                            trait_ref.to_predicate()));
                             }
+                            Err(ErrorReported) => { }
                         }
-                    })
+                    }
                 }
 
                 ty::TyBox(_) |
@@ -380,15 +347,16 @@ impl<'a,'tcx> WfPredicates<'a,'tcx> {
                 ty::TyClosure(..) => {
                     // the types in a closure are always the types of
                     // local variables (or possibly references to local
-                    // variables), which are separately checked w/r/t
-                    // WFedness.
+                    // variables), we'll walk those.
+                    //
+                    // (Though, local variables are probably not
+                    // needed, as they are separately checked w/r/t
+                    // WFedness.)
                 }
 
                 ty::TyBareFn(..) => {
-                    // process the bound types; because the old implicator
-                    // did not do this, go into RFC1214 mode.
-                    subtys.skip_current_subtree();
-                    self.compute_rfc1214(ty);
+                    // let the loop iterator into the argument/return
+                    // types appearing in the fn signature
                 }
 
                 ty::TyTrait(ref data) => {
@@ -407,11 +375,6 @@ impl<'a,'tcx> WfPredicates<'a,'tcx> {
                         traits::Obligation::new(
                             cause,
                             ty::Predicate::ObjectSafe(data.principal_def_id())));
-
-                    // process the bound types; because the old implicator
-                    // did not do this, go into RFC1214 mode.
-                    subtys.skip_current_subtree();
-                    self.compute_rfc1214(ty);
                 }
 
                 // Inference variables are the complicated case, since we don't
index d5d8da248e0811359653f68d91d80a9487de227d..8496f606b7b87caf8b15e043ee5571786b5e5fe4 100644 (file)
 use middle::const_eval::ConstVal;
 use middle::def_id::DefId;
 use middle::subst::Substs;
-use middle::ty::{AdtDef, ClosureSubsts, FnOutput, Region, Ty};
+use middle::ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
 use rustc_back::slice;
+use rustc_data_structures::tuple_slice::TupleSlice;
 use rustc_front::hir::InlineAsm;
-use syntax::ast::Name;
+use syntax::ast::{self, Name};
 use syntax::codemap::Span;
-use std::fmt::{Debug, Formatter, Error};
-use std::u32;
+use graphviz::IntoCow;
+use std::ascii;
+use std::borrow::Cow;
+use std::fmt::{self, Debug, Formatter, Write};
+use std::{iter, u32};
+use std::ops::{Index, IndexMut};
 
 /// Lowered representation of a single function.
+#[derive(RustcEncodable, RustcDecodable)]
 pub struct Mir<'tcx> {
     /// List of basic blocks. References to basic block use a newtyped index type `BasicBlock`
     /// that indexes into this vector.
@@ -48,9 +54,6 @@ pub const START_BLOCK: BasicBlock = BasicBlock(0);
 /// where execution ends, on normal return
 pub const END_BLOCK: BasicBlock = BasicBlock(1);
 
-/// where execution ends, on panic
-pub const DIVERGE_BLOCK: BasicBlock = BasicBlock(2);
-
 impl<'tcx> Mir<'tcx> {
     pub fn all_basic_blocks(&self) -> Vec<BasicBlock> {
         (0..self.basic_blocks.len())
@@ -67,16 +70,32 @@ impl<'tcx> Mir<'tcx> {
     }
 }
 
+impl<'tcx> Index<BasicBlock> for Mir<'tcx> {
+    type Output = BasicBlockData<'tcx>;
+
+    #[inline]
+    fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
+        self.basic_block_data(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)
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Mutability and borrow kinds
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
 pub enum Mutability {
     Mut,
     Not,
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
 pub enum BorrowKind {
     /// Data must be immutable and is aliasable.
     Shared,
@@ -127,6 +146,7 @@ pub enum BorrowKind {
 
 // A "variable" is a binding declared by the user as part of the fn
 // decl, a let, etc.
+#[derive(RustcEncodable, RustcDecodable)]
 pub struct VarDecl<'tcx> {
     pub mutability: Mutability,
     pub name: Name,
@@ -135,6 +155,7 @@ pub struct VarDecl<'tcx> {
 
 // A "temp" is a temporary that we place on the stack. They are
 // anonymous, always mutable, and have only a type.
+#[derive(RustcEncodable, RustcDecodable)]
 pub struct TempDecl<'tcx> {
     pub ty: Ty<'tcx>,
 }
@@ -150,6 +171,7 @@ pub struct TempDecl<'tcx> {
 //
 // there is only one argument, of type `(i32, u32)`, but two bindings
 // (`x` and `y`).
+#[derive(RustcEncodable, RustcDecodable)]
 pub struct ArgDecl<'tcx> {
     pub ty: Ty<'tcx>,
 }
@@ -161,7 +183,7 @@ pub struct ArgDecl<'tcx> {
 /// list of the `Mir`.
 ///
 /// (We use a `u32` internally just to save memory.)
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)]
 pub struct BasicBlock(u32);
 
 impl BasicBlock {
@@ -177,36 +199,32 @@ impl BasicBlock {
 }
 
 impl Debug for BasicBlock {
-    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
-        write!(fmt, "BB({})", self.0)
+    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
+        write!(fmt, "bb{}", self.0)
     }
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // BasicBlock and Terminator
 
-#[derive(Debug)]
+#[derive(Debug, RustcEncodable, RustcDecodable)]
 pub struct BasicBlockData<'tcx> {
     pub statements: Vec<Statement<'tcx>>,
-    pub terminator: Terminator<'tcx>,
+    pub terminator: Option<Terminator<'tcx>>,
+    pub is_cleanup: bool,
 }
 
+#[derive(RustcEncodable, RustcDecodable)]
 pub enum Terminator<'tcx> {
     /// block should have one successor in the graph; we jump there
     Goto {
         target: BasicBlock,
     },
 
-    /// block should initiate unwinding; should be one successor
-    /// that does cleanup and branches to DIVERGE_BLOCK
-    Panic {
-        target: BasicBlock,
-    },
-
     /// jump to branch 0 if this lvalue evaluates to true
     If {
         cond: Operand<'tcx>,
-        targets: [BasicBlock; 2],
+        targets: (BasicBlock, BasicBlock),
     },
 
     /// lvalue evaluates to some enum; jump depending on the branch
@@ -236,40 +254,97 @@ pub enum Terminator<'tcx> {
         targets: Vec<BasicBlock>,
     },
 
-    /// Indicates that the last statement in the block panics, aborts,
-    /// etc. No successors. This terminator appears on exactly one
-    /// basic block which we create in advance. However, during
-    /// construction, we use this value as a sentinel for "terminator
-    /// not yet assigned", and assert at the end that only the
-    /// well-known diverging block actually diverges.
-    Diverge,
+    /// Indicates that the landing pad is finished and unwinding should
+    /// continue. Emitted by build::scope::diverge_cleanup.
+    Resume,
 
     /// Indicates a normal return. The ReturnPointer lvalue should
     /// have been filled in by now. This should only occur in the
     /// `END_BLOCK`.
     Return,
 
-    /// block ends with a call; it should have two successors. The
-    /// first successor indicates normal return. The second indicates
-    /// unwinding.
+    /// Block ends with a call of a converging function
     Call {
-        data: CallData<'tcx>,
-        targets: [BasicBlock; 2],
+        /// The function that’s being called
+        func: Operand<'tcx>,
+        /// Arguments the function is called with
+        args: Vec<Operand<'tcx>>,
+        /// The kind of call with associated information
+        kind: CallKind<'tcx>,
     },
 }
 
+#[derive(Clone, RustcEncodable, RustcDecodable)]
+pub enum CallKind<'tcx> {
+    /// Diverging function without associated cleanup
+    Diverging,
+    /// Diverging function with associated cleanup
+    DivergingCleanup(BasicBlock),
+    /// Converging function without associated cleanup
+    Converging {
+        /// Destination where the call result is written
+        destination: Lvalue<'tcx>,
+        /// Block to branch into on successful return
+        target: BasicBlock,
+    },
+    ConvergingCleanup {
+        /// Destination where the call result is written
+        destination: Lvalue<'tcx>,
+        /// First target is branched to on successful return.
+        /// Second block contains the cleanups to do on unwind.
+        targets: (BasicBlock, BasicBlock)
+    }
+}
+
+impl<'tcx> CallKind<'tcx> {
+    pub fn successors(&self) -> &[BasicBlock] {
+        match *self {
+            CallKind::Diverging => &[],
+            CallKind::DivergingCleanup(ref b) |
+            CallKind::Converging { target: ref b, .. } => slice::ref_slice(b),
+            CallKind::ConvergingCleanup { ref targets, .. } => targets.as_slice(),
+        }
+    }
+
+    pub fn successors_mut(&mut self) -> &mut [BasicBlock] {
+        match *self {
+            CallKind::Diverging => &mut [],
+            CallKind::DivergingCleanup(ref mut b) |
+            CallKind::Converging { target: ref mut b, .. } => slice::mut_ref_slice(b),
+            CallKind::ConvergingCleanup { ref mut targets, .. } => targets.as_mut_slice(),
+        }
+    }
+
+    pub fn destination(&self) -> Option<&Lvalue<'tcx>> {
+        match *self {
+            CallKind::Converging { ref destination, .. } |
+            CallKind::ConvergingCleanup { ref destination, .. } => Some(destination),
+            CallKind::Diverging |
+            CallKind::DivergingCleanup(_) => None
+        }
+    }
+
+    pub fn destination_mut(&mut self) -> Option<&mut Lvalue<'tcx>> {
+        match *self {
+            CallKind::Converging { ref mut destination, .. } |
+            CallKind::ConvergingCleanup { ref mut destination, .. } => Some(destination),
+            CallKind::Diverging |
+            CallKind::DivergingCleanup(_) => None
+        }
+    }
+}
+
 impl<'tcx> Terminator<'tcx> {
     pub fn successors(&self) -> &[BasicBlock] {
         use self::Terminator::*;
         match *self {
             Goto { target: ref b } => slice::ref_slice(b),
-            Panic { target: ref b } => slice::ref_slice(b),
-            If { cond: _, targets: ref b } => b,
+            If { targets: ref b, .. } => b.as_slice(),
             Switch { targets: ref b, .. } => b,
             SwitchInt { targets: ref b, .. } => b,
-            Diverge => &[],
+            Resume => &[],
             Return => &[],
-            Call { data: _, targets: ref b } => b,
+            Call { ref kind, .. } => kind.successors(),
         }
     }
 
@@ -277,93 +352,155 @@ impl<'tcx> Terminator<'tcx> {
         use self::Terminator::*;
         match *self {
             Goto { target: ref mut b } => slice::mut_ref_slice(b),
-            Panic { target: ref mut b } => slice::mut_ref_slice(b),
-            If { cond: _, targets: ref mut b } => b,
+            If { targets: ref mut b, .. } => b.as_mut_slice(),
             Switch { targets: ref mut b, .. } => b,
             SwitchInt { targets: ref mut b, .. } => b,
-            Diverge => &mut [],
+            Resume => &mut [],
             Return => &mut [],
-            Call { data: _, targets: ref mut b } => b,
+            Call { ref mut kind, .. } => kind.successors_mut(),
         }
     }
 }
 
-#[derive(Debug)]
-pub struct CallData<'tcx> {
-    /// where the return value is written to
-    pub destination: Lvalue<'tcx>,
-
-    /// the fn being called
-    pub func: Operand<'tcx>,
-
-    /// the arguments
-    pub args: Vec<Operand<'tcx>>,
-}
-
 impl<'tcx> BasicBlockData<'tcx> {
-    pub fn new(terminator: Terminator<'tcx>) -> BasicBlockData<'tcx> {
+    pub fn new(terminator: Option<Terminator<'tcx>>) -> BasicBlockData<'tcx> {
         BasicBlockData {
             statements: vec![],
             terminator: terminator,
+            is_cleanup: false,
         }
     }
+
+    /// Accessor for terminator.
+    ///
+    /// Terminator may not be None after construction of the basic block is complete. This accessor
+    /// provides a convenience way to reach the terminator.
+    pub fn terminator(&self) -> &Terminator<'tcx> {
+        self.terminator.as_ref().expect("invalid terminator state")
+    }
+
+    pub fn terminator_mut(&mut self) -> &mut Terminator<'tcx> {
+        self.terminator.as_mut().expect("invalid terminator state")
+    }
 }
 
 impl<'tcx> Debug for Terminator<'tcx> {
-    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
+    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
+        try!(self.fmt_head(fmt));
+        let successors = self.successors();
+        let labels = self.fmt_successor_labels();
+        assert_eq!(successors.len(), labels.len());
+
+        match successors.len() {
+            0 => Ok(()),
+
+            1 => write!(fmt, " -> {:?}", successors[0]),
+
+            _ => {
+                try!(write!(fmt, " -> ["));
+                for (i, target) in successors.iter().enumerate() {
+                    if i > 0 {
+                        try!(write!(fmt, ", "));
+                    }
+                    try!(write!(fmt, "{}: {:?}", labels[i], target));
+                }
+                write!(fmt, "]")
+            }
+
+        }
+    }
+}
+
+impl<'tcx> Terminator<'tcx> {
+    /// Write the "head" part of the terminator; that is, its name and the data it uses to pick the
+    /// successor basic block, if any. The only information not inlcuded is the list of possible
+    /// successors, which may be rendered differently between the text and the graphviz format.
+    pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
         use self::Terminator::*;
         match *self {
-            Goto { target } =>
-                write!(fmt, "goto -> {:?}", target),
-            Panic { target } =>
-                write!(fmt, "panic -> {:?}", target),
-            If { cond: ref lv, ref targets } =>
-                write!(fmt, "if({:?}) -> {:?}", lv, targets),
-            Switch { discr: ref lv, adt_def: _, ref targets } =>
-                write!(fmt, "switch({:?}) -> {:?}", lv, targets),
-            SwitchInt { discr: ref lv, switch_ty: _, ref values, ref targets } =>
-                write!(fmt, "switchInt({:?}, {:?}) -> {:?}", lv, values, targets),
-            Diverge =>
-                write!(fmt, "diverge"),
-            Return =>
-                write!(fmt, "return"),
-            Call { data: ref c, targets } => {
-                try!(write!(fmt, "{:?} = {:?}(", c.destination, c.func));
-                for (index, arg) in c.args.iter().enumerate() {
+            Goto { .. } => write!(fmt, "goto"),
+            If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv),
+            Switch { discr: ref lv, .. } => write!(fmt, "switch({:?})", lv),
+            SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
+            Return => write!(fmt, "return"),
+            Resume => write!(fmt, "resume"),
+            Call { ref kind, ref func, ref args } => {
+                if let Some(destination) = kind.destination() {
+                    try!(write!(fmt, "{:?} = ", destination));
+                }
+                try!(write!(fmt, "{:?}(", func));
+                for (index, arg) in args.iter().enumerate() {
                     if index > 0 {
                         try!(write!(fmt, ", "));
                     }
                     try!(write!(fmt, "{:?}", arg));
                 }
-                write!(fmt, ") -> {:?}", targets)
+                write!(fmt, ")")
             }
         }
     }
+
+    /// Return the list of labels for the edges to the successor basic blocks.
+    pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
+        use self::Terminator::*;
+        match *self {
+            Return | Resume => vec![],
+            Goto { .. } => vec!["".into_cow()],
+            If { .. } => vec!["true".into_cow(), "false".into_cow()],
+            Switch { ref adt_def, .. } => {
+                adt_def.variants
+                       .iter()
+                       .map(|variant| variant.name.to_string().into_cow())
+                       .collect()
+            }
+            SwitchInt { ref values, .. } => {
+                values.iter()
+                      .map(|const_val| {
+                          let mut buf = String::new();
+                          fmt_const_val(&mut buf, const_val).unwrap();
+                          buf.into_cow()
+                      })
+                      .chain(iter::once(String::from("otherwise").into_cow()))
+                      .collect()
+            }
+            Call { ref kind, .. } => match *kind {
+                CallKind::Diverging =>
+                    vec![],
+                CallKind::DivergingCleanup(..) =>
+                    vec!["unwind".into_cow()],
+                CallKind::Converging { .. } =>
+                    vec!["return".into_cow()],
+                CallKind::ConvergingCleanup { .. } =>
+                    vec!["return".into_cow(), "unwind".into_cow()],
+            },
+        }
+    }
 }
 
 
 ///////////////////////////////////////////////////////////////////////////
 // Statements
 
+#[derive(RustcEncodable, RustcDecodable)]
 pub struct Statement<'tcx> {
     pub span: Span,
     pub kind: StatementKind<'tcx>,
 }
 
-#[derive(Debug)]
+#[derive(Debug, RustcEncodable, RustcDecodable)]
 pub enum StatementKind<'tcx> {
     Assign(Lvalue<'tcx>, Rvalue<'tcx>),
     Drop(DropKind, Lvalue<'tcx>),
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
 pub enum DropKind {
     Free, // free a partially constructed box, should go away eventually
     Deep
 }
 
 impl<'tcx> Debug for Statement<'tcx> {
-    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
+    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
         use self::StatementKind::*;
         match self.kind {
             Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
@@ -377,7 +514,7 @@ impl<'tcx> Debug for Statement<'tcx> {
 
 /// A path to a value; something that can be evaluated without
 /// changing or disturbing program state.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
 pub enum Lvalue<'tcx> {
     /// local variable declared by the user
     Var(u32),
@@ -403,13 +540,13 @@ pub enum Lvalue<'tcx> {
 /// or `*B` or `B[index]`. Note that it is parameterized because it is
 /// shared between `Constant` and `Lvalue`. See the aliases
 /// `LvalueProjection` etc below.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
 pub struct Projection<'tcx, B, V> {
     pub base: B,
     pub elem: ProjectionElem<'tcx, V>,
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
 pub enum ProjectionElem<'tcx, V> {
     Deref,
     Field(Field),
@@ -438,16 +575,14 @@ pub enum ProjectionElem<'tcx, V> {
 
 /// Alias for projections as they appear in lvalues, where the base is an lvalue
 /// and the index is an operand.
-pub type LvalueProjection<'tcx> =
-    Projection<'tcx,Lvalue<'tcx>,Operand<'tcx>>;
+pub type LvalueProjection<'tcx> = Projection<'tcx, Lvalue<'tcx>, Operand<'tcx>>;
 
 /// Alias for projections as they appear in lvalues, where the base is an lvalue
 /// and the index is an operand.
-pub type LvalueElem<'tcx> =
-    ProjectionElem<'tcx,Operand<'tcx>>;
+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)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub struct Field(u32);
 
 impl Field {
@@ -483,34 +618,34 @@ impl<'tcx> Lvalue<'tcx> {
 }
 
 impl<'tcx> Debug for Lvalue<'tcx> {
-    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
+    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
         use self::Lvalue::*;
 
         match *self {
             Var(id) =>
-                write!(fmt,"Var({:?})", id),
+                write!(fmt, "var{:?}", id),
             Arg(id) =>
-                write!(fmt,"Arg({:?})", id),
+                write!(fmt, "arg{:?}", id),
             Temp(id) =>
-                write!(fmt,"Temp({:?})", id),
-            Static(id) =>
-                write!(fmt,"Static({:?})", id),
+                write!(fmt, "tmp{:?}", id),
+            Static(def_id) =>
+                write!(fmt, "{}", ty::tls::with(|tcx| tcx.item_path_str(def_id))),
             ReturnPointer =>
-                write!(fmt,"ReturnPointer"),
+                write!(fmt, "return"),
             Projection(ref data) =>
                 match data.elem {
-                    ProjectionElem::Downcast(_, variant_index) =>
-                        write!(fmt,"({:?} as {:?})", data.base, variant_index),
+                    ProjectionElem::Downcast(ref adt_def, index) =>
+                        write!(fmt, "({:?} as {})", data.base, adt_def.variants[index].name),
                     ProjectionElem::Deref =>
-                        write!(fmt,"(*{:?})", data.base),
+                        write!(fmt, "(*{:?})", data.base),
                     ProjectionElem::Field(field) =>
-                        write!(fmt,"{:?}.{:?}", data.base, field.index()),
+                        write!(fmt, "{:?}.{:?}", data.base, field.index()),
                     ProjectionElem::Index(ref index) =>
-                        write!(fmt,"{:?}[{:?}]", data.base, index),
+                        write!(fmt, "{:?}[{:?}]", data.base, index),
                     ProjectionElem::ConstantIndex { offset, min_length, from_end: false } =>
-                        write!(fmt,"{:?}[{:?} of {:?}]", data.base, offset, min_length),
+                        write!(fmt, "{:?}[{:?} of {:?}]", data.base, offset, min_length),
                     ProjectionElem::ConstantIndex { offset, min_length, from_end: true } =>
-                        write!(fmt,"{:?}[-{:?} of {:?}]", data.base, offset, min_length),
+                        write!(fmt, "{:?}[-{:?} of {:?}]", data.base, offset, min_length),
                 },
         }
     }
@@ -523,14 +658,14 @@ impl<'tcx> Debug for Lvalue<'tcx> {
 // lvalue). They are intentionally limited to prevent rvalues from
 // being nested in one another.
 
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
 pub enum Operand<'tcx> {
     Consume(Lvalue<'tcx>),
     Constant(Constant<'tcx>),
 }
 
 impl<'tcx> Debug for Operand<'tcx> {
-    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
+    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
         use self::Operand::*;
         match *self {
             Constant(ref a) => write!(fmt, "{:?}", a),
@@ -542,7 +677,7 @@ impl<'tcx> Debug for Operand<'tcx> {
 ///////////////////////////////////////////////////////////////////////////
 // Rvalues
 
-#[derive(Clone)]
+#[derive(Clone, RustcEncodable, RustcDecodable)]
 pub enum Rvalue<'tcx> {
     // x (either a move or copy, depending on type of x)
     Use(Operand<'tcx>),
@@ -583,10 +718,10 @@ pub enum Rvalue<'tcx> {
         from_end: usize,
     },
 
-    InlineAsm(&'tcx InlineAsm),
+    InlineAsm(InlineAsm),
 }
 
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
 pub enum CastKind {
     Misc,
 
@@ -604,7 +739,7 @@ pub enum CastKind {
     Unsize,
 }
 
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
 pub enum AggregateKind<'tcx> {
     Vec,
     Tuple,
@@ -612,7 +747,7 @@ pub enum AggregateKind<'tcx> {
     Closure(DefId, &'tcx ClosureSubsts<'tcx>),
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
 pub enum BinOp {
     /// The `+` operator (addition)
     Add,
@@ -648,7 +783,7 @@ pub enum BinOp {
     Gt,
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
 pub enum UnOp {
     /// The `!` operator for logical inversion
     Not,
@@ -657,22 +792,87 @@ pub enum UnOp {
 }
 
 impl<'tcx> Debug for Rvalue<'tcx> {
-    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
+    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
         use self::Rvalue::*;
 
         match *self {
             Use(ref lvalue) => write!(fmt, "{:?}", lvalue),
             Repeat(ref a, ref b) => write!(fmt, "[{:?}; {:?}]", a, b),
-            Ref(ref a, bk, ref b) => write!(fmt, "&{:?} {:?} {:?}", a, bk, b),
-            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),
+            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),
             UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
-            Box(ref t) => write!(fmt, "Box {:?}", t),
-            Aggregate(ref kind, ref lvs) => write!(fmt, "Aggregate<{:?}>({:?})", kind, lvs),
+            Box(ref t) => write!(fmt, "Box({:?})", t),
             InlineAsm(ref asm) => write!(fmt, "InlineAsm({:?})", asm),
             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 {
+                    BorrowKind::Shared => "",
+                    BorrowKind::Mut | BorrowKind::Unique => "mut ",
+                };
+                write!(fmt, "&{}{:?}", kind_str, lv)
+            }
+
+            Aggregate(ref kind, ref lvs) => {
+                use self::AggregateKind::*;
+
+                fn fmt_tuple(fmt: &mut Formatter, name: &str, lvs: &[Operand]) -> fmt::Result {
+                    let mut tuple_fmt = fmt.debug_tuple(name);
+                    for lv in lvs {
+                        tuple_fmt.field(lv);
+                    }
+                    tuple_fmt.finish()
+                }
+
+                match *kind {
+                    Vec => write!(fmt, "{:?}", lvs),
+
+                    Tuple => {
+                        match lvs.len() {
+                            0 => write!(fmt, "()"),
+                            1 => write!(fmt, "({:?},)", lvs[0]),
+                            _ => fmt_tuple(fmt, "", lvs),
+                        }
+                    }
+
+                    Adt(adt_def, variant, _) => {
+                        let variant_def = &adt_def.variants[variant];
+                        let name = ty::tls::with(|tcx| tcx.item_path_str(variant_def.did));
+
+                        match variant_def.kind() {
+                            ty::VariantKind::Unit => write!(fmt, "{}", name),
+                            ty::VariantKind::Tuple => fmt_tuple(fmt, &name, lvs),
+                            ty::VariantKind::Struct => {
+                                let mut struct_fmt = fmt.debug_struct(&name);
+                                for (field, lv) in variant_def.fields.iter().zip(lvs) {
+                                    struct_fmt.field(&field.name.as_str(), lv);
+                                }
+                                struct_fmt.finish()
+                            }
+                        }
+                    }
+
+                    Closure(def_id, _) => ty::tls::with(|tcx| {
+                        if let Some(node_id) = tcx.map.as_local_node_id(def_id) {
+                            let name = format!("[closure@{:?}]", tcx.map.span(node_id));
+                            let mut struct_fmt = fmt.debug_struct(&name);
+
+                            tcx.with_freevars(node_id, |freevars| {
+                                for (freevar, lv) in freevars.iter().zip(lvs) {
+                                    let var_name = tcx.local_var_name_str(freevar.def.var_id());
+                                    struct_fmt.field(&var_name, lv);
+                                }
+                            });
+
+                            struct_fmt.finish()
+                        } else {
+                            write!(fmt, "[closure]")
+                        }
+                    }),
+                }
+            }
         }
     }
 }
@@ -684,20 +884,80 @@ impl<'tcx> Debug for Rvalue<'tcx> {
 // this does not necessarily mean that they are "==" in Rust -- in
 // particular one must be wary of `NaN`!
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
 pub struct Constant<'tcx> {
     pub span: Span,
     pub ty: Ty<'tcx>,
     pub literal: Literal<'tcx>,
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, RustcEncodable, RustcDecodable)]
+pub enum ItemKind {
+    Constant,
+    /// This is any sort of callable (usually those that have a type of `fn(…) -> …`). This
+    /// includes functions, constructors, but not methods which have their own ItemKind.
+    Function,
+    Method,
+}
+
+#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
 pub enum Literal<'tcx> {
     Item {
         def_id: DefId,
+        kind: ItemKind,
         substs: &'tcx Substs<'tcx>,
     },
     Value {
         value: ConstVal,
     },
 }
+
+impl<'tcx> Debug for Constant<'tcx> {
+    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
+        write!(fmt, "{:?}", self.literal)
+    }
+}
+
+impl<'tcx> Debug for Literal<'tcx> {
+    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
+        use self::Literal::*;
+        match *self {
+            Item { def_id, .. } =>
+                write!(fmt, "{}", item_path_str(def_id)),
+            Value { ref value } => {
+                try!(write!(fmt, "const "));
+                fmt_const_val(fmt, value)
+            }
+        }
+    }
+}
+
+/// Write a `ConstVal` in a way closer to the original source code than the `Debug` output.
+fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ConstVal) -> fmt::Result {
+    use middle::const_eval::ConstVal::*;
+    match *const_val {
+        Float(f) => write!(fmt, "{:?}", f),
+        Int(n) => write!(fmt, "{:?}", n),
+        Uint(n) => write!(fmt, "{:?}", n),
+        Str(ref s) => write!(fmt, "{:?}", s),
+        ByteStr(ref bytes) => {
+            let escaped: String = bytes
+                .iter()
+                .flat_map(|&ch| ascii::escape_default(ch).map(|c| c as char))
+                .collect();
+            write!(fmt, "b\"{}\"", escaped)
+        }
+        Bool(b) => write!(fmt, "{:?}", b),
+        Function(def_id) => write!(fmt, "{}", item_path_str(def_id)),
+        Struct(node_id) | Tuple(node_id) | Array(node_id, _) | Repeat(node_id, _) =>
+            write!(fmt, "{}", node_to_string(node_id)),
+    }
+}
+
+fn node_to_string(node_id: ast::NodeId) -> String {
+    ty::tls::with(|tcx| tcx.map.node_to_user_string(node_id))
+}
+
+fn item_path_str(def_id: DefId) -> String {
+    ty::tls::with(|tcx| tcx.item_path_str(def_id))
+}
index ac4f54b4b49627ff05ee7692e73689ea62e889c9..7c8ea22de8e922a315d42de9bb875ccfb5c6535d 100644 (file)
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use middle::def_id::DefId;
 use middle::ty::Region;
 use mir::repr::*;
+use rustc_data_structures::tuple_slice::TupleSlice;
+use syntax::codemap::Span;
 
-pub trait Visitor<'tcx> {
-    // Override these, and call `self.super_xxx` to revert back to the
-    // default behavior.
+macro_rules! make_mir_visitor {
+    ($visitor_trait_name:ident, $($mutability:ident)*) => {
+        pub trait $visitor_trait_name<'tcx> {
+            // Override these, and call `self.super_xxx` to revert back to the
+            // default behavior.
 
-    fn visit_mir(&mut self, mir: &Mir<'tcx>) {
-        self.super_mir(mir);
-    }
-
-    fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) {
-        self.super_basic_block_data(block, data);
-    }
-
-    fn visit_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) {
-        self.super_statement(block, statement);
-    }
-
-    fn visit_assign(&mut self, block: BasicBlock, lvalue: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) {
-        self.super_assign(block, lvalue, rvalue);
-    }
-
-    fn visit_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) {
-        self.super_terminator(block, terminator);
-    }
-
-    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
-        self.super_rvalue(rvalue);
-    }
-
-    fn visit_operand(&mut self, operand: &Operand<'tcx>) {
-        self.super_operand(operand);
-    }
-
-    fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) {
-        self.super_lvalue(lvalue, context);
-    }
-
-    fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) {
-        self.super_branch(source, target);
-    }
-
-    fn visit_constant(&mut self, constant: &Constant<'tcx>) {
-        self.super_constant(constant);
-    }
-
-    // The `super_xxx` methods comprise the default behavior and are
-    // not meant to be overidden.
-
-    fn super_mir(&mut self, mir: &Mir<'tcx>) {
-        for block in mir.all_basic_blocks() {
-            let data = mir.basic_block_data(block);
-            self.visit_basic_block_data(block, data);
-        }
-    }
+            fn visit_mir(&mut self, mir: & $($mutability)* Mir<'tcx>) {
+                self.super_mir(mir);
+            }
 
-    fn super_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) {
-        for statement in &data.statements {
-            self.visit_statement(block, statement);
-        }
-        self.visit_terminator(block, &data.terminator);
-    }
+            fn visit_basic_block_data(&mut self,
+                                      block: BasicBlock,
+                                      data: & $($mutability)* BasicBlockData<'tcx>) {
+                self.super_basic_block_data(block, data);
+            }
 
-    fn super_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) {
-        match statement.kind {
-            StatementKind::Assign(ref lvalue, ref rvalue) => {
-                self.visit_assign(block, lvalue, rvalue);
+            fn visit_statement(&mut self,
+                               block: BasicBlock,
+                               statement: & $($mutability)* Statement<'tcx>) {
+                self.super_statement(block, statement);
             }
-            StatementKind::Drop(_, ref lvalue) => {
-                self.visit_lvalue(lvalue, LvalueContext::Drop);
+
+            fn visit_assign(&mut self,
+                            block: BasicBlock,
+                            lvalue: & $($mutability)* Lvalue<'tcx>,
+                            rvalue: & $($mutability)* Rvalue<'tcx>) {
+                self.super_assign(block, lvalue, rvalue);
             }
-        }
-    }
 
-    fn super_assign(&mut self, _block: BasicBlock, lvalue: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) {
-        self.visit_lvalue(lvalue, LvalueContext::Store);
-        self.visit_rvalue(rvalue);
-    }
+            fn visit_terminator(&mut self,
+                                block: BasicBlock,
+                                terminator: & $($mutability)* Terminator<'tcx>) {
+                self.super_terminator(block, terminator);
+            }
 
-    fn super_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) {
-        match *terminator {
-            Terminator::Goto { target } |
-            Terminator::Panic { target } => {
-                self.visit_branch(block, target);
+            fn visit_rvalue(&mut self,
+                            rvalue: & $($mutability)* Rvalue<'tcx>) {
+                self.super_rvalue(rvalue);
             }
 
-            Terminator::If { ref cond, ref targets } => {
-                self.visit_operand(cond);
-                for &target in &targets[..] {
-                    self.visit_branch(block, target);
-                }
+            fn visit_operand(&mut self,
+                             operand: & $($mutability)* Operand<'tcx>) {
+                self.super_operand(operand);
             }
 
-            Terminator::Switch { ref discr, adt_def: _, ref targets } => {
-                self.visit_lvalue(discr, LvalueContext::Inspect);
-                for &target in targets {
-                    self.visit_branch(block, target);
-                }
+            fn visit_lvalue(&mut self,
+                            lvalue: & $($mutability)* Lvalue<'tcx>,
+                            context: LvalueContext) {
+                self.super_lvalue(lvalue, context);
             }
 
-            Terminator::SwitchInt { ref discr, switch_ty: _, values: _, ref targets } => {
-                self.visit_lvalue(discr, LvalueContext::Inspect);
-                for &target in targets {
-                    self.visit_branch(block, target);
-                }
+            fn visit_branch(&mut self,
+                            source: BasicBlock,
+                            target: BasicBlock) {
+                self.super_branch(source, target);
             }
 
-            Terminator::Diverge |
-            Terminator::Return => {
+            fn visit_constant(&mut self,
+                              constant: & $($mutability)* Constant<'tcx>) {
+                self.super_constant(constant);
             }
 
-            Terminator::Call { ref data, ref targets } => {
-                self.visit_lvalue(&data.destination, LvalueContext::Store);
-                self.visit_operand(&data.func);
-                for arg in &data.args {
-                    self.visit_operand(arg);
-                }
-                for &target in &targets[..] {
-                    self.visit_branch(block, target);
-                }
+            fn visit_literal(&mut self,
+                             literal: & $($mutability)* Literal<'tcx>) {
+                self.super_literal(literal);
             }
-        }
-    }
 
-    fn super_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
-        match *rvalue {
-            Rvalue::Use(ref operand) => {
-                self.visit_operand(operand);
+            fn visit_def_id(&mut self,
+                            def_id: & $($mutability)* DefId) {
+                self.super_def_id(def_id);
             }
 
-            Rvalue::Repeat(ref value, ref len) => {
-                self.visit_operand(value);
-                self.visit_constant(len);
+            fn visit_span(&mut self,
+                          span: & $($mutability)* Span) {
+                self.super_span(span);
             }
 
-            Rvalue::Ref(r, bk, ref path) => {
-                self.visit_lvalue(path, LvalueContext::Borrow {
-                    region: r,
-                    kind: bk
-                });
+            // The `super_xxx` methods comprise the default behavior and are
+            // not meant to be overidden.
+
+            fn super_mir(&mut self,
+                         mir: & $($mutability)* Mir<'tcx>) {
+                for block in mir.all_basic_blocks() {
+                    let data = & $($mutability)* mir[block];
+                    self.visit_basic_block_data(block, data);
+                }
             }
 
-            Rvalue::Len(ref path) => {
-                self.visit_lvalue(path, LvalueContext::Inspect);
+            fn super_basic_block_data(&mut self,
+                                      block: BasicBlock,
+                                      data: & $($mutability)* BasicBlockData<'tcx>) {
+                for statement in & $($mutability)* data.statements {
+                    self.visit_statement(block, statement);
+                }
+
+                if let Some(ref $($mutability)* terminator) = data.terminator {
+                    self.visit_terminator(block, terminator);
+                }
             }
 
-            Rvalue::Cast(_, ref operand, _) => {
-                self.visit_operand(operand);
+            fn super_statement(&mut self,
+                               block: BasicBlock,
+                               statement: & $($mutability)* Statement<'tcx>) {
+                self.visit_span(& $($mutability)* statement.span);
+
+                match statement.kind {
+                    StatementKind::Assign(ref $($mutability)* lvalue,
+                                          ref $($mutability)* rvalue) => {
+                        self.visit_assign(block, lvalue, rvalue);
+                    }
+                    StatementKind::Drop(_, ref $($mutability)* lvalue) => {
+                        self.visit_lvalue(lvalue, LvalueContext::Drop);
+                    }
+                }
             }
 
-            Rvalue::BinaryOp(_, ref lhs, ref rhs) => {
-                self.visit_operand(lhs);
-                self.visit_operand(rhs);
+            fn super_assign(&mut self,
+                            _block: BasicBlock,
+                            lvalue: &$($mutability)* Lvalue<'tcx>,
+                            rvalue: &$($mutability)* Rvalue<'tcx>) {
+                self.visit_lvalue(lvalue, LvalueContext::Store);
+                self.visit_rvalue(rvalue);
             }
 
-            Rvalue::UnaryOp(_, ref op) => {
-                self.visit_operand(op);
+            fn super_terminator(&mut self,
+                                block: BasicBlock,
+                                terminator: &$($mutability)* Terminator<'tcx>) {
+                match *terminator {
+                    Terminator::Goto { target } => {
+                        self.visit_branch(block, target);
+                    }
+
+                    Terminator::If { ref $($mutability)* cond,
+                                     ref $($mutability)* targets } => {
+                        self.visit_operand(cond);
+                        for &target in targets.as_slice() {
+                            self.visit_branch(block, target);
+                        }
+                    }
+
+                    Terminator::Switch { ref $($mutability)* discr,
+                                         adt_def: _,
+                                         ref targets } => {
+                        self.visit_lvalue(discr, LvalueContext::Inspect);
+                        for &target in targets {
+                            self.visit_branch(block, target);
+                        }
+                    }
+
+                    Terminator::SwitchInt { ref $($mutability)* discr,
+                                            switch_ty: _,
+                                            values: _,
+                                            ref targets } => {
+                        self.visit_lvalue(discr, LvalueContext::Inspect);
+                        for &target in targets {
+                            self.visit_branch(block, target);
+                        }
+                    }
+
+                    Terminator::Resume |
+                    Terminator::Return => {
+                    }
+
+                    Terminator::Call { ref $($mutability)* func,
+                                       ref $($mutability)* args,
+                                       ref $($mutability)* kind } => {
+                        self.visit_operand(func);
+                        for arg in args {
+                            self.visit_operand(arg);
+                        }
+                        match *kind {
+                            CallKind::Converging {
+                                ref $($mutability)* destination,
+                                ..
+                            }        |
+                            CallKind::ConvergingCleanup {
+                                ref $($mutability)* destination,
+                                ..
+                            } => {
+                                self.visit_lvalue(destination, LvalueContext::Store);
+                            }
+                            CallKind::Diverging           |
+                            CallKind::DivergingCleanup(_) => {}
+                        }
+                        for &target in kind.successors() {
+                            self.visit_branch(block, target);
+                        }
+                    }
+                }
             }
 
-            Rvalue::Box(_) => {
+            fn super_rvalue(&mut self,
+                            rvalue: & $($mutability)* Rvalue<'tcx>) {
+                match *rvalue {
+                    Rvalue::Use(ref $($mutability)* operand) => {
+                        self.visit_operand(operand);
+                    }
+
+                    Rvalue::Repeat(ref $($mutability)* value,
+                                   ref $($mutability)* len) => {
+                        self.visit_operand(value);
+                        self.visit_constant(len);
+                    }
+
+                    Rvalue::Ref(r, bk, ref $($mutability)* path) => {
+                        self.visit_lvalue(path, LvalueContext::Borrow {
+                            region: r,
+                            kind: bk
+                        });
+                    }
+
+                    Rvalue::Len(ref $($mutability)* path) => {
+                        self.visit_lvalue(path, LvalueContext::Inspect);
+                    }
+
+                    Rvalue::Cast(_, ref $($mutability)* operand, _) => {
+                        self.visit_operand(operand);
+                    }
+
+                    Rvalue::BinaryOp(_,
+                                     ref $($mutability)* lhs,
+                                     ref $($mutability)* rhs) => {
+                        self.visit_operand(lhs);
+                        self.visit_operand(rhs);
+                    }
+
+                    Rvalue::UnaryOp(_, ref $($mutability)* op) => {
+                        self.visit_operand(op);
+                    }
+
+                    Rvalue::Box(_) => {
+                    }
+
+                    Rvalue::Aggregate(ref $($mutability)* kind,
+                                      ref $($mutability)* operands) => {
+                        match *kind {
+                            AggregateKind::Closure(ref $($mutability)* def_id, _) => {
+                                self.visit_def_id(def_id);
+                            }
+                            _ => { /* nothing to do */ }
+                        }
+
+                        for operand in & $($mutability)* operands[..] {
+                            self.visit_operand(operand);
+                        }
+                    }
+
+                    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(_) => {
+                    }
+                }
             }
 
-            Rvalue::Aggregate(_, ref operands) => {
-                for operand in operands {
-                    self.visit_operand(operand);
+            fn super_operand(&mut self,
+                             operand: & $($mutability)* Operand<'tcx>) {
+                match *operand {
+                    Operand::Consume(ref $($mutability)* lvalue) => {
+                        self.visit_lvalue(lvalue, LvalueContext::Consume);
+                    }
+                    Operand::Constant(ref $($mutability)* constant) => {
+                        self.visit_constant(constant);
+                    }
                 }
             }
 
-            Rvalue::Slice { ref input, from_start, from_end } => {
-                self.visit_lvalue(input, LvalueContext::Slice {
-                    from_start: from_start,
-                    from_end: from_end,
-                });
+            fn super_lvalue(&mut self,
+                            lvalue: & $($mutability)* Lvalue<'tcx>,
+                            _context: LvalueContext) {
+                match *lvalue {
+                    Lvalue::Var(_) |
+                    Lvalue::Temp(_) |
+                    Lvalue::Arg(_) |
+                    Lvalue::ReturnPointer => {
+                    }
+                    Lvalue::Static(ref $($mutability)* def_id) => {
+                        self.visit_def_id(def_id);
+                    }
+                    Lvalue::Projection(ref $($mutability)* proj) => {
+                        self.visit_lvalue(& $($mutability)* proj.base,
+                                          LvalueContext::Projection);
+                    }
+                }
             }
 
-            Rvalue::InlineAsm(_) => {
+            fn super_branch(&mut self,
+                            _source: BasicBlock,
+                            _target: BasicBlock) {
             }
-        }
-    }
 
-    fn super_operand(&mut self, operand: &Operand<'tcx>) {
-        match *operand {
-            Operand::Consume(ref lvalue) => {
-                self.visit_lvalue(lvalue, LvalueContext::Consume);
+            fn super_constant(&mut self,
+                              constant: & $($mutability)* Constant<'tcx>) {
+                self.visit_span(& $($mutability)* constant.span);
+                self.visit_literal(& $($mutability)* constant.literal);
             }
-            Operand::Constant(ref constant) => {
-                self.visit_constant(constant);
+
+            fn super_literal(&mut self,
+                             literal: & $($mutability)* Literal<'tcx>) {
+                match *literal {
+                    Literal::Item { ref $($mutability)* def_id, .. } => {
+                        self.visit_def_id(def_id);
+                    },
+                    Literal::Value { .. } => {
+                        // Nothing to do
+                    }
+                }
             }
-        }
-    }
 
-    fn super_lvalue(&mut self, lvalue: &Lvalue<'tcx>, _context: LvalueContext) {
-        match *lvalue {
-            Lvalue::Var(_) |
-            Lvalue::Temp(_) |
-            Lvalue::Arg(_) |
-            Lvalue::Static(_) |
-            Lvalue::ReturnPointer => {
+            fn super_def_id(&mut self, _def_id: & $($mutability)* DefId) {
             }
-            Lvalue::Projection(ref proj) => {
-                self.visit_lvalue(&proj.base, LvalueContext::Projection);
+
+            fn super_span(&mut self, _span: & $($mutability)* Span) {
             }
         }
     }
-
-    fn super_branch(&mut self, _source: BasicBlock, _target: BasicBlock) {
-    }
-
-    fn super_constant(&mut self, _constant: &Constant<'tcx>) {
-    }
 }
 
+make_mir_visitor!(Visitor,);
+make_mir_visitor!(MutVisitor,mut);
+
 #[derive(Copy, Clone, Debug)]
 pub enum LvalueContext {
     // Appears as LHS of an assignment or as dest of a call
index 6fb8c03370142713f7c935fee8a1765e53af2a0b..c4697ebbfd5603d4ca00d67c17ad71a40e0fa0a2 100644 (file)
@@ -14,7 +14,6 @@
 pub use self::EntryFnType::*;
 pub use self::CrateType::*;
 pub use self::Passes::*;
-pub use self::OptLevel::*;
 pub use self::DebugInfoLevel::*;
 
 use session::{early_error, early_warn, Session};
@@ -27,7 +26,7 @@ use middle::cstore;
 use syntax::ast::{self, IntTy, UintTy};
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
-use syntax::diagnostic::{ColorConfig, Auto, Always, Never, SpanHandler};
+use syntax::errors::{ColorConfig, Handler};
 use syntax::parse;
 use syntax::parse::token::InternedString;
 use syntax::feature_gate::UnstableFeatures;
@@ -71,6 +70,18 @@ pub enum OutputType {
     DepInfo,
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum ErrorOutputType {
+    HumanReadable(ColorConfig),
+    Json,
+}
+
+impl Default for ErrorOutputType {
+    fn default() -> ErrorOutputType {
+        ErrorOutputType::HumanReadable(ColorConfig::Auto)
+    }
+}
+
 impl OutputType {
     fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
         match *self {
@@ -124,13 +135,14 @@ pub struct Options {
     pub test: bool,
     pub parse_only: bool,
     pub no_trans: bool,
+    pub error_format: ErrorOutputType,
     pub treat_err_as_bug: bool,
+    pub incremental_compilation: bool,
+    pub dump_dep_graph: bool,
     pub no_analysis: bool,
     pub debugging_opts: DebuggingOptions,
     pub prints: Vec<PrintRequest>,
     pub cg: CodegenOptions,
-    pub color: ColorConfig,
-    pub show_span: Option<String>,
     pub externs: HashMap<String, Vec<String>>,
     pub crate_name: Option<String>,
     /// An optional name to use as the crate for std during std injection,
@@ -220,7 +232,7 @@ pub fn basic_options() -> Options {
     Options {
         crate_types: Vec::new(),
         gc: false,
-        optimize: No,
+        optimize: OptLevel::No,
         debuginfo: NoDebugInfo,
         lint_opts: Vec::new(),
         lint_cap: None,
@@ -234,12 +246,13 @@ pub fn basic_options() -> Options {
         parse_only: false,
         no_trans: false,
         treat_err_as_bug: false,
+        incremental_compilation: false,
+        dump_dep_graph: false,
         no_analysis: false,
         debugging_opts: basic_debugging_options(),
         prints: Vec::new(),
         cg: basic_codegen_options(),
-        color: Auto,
-        show_span: None,
+        error_format: ErrorOutputType::default(),
         externs: HashMap::new(),
         crate_name: None,
         alt_std_name: None,
@@ -306,7 +319,7 @@ macro_rules! options {
         $struct_name { $($opt: $init),* }
     }
 
-    pub fn $buildfn(matches: &getopts::Matches, color: ColorConfig) -> $struct_name
+    pub fn $buildfn(matches: &getopts::Matches, error_format: ErrorOutputType) -> $struct_name
     {
         let mut op = $defaultfn();
         for option in matches.opt_strs($prefix) {
@@ -320,20 +333,20 @@ macro_rules! options {
                 if !setter(&mut op, value) {
                     match (value, opt_type_desc) {
                         (Some(..), None) => {
-                            early_error(color, &format!("{} option `{}` takes no \
-                                                         value", $outputname, key))
+                            early_error(error_format, &format!("{} option `{}` takes no \
+                                                              value", $outputname, key))
                         }
                         (None, Some(type_desc)) => {
-                            early_error(color, &format!("{0} option `{1}` requires \
-                                                         {2} ({3} {1}=<value>)",
-                                                        $outputname, key,
-                                                        type_desc, $prefix))
+                            early_error(error_format, &format!("{0} option `{1}` requires \
+                                                              {2} ({3} {1}=<value>)",
+                                                             $outputname, key,
+                                                             type_desc, $prefix))
                         }
                         (Some(value), Some(type_desc)) => {
-                            early_error(color, &format!("incorrect value `{}` for {} \
-                                                         option `{}` - {} was expected",
-                                                        value, $outputname,
-                                                        key, type_desc))
+                            early_error(error_format, &format!("incorrect value `{}` for {} \
+                                                              option `{}` - {} was expected",
+                                                             value, $outputname,
+                                                             key, type_desc))
                         }
                         (None, None) => unreachable!()
                     }
@@ -342,8 +355,8 @@ macro_rules! options {
                 break;
             }
             if !found {
-                early_error(color, &format!("unknown {} option: `{}`",
-                                            $outputname, key));
+                early_error(error_format, &format!("unknown {} option: `{}`",
+                                                 $outputname, key));
             }
         }
         return op;
@@ -604,6 +617,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
           "run all passes except translation; no output"),
     treat_err_as_bug: bool = (false, parse_bool,
           "treat all errors that occur as bugs"),
+    incr_comp: bool = (false, parse_bool,
+          "enable incremental compilation (experimental)"),
+    dump_dep_graph: bool = (false, parse_bool,
+          "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"),
     no_analysis: bool = (false, parse_bool,
           "parse and expand the source, but run no analysis"),
     extra_plugins: Vec<String> = (Vec::new(), parse_list,
@@ -622,6 +639,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
           "force nonzeroing move optimization on"),
     keep_mtwt_tables: bool = (false, parse_bool,
           "don't clear the resolution tables after analysis"),
+    keep_ast: bool = (false, parse_bool,
+          "keep the AST after lowering it to HIR"),
+    show_span: Option<String> = (None, parse_opt_string,
+          "show spans for compiler debugging (expr|pat|ty)"),
 }
 
 pub fn default_lib_output() -> CrateType {
@@ -660,6 +681,9 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
         "windows" | "unix" => ret.push(attr::mk_word_item(fam)),
         _ => (),
     }
+    if sess.target.target.options.has_elf_tls {
+        ret.push(attr::mk_word_item(InternedString::new("target_thread_local")));
+    }
     if sess.opts.debug_assertions {
         ret.push(attr::mk_word_item(InternedString::new("debug_assertions")));
     }
@@ -687,19 +711,19 @@ pub fn build_configuration(sess: &Session) -> ast::CrateConfig {
     v
 }
 
-pub fn build_target_config(opts: &Options, sp: &SpanHandler) -> Config {
+pub fn build_target_config(opts: &Options, sp: &Handler) -> Config {
     let target = match Target::search(&opts.target_triple) {
         Ok(t) => t,
         Err(e) => {
-            panic!(sp.handler().fatal(&format!("Error loading target specification: {}", e)));
+            panic!(sp.fatal(&format!("Error loading target specification: {}", e)));
         }
     };
 
     let (int_type, uint_type) = match &target.target_pointer_width[..] {
         "32" => (ast::TyI32, ast::TyU32),
         "64" => (ast::TyI64, ast::TyU64),
-        w    => panic!(sp.handler().fatal(&format!("target specification was invalid: \
-                                                    unrecognized target-pointer-width {}", w))),
+        w    => panic!(sp.fatal(&format!("target specification was invalid: \
+                                          unrecognized target-pointer-width {}", w))),
     };
 
     Config {
@@ -848,6 +872,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
                  "NAME=PATH"),
         opt::opt("", "sysroot", "Override the system root", "PATH"),
         opt::multi("Z", "", "Set internal debugging options", "FLAG"),
+        opt::opt_u("", "error-format", "How errors and other messages are produced", "human|json"),
         opt::opt("", "color", "Configure coloring of output:
             auto   = colorize, if output goes to a tty (default);
             always = always colorize output;
@@ -867,7 +892,6 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
                       `hir` (the HIR), `hir,identified`, or
                       `hir,typed` (HIR with types for each node).",
                      "TYPE"),
-        opt::opt_u("", "show-span", "Show spans for compiler debugging", "expr|pat|ty"),
     ]);
     opts
 }
@@ -884,22 +908,44 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String> ) -> ast::CrateConfig {
 
 pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let color = match matches.opt_str("color").as_ref().map(|s| &s[..]) {
-        Some("auto")   => Auto,
-        Some("always") => Always,
-        Some("never")  => Never,
+        Some("auto")   => ColorConfig::Auto,
+        Some("always") => ColorConfig::Always,
+        Some("never")  => ColorConfig::Never,
 
-        None => Auto,
+        None => ColorConfig::Auto,
 
         Some(arg) => {
-            early_error(Auto, &format!("argument for --color must be auto, always \
-                                        or never (instead was `{}`)",
-                                       arg))
+            early_error(ErrorOutputType::default(), &format!("argument for --color must be auto, \
+                                                              always or never (instead was `{}`)",
+                                                            arg))
+        }
+    };
+
+    // We need the opts_present check because the driver will send us Matches
+    // with only stable options if no unstable options are used. Since error-format
+    // is unstable, it will not be present. We have to use opts_present not
+    // opt_present because the latter will panic.
+    let error_format = if matches.opts_present(&["error-format".to_owned()]) {
+        match matches.opt_str("error-format").as_ref().map(|s| &s[..]) {
+            Some("human")   => ErrorOutputType::HumanReadable(color),
+            Some("json") => ErrorOutputType::Json,
+
+            None => ErrorOutputType::HumanReadable(color),
+
+            Some(arg) => {
+                early_error(ErrorOutputType::HumanReadable(color),
+                            &format!("argument for --error-format must be human or json (instead \
+                                      was `{}`)",
+                                     arg))
+            }
         }
+    } else {
+        ErrorOutputType::HumanReadable(color)
     };
 
     let unparsed_crate_types = matches.opt_strs("crate-type");
     let crate_types = parse_crate_types_from_list(unparsed_crate_types)
-        .unwrap_or_else(|e| early_error(color, &e[..]));
+        .unwrap_or_else(|e| early_error(error_format, &e[..]));
 
     let mut lint_opts = vec!();
     let mut describe_lints = false;
@@ -916,15 +962,17 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     let lint_cap = matches.opt_str("cap-lints").map(|cap| {
         lint::Level::from_str(&cap).unwrap_or_else(|| {
-            early_error(color, &format!("unknown lint level: `{}`", cap))
+            early_error(error_format, &format!("unknown lint level: `{}`", cap))
         })
     });
 
-    let debugging_opts = build_debugging_options(matches, color);
+    let debugging_opts = build_debugging_options(matches, error_format);
 
     let parse_only = debugging_opts.parse_only;
     let no_trans = debugging_opts.no_trans;
     let treat_err_as_bug = debugging_opts.treat_err_as_bug;
+    let incremental_compilation = debugging_opts.incr_comp;
+    let dump_dep_graph = debugging_opts.dump_dep_graph;
     let no_analysis = debugging_opts.no_analysis;
 
     if debugging_opts.debug_llvm {
@@ -944,7 +992,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
                     "link" => OutputType::Exe,
                     "dep-info" => OutputType::DepInfo,
                     part => {
-                        early_error(color, &format!("unknown emission type: `{}`",
+                        early_error(error_format, &format!("unknown emission type: `{}`",
                                                     part))
                     }
                 };
@@ -957,7 +1005,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         output_types.insert(OutputType::Exe, None);
     }
 
-    let mut cg = build_codegen_options(matches, color);
+    let mut cg = build_codegen_options(matches, error_format);
 
     // Issue #30063: if user requests llvm-related output to one
     // particular path, disable codegen-units.
@@ -969,11 +1017,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             }).collect();
         if !incompatible.is_empty() {
             for ot in &incompatible {
-                early_warn(color, &format!("--emit={} with -o incompatible with \
-                                            -C codegen-units=N for N > 1",
-                                           ot.shorthand()));
+                early_warn(error_format, &format!("--emit={} with -o incompatible with \
+                                                 -C codegen-units=N for N > 1",
+                                                ot.shorthand()));
             }
-            early_warn(color, "resetting to default -C codegen-units=1");
+            early_warn(error_format, "resetting to default -C codegen-units=1");
             cg.codegen_units = 1;
         }
     }
@@ -986,29 +1034,29 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let opt_level = {
         if matches.opt_present("O") {
             if cg.opt_level.is_some() {
-                early_error(color, "-O and -C opt-level both provided");
+                early_error(error_format, "-O and -C opt-level both provided");
             }
-            Default
+            OptLevel::Default
         } else {
             match cg.opt_level {
-                None => No,
-                Some(0) => No,
-                Some(1) => Less,
-                Some(2) => Default,
-                Some(3) => Aggressive,
+                None => OptLevel::No,
+                Some(0) => OptLevel::No,
+                Some(1) => OptLevel::Less,
+                Some(2) => OptLevel::Default,
+                Some(3) => OptLevel::Aggressive,
                 Some(arg) => {
-                    early_error(color, &format!("optimization level needs to be \
-                                                 between 0-3 (instead was `{}`)",
-                                                arg));
+                    early_error(error_format, &format!("optimization level needs to be \
+                                                      between 0-3 (instead was `{}`)",
+                                                     arg));
                 }
             }
         }
     };
-    let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == No);
+    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(color, "-g and -C debuginfo both provided");
+            early_error(error_format, "-g and -C debuginfo both provided");
         }
         FullDebugInfo
     } else {
@@ -1017,16 +1065,16 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             Some(1) => LimitedDebugInfo,
             Some(2) => FullDebugInfo,
             Some(arg) => {
-                early_error(color, &format!("debug info level needs to be between \
-                                             0-2 (instead was `{}`)",
-                                            arg));
+                early_error(error_format, &format!("debug info level needs to be between \
+                                                  0-2 (instead was `{}`)",
+                                                 arg));
             }
         }
     };
 
     let mut search_paths = SearchPaths::new();
     for s in &matches.opt_strs("L") {
-        search_paths.add_path(&s[..], color);
+        search_paths.add_path(&s[..], error_format);
     }
 
     let libs = matches.opt_strs("l").into_iter().map(|s| {
@@ -1038,9 +1086,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             (Some(name), "framework") => (name, cstore::NativeFramework),
             (Some(name), "static") => (name, cstore::NativeStatic),
             (_, s) => {
-                early_error(color, &format!("unknown library kind `{}`, expected \
-                                             one of dylib, framework, or static",
-                                            s));
+                early_error(error_format, &format!("unknown library kind `{}`, expected \
+                                                  one of dylib, framework, or static",
+                                                 s));
             }
         };
         (name.to_string(), kind)
@@ -1055,14 +1103,14 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             "file-names" => PrintRequest::FileNames,
             "sysroot" => PrintRequest::Sysroot,
             req => {
-                early_error(color, &format!("unknown print request `{}`", req))
+                early_error(error_format, &format!("unknown print request `{}`", req))
             }
         }
     }).collect::<Vec<_>>();
 
     if !cg.remark.is_empty() && debuginfo == NoDebugInfo {
-        early_warn(color, "-C remark will not show source locations without \
-                           --debuginfo");
+        early_warn(error_format, "-C remark will not show source locations without \
+                                --debuginfo");
     }
 
     let mut externs = HashMap::new();
@@ -1070,11 +1118,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         let mut parts = arg.splitn(2, '=');
         let name = match parts.next() {
             Some(s) => s,
-            None => early_error(color, "--extern value must not be empty"),
+            None => early_error(error_format, "--extern value must not be empty"),
         };
         let location = match parts.next() {
             Some(s) => s,
-            None => early_error(color, "--extern value must be of the format `foo=bar`"),
+            None => early_error(error_format, "--extern value must be of the format `foo=bar`"),
         };
 
         externs.entry(name.to_string()).or_insert(vec![]).push(location.to_string());
@@ -1099,12 +1147,13 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         parse_only: parse_only,
         no_trans: no_trans,
         treat_err_as_bug: treat_err_as_bug,
+        incremental_compilation: incremental_compilation || dump_dep_graph,
+        dump_dep_graph: dump_dep_graph,
         no_analysis: no_analysis,
         debugging_opts: debugging_opts,
         prints: prints,
         cg: cg,
-        color: color,
-        show_span: None,
+        error_format: error_format,
         externs: externs,
         crate_name: crate_name,
         alt_std_name: None,
@@ -1224,7 +1273,7 @@ mod tests {
             let sessopts = build_session_options(&matches);
             let sess = build_session(sessopts, None, registry,
                                      Rc::new(DummyCrateStore));
-            assert!(!sess.can_print_warnings);
+            assert!(!sess.diagnostic().can_emit_warnings);
         }
 
         {
@@ -1236,7 +1285,7 @@ mod tests {
             let sessopts = build_session_options(&matches);
             let sess = build_session(sessopts, None, registry,
                                      Rc::new(DummyCrateStore));
-            assert!(sess.can_print_warnings);
+            assert!(sess.diagnostic().can_emit_warnings);
         }
 
         {
@@ -1247,7 +1296,7 @@ mod tests {
             let sessopts = build_session_options(&matches);
             let sess = build_session(sessopts, None, registry,
                                      Rc::new(DummyCrateStore));
-            assert!(sess.can_print_warnings);
+            assert!(sess.diagnostic().can_emit_warnings);
         }
     }
 }
index 7bf96b41dce7f4eb3e5242a713eb25b3caefe2ef..2f3af1c0d09b5c4b14ab2cfd501d1660f4d9d838 100644 (file)
@@ -16,7 +16,9 @@ use util::nodemap::{NodeMap, FnvHashMap};
 
 use syntax::ast::{NodeId, NodeIdAssigner, Name};
 use syntax::codemap::Span;
-use syntax::diagnostic::{self, Emitter};
+use syntax::errors::{self, DiagnosticBuilder};
+use syntax::errors::emitter::{Emitter, BasicEmitter, EmitterWriter};
+use syntax::errors::json::JsonEmitter;
 use syntax::diagnostics;
 use syntax::feature_gate;
 use syntax::parse;
@@ -63,14 +65,10 @@ pub struct Session {
     pub crate_metadata: RefCell<Vec<String>>,
     pub features: RefCell<feature_gate::Features>,
 
-    pub delayed_span_bug: RefCell<Option<(codemap::Span, String)>>,
-
     /// The maximum recursion limit for potentially infinitely recursive
     /// operations such as auto-dereference and monomorphization.
     pub recursion_limit: Cell<usize>,
 
-    pub can_print_warnings: bool,
-
     /// The metadata::creader module may inject an allocator dependency if it
     /// didn't already find one, and this tracks what was injected.
     pub injected_allocator: Cell<Option<ast::CrateNum>>,
@@ -83,23 +81,69 @@ pub struct Session {
 }
 
 impl Session {
-    pub fn span_fatal(&self, sp: Span, msg: &str) -> ! {
-        if self.opts.treat_err_as_bug {
-            self.span_bug(sp, msg);
+    pub fn struct_span_warn<'a>(&'a self,
+                                sp: Span,
+                                msg: &str)
+                                -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_span_warn(sp, msg)
+    }
+    pub fn struct_span_warn_with_code<'a>(&'a self,
+                                          sp: Span,
+                                          msg: &str,
+                                          code: &str)
+                                          -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_span_warn_with_code(sp, msg, code)
+    }
+    pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_warn(msg)
+    }
+    pub fn struct_span_err<'a>(&'a self,
+                               sp: Span,
+                               msg: &str)
+                               -> DiagnosticBuilder<'a>  {
+        match split_msg_into_multilines(msg) {
+            Some(ref msg) => self.diagnostic().struct_span_err(sp, msg),
+            None => self.diagnostic().struct_span_err(sp, msg),
+        }
+    }
+    pub fn struct_span_err_with_code<'a>(&'a self,
+                                         sp: Span,
+                                         msg: &str,
+                                         code: &str)
+                                         -> DiagnosticBuilder<'a>  {
+        match split_msg_into_multilines(msg) {
+            Some(ref msg) => self.diagnostic().struct_span_err_with_code(sp, msg, code),
+            None => self.diagnostic().struct_span_err_with_code(sp, msg, code),
         }
+    }
+    pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_err(msg)
+    }
+    pub fn struct_span_fatal<'a>(&'a self,
+                                 sp: Span,
+                                 msg: &str)
+                                 -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_span_fatal(sp, msg)
+    }
+    pub fn struct_span_fatal_with_code<'a>(&'a self,
+                                           sp: Span,
+                                           msg: &str,
+                                           code: &str)
+                                           -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_span_fatal_with_code(sp, msg, code)
+    }
+    pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_fatal(msg)
+    }
+
+    pub fn span_fatal(&self, sp: Span, msg: &str) -> ! {
         panic!(self.diagnostic().span_fatal(sp, msg))
     }
     pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> ! {
-        if self.opts.treat_err_as_bug {
-            self.span_bug(sp, msg);
-        }
         panic!(self.diagnostic().span_fatal_with_code(sp, msg, code))
     }
     pub fn fatal(&self, msg: &str) -> ! {
-        if self.opts.treat_err_as_bug {
-            self.bug(msg);
-        }
-        panic!(self.diagnostic().handler().fatal(msg))
+        panic!(self.diagnostic().fatal(msg))
     }
     pub fn span_err_or_warn(&self, is_warning: bool, sp: Span, msg: &str) {
         if is_warning {
@@ -109,67 +153,46 @@ impl Session {
         }
     }
     pub fn span_err(&self, sp: Span, msg: &str) {
-        if self.opts.treat_err_as_bug {
-            self.span_bug(sp, msg);
-        }
         match split_msg_into_multilines(msg) {
-            Some(msg) => self.diagnostic().span_err(sp, &msg[..]),
+            Some(msg) => self.diagnostic().span_err(sp, &msg),
             None => self.diagnostic().span_err(sp, msg)
         }
     }
-    pub fn note_rfc_1214(&self, span: Span) {
-        self.span_note(
-            span,
-            &format!("this warning results from recent bug fixes and clarifications; \
-                      it will become a HARD ERROR in the next release. \
-                      See RFC 1214 for details."));
-    }
     pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
-        if self.opts.treat_err_as_bug {
-            self.span_bug(sp, msg);
-        }
         match split_msg_into_multilines(msg) {
-            Some(msg) => self.diagnostic().span_err_with_code(sp, &msg[..], code),
+            Some(msg) => self.diagnostic().span_err_with_code(sp, &msg, code),
             None => self.diagnostic().span_err_with_code(sp, msg, code)
         }
     }
     pub fn err(&self, msg: &str) {
-        if self.opts.treat_err_as_bug {
-            self.bug(msg);
-        }
-        self.diagnostic().handler().err(msg)
+        self.diagnostic().err(msg)
     }
     pub fn err_count(&self) -> usize {
-        self.diagnostic().handler().err_count()
+        self.diagnostic().err_count()
     }
     pub fn has_errors(&self) -> bool {
-        self.diagnostic().handler().has_errors()
+        self.diagnostic().has_errors()
     }
     pub fn abort_if_errors(&self) {
-        self.diagnostic().handler().abort_if_errors();
-
-        let delayed_bug = self.delayed_span_bug.borrow();
-        match *delayed_bug {
-            Some((span, ref errmsg)) => {
-                self.diagnostic().span_bug(span, errmsg);
-            },
-            _ => {}
+        self.diagnostic().abort_if_errors();
+    }
+    pub fn abort_if_new_errors<F>(&self, mut f: F)
+        where F: FnMut()
+    {
+        let count = self.err_count();
+        f();
+        if self.err_count() > count {
+            self.abort_if_errors();
         }
     }
     pub fn span_warn(&self, sp: Span, msg: &str) {
-        if self.can_print_warnings {
-            self.diagnostic().span_warn(sp, msg)
-        }
+        self.diagnostic().span_warn(sp, msg)
     }
     pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) {
-        if self.can_print_warnings {
-            self.diagnostic().span_warn_with_code(sp, msg, code)
-        }
+        self.diagnostic().span_warn_with_code(sp, msg, code)
     }
     pub fn warn(&self, msg: &str) {
-        if self.can_print_warnings {
-            self.diagnostic().handler().warn(msg)
-        }
+        self.diagnostic().warn(msg)
     }
     pub fn opt_span_warn(&self, opt_sp: Option<Span>, msg: &str) {
         match opt_sp {
@@ -177,34 +200,6 @@ impl Session {
             None => self.warn(msg),
         }
     }
-    pub fn span_note(&self, sp: Span, msg: &str) {
-        self.diagnostic().span_note(sp, msg)
-    }
-    pub fn span_end_note(&self, sp: Span, msg: &str) {
-        self.diagnostic().span_end_note(sp, msg)
-    }
-
-    /// Prints out a message with a suggested edit of the code.
-    ///
-    /// See `diagnostic::RenderSpan::Suggestion` for more information.
-    pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) {
-        self.diagnostic().span_suggestion(sp, msg, suggestion)
-    }
-    pub fn span_help(&self, sp: Span, msg: &str) {
-        self.diagnostic().span_help(sp, msg)
-    }
-    pub fn fileline_note(&self, sp: Span, msg: &str) {
-        self.diagnostic().fileline_note(sp, msg)
-    }
-    pub fn fileline_help(&self, sp: Span, msg: &str) {
-        self.diagnostic().fileline_help(sp, msg)
-    }
-    pub fn note(&self, msg: &str) {
-        self.diagnostic().handler().note(msg)
-    }
-    pub fn help(&self, msg: &str) {
-        self.diagnostic().handler().help(msg)
-    }
     pub fn opt_span_bug(&self, opt_sp: Option<Span>, msg: &str) -> ! {
         match opt_sp {
             Some(sp) => self.span_bug(sp, msg),
@@ -213,20 +208,25 @@ impl Session {
     }
     /// Delay a span_bug() call until abort_if_errors()
     pub fn delay_span_bug(&self, sp: Span, msg: &str) {
-        let mut delayed = self.delayed_span_bug.borrow_mut();
-        *delayed = Some((sp, msg.to_string()));
+        self.diagnostic().delay_span_bug(sp, msg)
     }
     pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
         self.diagnostic().span_bug(sp, msg)
     }
     pub fn bug(&self, msg: &str) -> ! {
-        self.diagnostic().handler().bug(msg)
+        self.diagnostic().bug(msg)
+    }
+    pub fn note_without_error(&self, msg: &str) {
+        self.diagnostic().note_without_error(msg)
+    }
+    pub fn span_note_without_error(&self, sp: Span, msg: &str) {
+        self.diagnostic().span_note_without_error(sp, msg)
     }
     pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
         self.diagnostic().span_unimpl(sp, msg)
     }
     pub fn unimpl(&self, msg: &str) -> ! {
-        self.diagnostic().handler().unimpl(msg)
+        self.diagnostic().unimpl(msg)
     }
     pub fn add_lint(&self,
                     lint: &'static lint::Lint,
@@ -251,7 +251,7 @@ impl Session {
 
         id
     }
-    pub fn diagnostic<'a>(&'a self) -> &'a diagnostic::SpanHandler {
+    pub fn diagnostic<'a>(&'a self) -> &'a errors::Handler {
         &self.parse_sess.span_diagnostic
     }
     pub fn codemap<'a>(&'a self) -> &'a codemap::CodeMap {
@@ -260,8 +260,7 @@ impl Session {
     // This exists to help with refactoring to eliminate impossible
     // cases later on
     pub fn impossible_case(&self, sp: Span, msg: &str) -> ! {
-        self.span_bug(sp,
-                      &format!("impossible case reached: {}", msg));
+        self.span_bug(sp, &format!("impossible case reached: {}", msg));
     }
     pub fn verbose(&self) -> bool { self.opts.debugging_opts.verbose }
     pub fn time_passes(&self) -> bool { self.opts.debugging_opts.time_passes }
@@ -404,30 +403,40 @@ pub fn build_session(sopts: config::Options,
         .map(|&(_, ref level)| *level != lint::Allow)
         .last()
         .unwrap_or(true);
+    let treat_err_as_bug = sopts.treat_err_as_bug;
+
+    let codemap = Rc::new(codemap::CodeMap::new());
+    let emitter: Box<Emitter> = match sopts.error_format {
+        config::ErrorOutputType::HumanReadable(color_config) => {
+            Box::new(EmitterWriter::stderr(color_config, Some(registry), codemap.clone()))
+        }
+        config::ErrorOutputType::Json => {
+            Box::new(JsonEmitter::stderr(Some(registry), codemap.clone()))
+        }
+    };
 
-    let codemap = codemap::CodeMap::new();
     let diagnostic_handler =
-        diagnostic::Handler::new(sopts.color, Some(registry), can_print_warnings);
-    let span_diagnostic_handler =
-        diagnostic::SpanHandler::new(diagnostic_handler, codemap);
+        errors::Handler::with_emitter(can_print_warnings,
+                                      treat_err_as_bug,
+                                      emitter);
 
-    build_session_(sopts, local_crate_source_file, span_diagnostic_handler, cstore)
+    build_session_(sopts, local_crate_source_file, diagnostic_handler, codemap, cstore)
 }
 
 pub fn build_session_(sopts: config::Options,
                       local_crate_source_file: Option<PathBuf>,
-                      span_diagnostic: diagnostic::SpanHandler,
+                      span_diagnostic: errors::Handler,
+                      codemap: Rc<codemap::CodeMap>,
                       cstore: Rc<for<'a> CrateStore<'a>>)
                       -> Session {
     let host = match Target::search(config::host_triple()) {
         Ok(t) => t,
         Err(e) => {
-            panic!(span_diagnostic.handler()
-                                  .fatal(&format!("Error loading host specification: {}", e)));
+            panic!(span_diagnostic.fatal(&format!("Error loading host specification: {}", e)));
     }
     };
     let target_cfg = config::build_target_config(&sopts, &span_diagnostic);
-    let p_s = parse::ParseSess::with_span_handler(span_diagnostic);
+    let p_s = parse::ParseSess::with_span_handler(span_diagnostic, codemap);
     let default_sysroot = match sopts.maybe_sysroot {
         Some(_) => None,
         None => Some(filesearch::get_or_default_sysroot())
@@ -442,13 +451,6 @@ pub fn build_session_(sopts: config::Options,
         }
     );
 
-    let can_print_warnings = sopts.lint_opts
-        .iter()
-        .filter(|&&(ref key, _)| *key == "warnings")
-        .map(|&(_, ref level)| *level != lint::Allow)
-        .last()
-        .unwrap_or(true);
-
     let sess = Session {
         target: target_cfg,
         host: host,
@@ -469,10 +471,8 @@ pub fn build_session_(sopts: config::Options,
         crate_types: RefCell::new(Vec::new()),
         dependency_formats: RefCell::new(FnvHashMap()),
         crate_metadata: RefCell::new(Vec::new()),
-        delayed_span_bug: RefCell::new(None),
         features: RefCell::new(feature_gate::Features::new()),
         recursion_limit: Cell::new(64),
-        can_print_warnings: can_print_warnings,
         next_node_id: Cell::new(1),
         injected_allocator: Cell::new(None),
         available_macros: RefCell::new(HashSet::new()),
@@ -481,20 +481,23 @@ pub fn build_session_(sopts: config::Options,
     sess
 }
 
-// Seems out of place, but it uses session, so I'm putting it here
-pub fn expect<T, M>(sess: &Session, opt: Option<T>, msg: M) -> T where
-    M: FnOnce() -> String,
-{
-    diagnostic::expect(sess.diagnostic(), opt, msg)
-}
-
-pub fn early_error(color: diagnostic::ColorConfig, msg: &str) -> ! {
-    let mut emitter = diagnostic::EmitterWriter::stderr(color, None);
-    emitter.emit(None, msg, None, diagnostic::Fatal);
-    panic!(diagnostic::FatalError);
+pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
+    let mut emitter: Box<Emitter> = match output {
+        config::ErrorOutputType::HumanReadable(color_config) => {
+            Box::new(BasicEmitter::stderr(color_config))
+        }
+        config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()),
+    };
+    emitter.emit(None, msg, None, errors::Level::Fatal);
+    panic!(errors::FatalError);
 }
 
-pub fn early_warn(color: diagnostic::ColorConfig, msg: &str) {
-    let mut emitter = diagnostic::EmitterWriter::stderr(color, None);
-    emitter.emit(None, msg, None, diagnostic::Warning);
+pub fn early_warn(output: config::ErrorOutputType, msg: &str) {
+    let mut emitter: Box<Emitter> = match output {
+        config::ErrorOutputType::HumanReadable(color_config) => {
+            Box::new(BasicEmitter::stderr(color_config))
+        }
+        config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()),
+    };
+    emitter.emit(None, msg, None, errors::Level::Warning);
 }
index caf776dad85e06ff28ebda7ee9a77b19dbb4b680..3c6cd26bef6ce87bfc853f040abae95d848a14a2 100644 (file)
@@ -10,8 +10,7 @@
 
 use std::slice;
 use std::path::{Path, PathBuf};
-use session::early_error;
-use syntax::diagnostic;
+use session::{early_error, config};
 
 #[derive(Clone, Debug)]
 pub struct SearchPaths {
@@ -38,7 +37,7 @@ impl SearchPaths {
         SearchPaths { paths: Vec::new() }
     }
 
-    pub fn add_path(&mut self, path: &str, color: diagnostic::ColorConfig) {
+    pub fn add_path(&mut self, path: &str, output: config::ErrorOutputType) {
         let (kind, path) = if path.starts_with("native=") {
             (PathKind::Native, &path["native=".len()..])
         } else if path.starts_with("crate=") {
@@ -53,7 +52,7 @@ impl SearchPaths {
             (PathKind::All, path)
         };
         if path.is_empty() {
-            early_error(color, "empty search path given via `-L`");
+            early_error(output, "empty search path given via `-L`");
         }
         self.paths.push((kind, PathBuf::from(path)));
     }
index 2a3d9cfa6b8882fa2a3732c42be63ad2173e6270..9441e34cb9b1fad3b011d6ede0475f96bf2fc14a 100644 (file)
 
 use std::cell::{RefCell, Cell};
 use std::collections::HashMap;
-use std::collections::hash_state::HashState;
 use std::ffi::CString;
 use std::fmt::Debug;
-use std::hash::Hash;
+use std::hash::{Hash, BuildHasher};
 use std::iter::repeat;
 use std::path::Path;
 use std::time::Instant;
@@ -90,7 +89,6 @@ fn get_resident() -> Option<usize> {
 }
 
 #[cfg(windows)]
-#[cfg_attr(stage0, allow(improper_ctypes))]
 fn get_resident() -> Option<usize> {
     type BOOL = i32;
     type DWORD = u32;
@@ -202,46 +200,38 @@ pub fn block_query<P>(b: &hir::Block, p: P) -> bool where P: FnMut(&hir::Expr) -
     return v.flag;
 }
 
-/// Memoizes a one-argument closure using the given RefCell containing
-/// a type implementing MutableMap to serve as a cache.
-///
-/// In the future the signature of this function is expected to be:
-/// ```
-/// pub fn memoized<T: Clone, U: Clone, M: MutableMap<T, U>>(
-///    cache: &RefCell<M>,
-///    f: &|T| -> U
-/// ) -> impl |T| -> U {
-/// ```
-/// but currently it is not possible.
-///
-/// # Examples
-/// ```
-/// struct Context {
-///    cache: RefCell<HashMap<usize, usize>>
-/// }
-///
-/// fn factorial(ctxt: &Context, n: usize) -> usize {
-///     memoized(&ctxt.cache, n, |n| match n {
-///         0 | 1 => n,
-///         _ => factorial(ctxt, n - 2) + factorial(ctxt, n - 1)
-///     })
-/// }
-/// ```
-#[inline(always)]
-pub fn memoized<T, U, S, F>(cache: &RefCell<HashMap<T, U, S>>, arg: T, f: F) -> U
-    where T: Clone + Hash + Eq,
-          U: Clone,
-          S: HashState,
-          F: FnOnce(T) -> U,
+pub trait MemoizationMap {
+    type Key: Clone;
+    type Value: Clone;
+
+    /// If `key` is present in the map, return the valuee,
+    /// otherwise invoke `op` and store the value in the map.
+    ///
+    /// NB: if the receiver is a `DepTrackingMap`, special care is
+    /// needed in the `op` to ensure that the correct edges are
+    /// added into the dep graph. See the `DepTrackingMap` impl for
+    /// more details!
+    fn memoize<OP>(&self, key: Self::Key, op: OP) -> Self::Value
+        where OP: FnOnce() -> Self::Value;
+}
+
+impl<K, V, S> MemoizationMap for RefCell<HashMap<K,V,S>>
+    where K: Hash+Eq+Clone, V: Clone, S: BuildHasher
 {
-    let key = arg.clone();
-    let result = cache.borrow().get(&key).cloned();
-    match result {
-        Some(result) => result,
-        None => {
-            let result = f(arg);
-            cache.borrow_mut().insert(key, result.clone());
-            result
+    type Key = K;
+    type Value = V;
+
+    fn memoize<OP>(&self, key: K, op: OP) -> V
+        where OP: FnOnce() -> V
+    {
+        let result = self.borrow().get(&key).cloned();
+        match result {
+            Some(result) => result,
+            None => {
+                let result = op();
+                self.borrow_mut().insert(key, result.clone());
+                result
+            }
         }
     }
 }
index 9db34eef91fd19599142111ad67fb7e9d23e6b92..77e39bba54afcd834725abc42c3d63d3f4474e7b 100644 (file)
 
 use middle::def_id::DefId;
 use middle::subst::{self, Subst};
-use middle::ty::{BoundRegion, BrAnon, BrNamed};
-use middle::ty::{ReEarlyBound, BrFresh, ctxt};
-use middle::ty::{ReFree, ReScope, ReStatic, Region, ReEmpty};
-use middle::ty::{ReSkolemized, ReVar, BrEnv};
+use middle::ty::{BrAnon, BrEnv, BrFresh, BrNamed};
 use middle::ty::{TyBool, TyChar, TyStruct, TyEnum};
 use middle::ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyBareFn};
 use middle::ty::{TyParam, TyRawPtr, TyRef, TyTuple};
 use middle::ty::TyClosure;
 use middle::ty::{TyBox, TyTrait, TyInt, TyUint, TyInfer};
-use middle::ty::{self, TypeAndMut, Ty, HasTypeFlags};
-use middle::ty::fold::TypeFoldable;
+use middle::ty::{self, Ty, TypeFoldable};
 
 use std::fmt;
-use syntax::{abi, ast_util};
+use syntax::{abi};
 use syntax::parse::token;
 use syntax::ast::CRATE_NODE_ID;
 use rustc_front::hir;
@@ -255,10 +251,13 @@ fn in_binder<'tcx, T, U>(f: &mut fmt::Formatter,
 struct TraitAndProjections<'tcx>(ty::TraitRef<'tcx>, Vec<ty::ProjectionPredicate<'tcx>>);
 
 impl<'tcx> TypeFoldable<'tcx> for TraitAndProjections<'tcx> {
-    fn fold_with<F:ty::fold::TypeFolder<'tcx>>(&self, folder: &mut F)
-                                              -> TraitAndProjections<'tcx> {
+    fn super_fold_with<F:ty::fold::TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         TraitAndProjections(self.0.fold_with(folder), self.1.fold_with(folder))
     }
+
+    fn super_visit_with<V: ty::fold::TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.0.visit_with(visitor) || self.1.visit_with(visitor)
+    }
 }
 
 impl<'tcx> fmt::Display for TraitAndProjections<'tcx> {
@@ -465,8 +464,7 @@ impl fmt::Debug for ty::Region {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
             ty::ReEarlyBound(ref data) => {
-                write!(f, "ReEarlyBound({:?}, {:?}, {}, {})",
-                       data.def_id,
+                write!(f, "ReEarlyBound({:?}, {}, {})",
                        data.space,
                        data.index,
                        data.name)
@@ -778,9 +776,9 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
         match *self {
             TyBool => write!(f, "bool"),
             TyChar => write!(f, "char"),
-            TyInt(t) => write!(f, "{}", ast_util::int_ty_to_string(t)),
-            TyUint(t) => write!(f, "{}", ast_util::uint_ty_to_string(t)),
-            TyFloat(t) => write!(f, "{}", ast_util::float_ty_to_string(t)),
+            TyInt(t) => write!(f, "{}", t.ty_to_string()),
+            TyUint(t) => write!(f, "{}", t.ty_to_string()),
+            TyFloat(t) => write!(f, "{}", t.ty_to_string()),
             TyBox(typ) => write!(f, "Box<{}>",  typ),
             TyRawPtr(ref tm) => {
                 write!(f, "*{} {}", match tm.mutbl {
@@ -923,13 +921,13 @@ impl fmt::Display for ty::InferTy {
 impl fmt::Display for ty::ExplicitSelfCategory {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.write_str(match *self {
-            ty::StaticExplicitSelfCategory => "static",
-            ty::ByValueExplicitSelfCategory => "self",
-            ty::ByReferenceExplicitSelfCategory(_, hir::MutMutable) => {
+            ty::ExplicitSelfCategory::Static => "static",
+            ty::ExplicitSelfCategory::ByValue => "self",
+            ty::ExplicitSelfCategory::ByReference(_, hir::MutMutable) => {
                 "&mut self"
             }
-            ty::ByReferenceExplicitSelfCategory(_, hir::MutImmutable) => "&self",
-            ty::ByBoxExplicitSelfCategory => "Box<self>",
+            ty::ExplicitSelfCategory::ByReference(_, hir::MutImmutable) => "&self",
+            ty::ExplicitSelfCategory::ByBox => "Box<self>",
         })
     }
 }
index 700cfb645bf05e10af858ad302521464da90c5d3..746d3ba07d6011383da92eaa73e3b22fad55fe60 100644 (file)
 //! one that doesn't; the one that doesn't might get decent parallel
 //! build speedups.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_back"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -36,7 +33,6 @@
 #![feature(libc)]
 #![feature(rand)]
 #![feature(rustc_private)]
-#![feature(clone_from_slice)]
 #![feature(staged_api)]
 #![feature(step_by)]
 #![cfg_attr(test, feature(test, rand))]
index b926f8b061e5836ad095d3b613c26eae02dff03c..6cba27fcf34063b214fb5cf1a7eab44ac30b0c71 100644 (file)
@@ -19,6 +19,7 @@ pub struct RPathConfig<'a> {
     pub out_filename: PathBuf,
     pub is_like_osx: bool,
     pub has_rpath: bool,
+    pub linker_is_gnu: bool,
     pub get_install_prefix_lib_path: &'a mut FnMut() -> PathBuf,
 }
 
@@ -36,6 +37,12 @@ pub fn get_rpath_flags(config: &mut RPathConfig) -> Vec<String> {
     let libs = libs.into_iter().filter_map(|(_, l)| l).collect::<Vec<_>>();
     let rpaths = get_rpaths(config, &libs[..]);
     flags.extend_from_slice(&rpaths_to_flags(&rpaths[..]));
+
+    // Use DT_RUNPATH instead of DT_RPATH if available
+    if config.linker_is_gnu {
+        flags.push("-Wl,--enable-new-dtags".to_string());
+    }
+
     flags
 }
 
@@ -228,6 +235,7 @@ mod tests {
                 used_crates: Vec::new(),
                 has_rpath: true,
                 is_like_osx: true,
+                linker_is_gnu: false,
                 out_filename: PathBuf::from("bin/rustc"),
                 get_install_prefix_lib_path: &mut || panic!(),
             };
@@ -241,6 +249,7 @@ mod tests {
                 get_install_prefix_lib_path: &mut || panic!(),
                 has_rpath: true,
                 is_like_osx: false,
+                linker_is_gnu: true,
             };
             let res = get_rpath_relative_to_output(config,
                                                    Path::new("lib/libstd.so"));
index a5df0b94b337400334ac91578659f89d157e794c..2532882d0127d22721541632b919ea8e759ad056 100644 (file)
@@ -234,6 +234,7 @@ mod svh_visitor {
         SawExprUnary(hir::UnOp),
         SawExprLit(ast::Lit_),
         SawExprCast,
+        SawExprType,
         SawExprIf,
         SawExprWhile,
         SawExprMatch,
@@ -262,6 +263,7 @@ mod svh_visitor {
             ExprUnary(op, _)         => SawExprUnary(op),
             ExprLit(ref lit)         => SawExprLit(lit.node.clone()),
             ExprCast(..)             => SawExprCast,
+            ExprType(..)             => SawExprType,
             ExprIf(..)               => SawExprIf,
             ExprWhile(..)            => SawExprWhile,
             ExprLoop(_, id)          => SawExprLoop(id.map(|id| id.name.as_str())),
index 2883ffd6e9f2244ccd24b24dea153eeac6422792..9791520e9339bbd8ad6908b74e70256abbf83673 100644 (file)
@@ -17,5 +17,6 @@ pub fn opts() -> TargetOptions {
     base.pre_link_args.push("-Wl,--allow-multiple-definition".to_string());
     base.is_like_android = true;
     base.position_independent_executables = true;
+    base.has_elf_tls = false;
     base
 }
index 8b75bb3941477b356aef4df75b75e253961f5614..ffcb6f971ae25ce793273f21e7a714414e777f1e 100644 (file)
@@ -8,10 +8,30 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use std::env;
+
 use target::TargetOptions;
-use std::default::Default;
 
 pub fn opts() -> TargetOptions {
+    // ELF TLS is only available in OSX 10.7+. If you try to compile for 10.6
+    // either the linker will complain if it is used or the binary will end up
+    // segfaulting at runtime when run on 10.6. Rust by default supports OSX
+    // 10.7+, but there is a standard environment variable,
+    // MACOSX_DEPLOYMENT_TARGET, which is used to signal targeting older
+    // versions of OSX. For example compiling on 10.10 with
+    // MACOSX_DEPLOYMENT_TARGET set to 10.6 will cause the linker to generate
+    // warnings about the usage of ELF TLS.
+    //
+    // Here we detect what version is being requested, defaulting to 10.7. ELF
+    // TLS is flagged as enabled if it looks to be supported.
+    let deployment_target = env::var("MACOSX_DEPLOYMENT_TARGET").ok();
+    let version = deployment_target.as_ref().and_then(|s| {
+        let mut i = s.splitn(2, ".");
+        i.next().and_then(|a| i.next().map(|b| (a, b)))
+    }).and_then(|(a, b)| {
+        a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok()
+    }).unwrap_or((10, 7));
+
     TargetOptions {
         // OSX has -dead_strip, which doesn't rely on ffunction_sections
         function_sections: false,
@@ -25,6 +45,7 @@ pub fn opts() -> TargetOptions {
         archive_format: "bsd".to_string(),
         pre_link_args: Vec::new(),
         exe_allocation_crate: super::maybe_jemalloc(),
+        has_elf_tls: version >= (10, 7),
         .. Default::default()
     }
 }
index 77030f5d76827f929c5d8cd1ea22b533386fa20c..d182fd9605640677322f9d1d425a6732d6bdb488 100644 (file)
@@ -88,6 +88,7 @@ pub fn opts(arch: Arch) -> TargetOptions {
         dynamic_linking: false,
         executables: true,
         pre_link_args: pre_link_args(arch),
+        has_elf_tls: false,
         .. super::apple_base::opts()
     }
 }
index 6492fa5015970e07ca149a11c3ab32866dadf997..0efcf73ee86808ecff5243e64d199410ffc996a1 100644 (file)
@@ -30,6 +30,7 @@ pub fn opts() -> TargetOptions {
         position_independent_executables: true,
         archive_format: "gnu".to_string(),
         exe_allocation_crate: super::maybe_jemalloc(),
+        has_elf_tls: true,
         .. Default::default()
     }
 }
index 1bfb7471daa212b580c75cdfb9790afce158fb5d..51149101e0c2dd441586d35a197cb59cd191fa86 100644 (file)
@@ -48,7 +48,7 @@
 use serialize::json::Json;
 use std::default::Default;
 use std::io::prelude::*;
-use syntax::{diagnostic, abi};
+use syntax::abi;
 
 mod android_base;
 mod apple_base;
@@ -80,7 +80,7 @@ pub struct Target {
     /// Vendor name to use for conditional compilation.
     pub target_vendor: String,
     /// Architecture to use for ABI considerations. Valid options: "x86", "x86_64", "arm",
-    /// "aarch64", "mips", and "powerpc". "mips" includes "mipsel".
+    /// "aarch64", "mips", "powerpc", "powerpc64" and "powerpc64le". "mips" includes "mipsel".
     pub arch: String,
     /// Optional settings with defaults.
     pub options: TargetOptions,
@@ -195,6 +195,10 @@ pub struct TargetOptions {
     /// Default crate for allocation symbols to link against
     pub lib_allocation_crate: String,
     pub exe_allocation_crate: String,
+
+    /// Flag indicating whether ELF TLS (e.g. #[thread_local]) is available for
+    /// this target.
+    pub has_elf_tls: bool,
 }
 
 impl Default for TargetOptions {
@@ -240,6 +244,7 @@ impl Default for TargetOptions {
             lib_allocation_crate: "alloc_system".to_string(),
             exe_allocation_crate: "alloc_system".to_string(),
             allow_asm: true,
+            has_elf_tls: false,
         }
     }
 }
@@ -263,17 +268,13 @@ impl Target {
     pub fn from_json(obj: Json) -> Target {
         // this is 1. ugly, 2. error prone.
 
-
-        let handler = diagnostic::Handler::new(diagnostic::Auto, None, true);
-
         let get_req_field = |name: &str| {
             match obj.find(name)
                      .map(|s| s.as_string())
                      .and_then(|os| os.map(|s| s.to_string())) {
                 Some(val) => val,
                 None => {
-                    panic!(handler.fatal(&format!("Field {} in target specification is required",
-                                                  name)))
+                    panic!("Field {} in target specification is required", name)
                 }
             }
         };
@@ -412,6 +413,8 @@ impl Target {
             mips_unknown_linux_gnu,
             mipsel_unknown_linux_gnu,
             powerpc_unknown_linux_gnu,
+            powerpc64_unknown_linux_gnu,
+            powerpc64le_unknown_linux_gnu,
             arm_unknown_linux_gnueabi,
             arm_unknown_linux_gnueabihf,
             aarch64_unknown_linux_gnu,
diff --git a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs
new file mode 100644 (file)
index 0000000..3ba0c67
--- /dev/null
@@ -0,0 +1,28 @@
+// 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 target::Target;
+
+pub fn target() -> Target {
+    let mut base = super::linux_base::opts();
+    base.cpu = "ppc64".to_string();
+    base.pre_link_args.push("-m64".to_string());
+
+    Target {
+        llvm_target: "powerpc64-unknown-linux-gnu".to_string(),
+        target_endian: "big".to_string(),
+        target_pointer_width: "64".to_string(),
+        arch: "powerpc64".to_string(),
+        target_os: "linux".to_string(),
+        target_env: "gnu".to_string(),
+        target_vendor: "unknown".to_string(),
+        options: base,
+    }
+}
diff --git a/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs
new file mode 100644 (file)
index 0000000..c82666f
--- /dev/null
@@ -0,0 +1,28 @@
+// 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 target::Target;
+
+pub fn target() -> Target {
+    let mut base = super::linux_base::opts();
+    base.cpu = "ppc64le".to_string();
+    base.pre_link_args.push("-m64".to_string());
+
+    Target {
+        llvm_target: "powerpc64le-unknown-linux-gnu".to_string(),
+        target_endian: "little".to_string(),
+        target_pointer_width: "64".to_string(),
+        arch: "powerpc64le".to_string(),
+        target_os: "linux".to_string(),
+        target_env: "gnu".to_string(),
+        target_vendor: "unknown".to_string(),
+        options: base,
+    }
+}
index 97816a2b2ae84ab13e8b16869d724719e9976154..e2a929f58e14da0b2fe0aa723a9c1c052d8e31d9 100644 (file)
@@ -9,14 +9,10 @@
 // except according to those terms.
 
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_bitflags"]
 #![feature(associated_consts)]
 #![feature(staged_api)]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
-#![cfg_attr(stage0, feature(no_std))]
 #![no_std]
 #![unstable(feature = "rustc_private", issue = "27812")]
 
index 50169ca64307b3f91f727d60618168a5d9a483b1..2d30b827750acda7b2bbba833b7689d829066dff 100644 (file)
@@ -21,6 +21,7 @@ use self::UseError::*;
 use borrowck::*;
 use borrowck::InteriorKind::{InteriorElement, InteriorField};
 use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::expr_use_visitor::MutateMode;
 use rustc::middle::infer;
 use rustc::middle::mem_categorization as mc;
 use rustc::middle::mem_categorization::Categorization;
@@ -161,7 +162,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
         match opt_loan_path(&assignee_cmt) {
             Some(lp) => {
                 match mode {
-                    euv::Init | euv::JustWrite => {
+                    MutateMode::Init | MutateMode::JustWrite => {
                         // In a case like `path = 1`, then path does not
                         // have to be *FULLY* initialized, but we still
                         // must be careful lest it contains derefs of
@@ -171,7 +172,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
                                                              MovedInUse,
                                                              &lp);
                     }
-                    euv::WriteAndRead => {
+                    MutateMode::WriteAndRead => {
                         // In a case like `path += 1`, then path must be
                         // fully initialized, since we will read it before
                         // we write it.
@@ -465,44 +466,44 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
                 format!("`{}`", ol)
             };
 
-            match (new_loan.kind, old_loan.kind) {
+            let mut err = match (new_loan.kind, old_loan.kind) {
                 (ty::MutBorrow, ty::MutBorrow) => {
-                    span_err!(self.bccx, new_loan.span, E0499,
-                              "cannot borrow `{}`{} as mutable \
-                               more than once at a time",
-                              nl, new_loan_msg);
+                    struct_span_err!(self.bccx, new_loan.span, E0499,
+                                     "cannot borrow `{}`{} as mutable \
+                                      more than once at a time",
+                                     nl, new_loan_msg)
                 }
 
                 (ty::UniqueImmBorrow, _) => {
-                    span_err!(self.bccx, new_loan.span, E0500,
-                              "closure requires unique access to `{}` \
-                               but {} is already borrowed{}",
-                              nl, ol_pronoun, old_loan_msg);
+                    struct_span_err!(self.bccx, new_loan.span, E0500,
+                                     "closure requires unique access to `{}` \
+                                      but {} is already borrowed{}",
+                                     nl, ol_pronoun, old_loan_msg)
                 }
 
                 (_, ty::UniqueImmBorrow) => {
-                    span_err!(self.bccx, new_loan.span, E0501,
-                              "cannot borrow `{}`{} as {} because \
-                               previous closure requires unique access",
-                              nl, new_loan_msg, new_loan.kind.to_user_str());
+                    struct_span_err!(self.bccx, new_loan.span, E0501,
+                                     "cannot borrow `{}`{} as {} because \
+                                      previous closure requires unique access",
+                                     nl, new_loan_msg, new_loan.kind.to_user_str())
                 }
 
                 (_, _) => {
-                    span_err!(self.bccx, new_loan.span, E0502,
-                              "cannot borrow `{}`{} as {} because \
-                               {} is also borrowed as {}{}",
-                              nl,
-                              new_loan_msg,
-                              new_loan.kind.to_user_str(),
-                              ol_pronoun,
-                              old_loan.kind.to_user_str(),
-                              old_loan_msg);
+                    struct_span_err!(self.bccx, new_loan.span, E0502,
+                                     "cannot borrow `{}`{} as {} because \
+                                      {} is also borrowed as {}{}",
+                                     nl,
+                                     new_loan_msg,
+                                     new_loan.kind.to_user_str(),
+                                     ol_pronoun,
+                                     old_loan.kind.to_user_str(),
+                                     old_loan_msg)
                 }
-            }
+            };
 
             match new_loan.cause {
                 euv::ClosureCapture(span) => {
-                    self.bccx.span_note(
+                    err.span_note(
                         span,
                         &format!("borrow occurs due to use of `{}` in closure",
                                 nl));
@@ -553,15 +554,15 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
                 }
             };
 
-            self.bccx.span_note(
+            err.span_note(
                 old_loan.span,
                 &format!("{}; {}", borrow_summary, rule_summary));
 
             let old_loan_span = self.tcx().map.span(
                 old_loan.kill_scope.node_id(&self.tcx().region_maps));
-            self.bccx.span_end_note(old_loan_span,
-                                    "previous borrow ends here");
-
+            err.span_end_note(old_loan_span,
+                              "previous borrow ends here");
+            err.emit();
             return false;
         }
 
@@ -616,14 +617,14 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
         match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
             UseOk => { }
             UseWhileBorrowed(loan_path, loan_span) => {
-                span_err!(self.bccx, span, E0503,
-                          "cannot use `{}` because it was mutably borrowed",
-                          &self.bccx.loan_path_to_string(copy_path));
-                self.bccx.span_note(
-                    loan_span,
-                    &format!("borrow of `{}` occurs here",
-                            &self.bccx.loan_path_to_string(&*loan_path))
-                    );
+                struct_span_err!(self.bccx, span, E0503,
+                                 "cannot use `{}` because it was mutably borrowed",
+                                 &self.bccx.loan_path_to_string(copy_path))
+                    .span_note(loan_span,
+                               &format!("borrow of `{}` occurs here",
+                                       &self.bccx.loan_path_to_string(&*loan_path))
+                               )
+                    .emit();
             }
         }
     }
@@ -639,24 +640,25 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
         match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
             UseOk => { }
             UseWhileBorrowed(loan_path, loan_span) => {
-                match move_kind {
+                let mut err = match move_kind {
                     move_data::Captured =>
-                        span_err!(self.bccx, span, E0504,
-                                  "cannot move `{}` into closure because it is borrowed",
-                                  &self.bccx.loan_path_to_string(move_path)),
+                        struct_span_err!(self.bccx, span, E0504,
+                                         "cannot move `{}` into closure because it is borrowed",
+                                         &self.bccx.loan_path_to_string(move_path)),
                     move_data::Declared |
                     move_data::MoveExpr |
                     move_data::MovePat =>
-                        span_err!(self.bccx, span, E0505,
-                                  "cannot move out of `{}` because it is borrowed",
-                                  &self.bccx.loan_path_to_string(move_path))
+                        struct_span_err!(self.bccx, span, E0505,
+                                         "cannot move out of `{}` because it is borrowed",
+                                         &self.bccx.loan_path_to_string(move_path))
                 };
 
-                self.bccx.span_note(
+                err.span_note(
                     loan_span,
                     &format!("borrow of `{}` occurs here",
                             &self.bccx.loan_path_to_string(&*loan_path))
                     );
+                err.emit();
             }
         }
     }
@@ -744,7 +746,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
                 self.check_if_assigned_path_is_moved(id, span,
                                                      use_kind, lp_base);
             }
-            LpExtend(ref lp_base, _, LpInterior(InteriorField(_))) => {
+            LpExtend(ref lp_base, _, LpInterior(_, InteriorField(_))) => {
                 match lp_base.to_type().sty {
                     ty::TyStruct(def, _) | ty::TyEnum(def, _) if def.has_dtor() => {
                         // In the case where the owner implements drop, then
@@ -770,7 +772,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
                 self.check_if_assigned_path_is_moved(id, span,
                                                      use_kind, lp_base);
             }
-            LpExtend(ref lp_base, _, LpInterior(InteriorElement(..))) |
+            LpExtend(ref lp_base, _, LpInterior(_, InteriorElement(..))) |
             LpExtend(ref lp_base, _, LpDeref(_)) => {
                 // assigning to `P[i]` requires `P` is initialized
                 // assigning to `(*P)` requires `P` is initialized
@@ -818,12 +820,12 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
                                    span: Span,
                                    loan_path: &LoanPath<'tcx>,
                                    loan: &Loan) {
-        span_err!(self.bccx, span, E0506,
-                  "cannot assign to `{}` because it is borrowed",
-                  self.bccx.loan_path_to_string(loan_path));
-        self.bccx.span_note(
-            loan.span,
-            &format!("borrow of `{}` occurs here",
-                    self.bccx.loan_path_to_string(loan_path)));
+        struct_span_err!(self.bccx, span, E0506,
+                         "cannot assign to `{}` because it is borrowed",
+                         self.bccx.loan_path_to_string(loan_path))
+            .span_note(loan.span,
+                       &format!("borrow of `{}` occurs here",
+                               self.bccx.loan_path_to_string(loan_path)))
+            .emit();
     }
 }
index 1639fcf77a66122120d7abaac5747e8bc1f6419f..c5e2b69683b10cc3676f4141c3186d37bb12d490 100644 (file)
@@ -379,7 +379,7 @@ fn add_fragment_siblings<'tcx>(this: &MoveData<'tcx>,
         // bind.
         //
         // Anyway, for now: LV[j] is not tracked precisely
-        LpExtend(_, _, LpInterior(InteriorElement(..))) => {
+        LpExtend(_, _, LpInterior(_, InteriorElement(..))) => {
             let mp = this.move_path(tcx, lp.clone());
             gathered_fragments.push(AllButOneFrom(mp));
         }
@@ -387,7 +387,7 @@ fn add_fragment_siblings<'tcx>(this: &MoveData<'tcx>,
         // field access LV.x and tuple access LV#k are the cases
         // we are interested in
         LpExtend(ref loan_parent, mc,
-                 LpInterior(InteriorField(ref field_name))) => {
+                 LpInterior(_, InteriorField(ref field_name))) => {
             let enum_variant_info = match loan_parent.kind {
                 LpDowncast(ref loan_parent_2, variant_def_id) =>
                     Some((variant_def_id, loan_parent_2.clone())),
@@ -516,7 +516,7 @@ fn add_fragment_sibling_core<'tcx>(this: &MoveData<'tcx>,
         LpVar(..) | LpUpvar(..) | LpExtend(..) => enum_variant_did,
     };
 
-    let loan_path_elem = LpInterior(InteriorField(new_field_name));
+    let loan_path_elem = LpInterior(opt_variant_did, InteriorField(new_field_name));
     let new_lp_type = match new_field_name {
         mc::NamedField(ast_name) =>
             tcx.named_element_ty(parent.to_type(), ast_name, opt_variant_did),
index 47f29a26db145d09baac303b694cbf4cdacd7f65..6f6ce67380be470db2e6108238557cda4e5949d3 100644 (file)
@@ -29,10 +29,12 @@ use syntax::ast;
 use syntax::codemap::Span;
 use syntax::ast::NodeId;
 use rustc_front::hir;
-use rustc_front::hir::{Expr, FnDecl, Block, Pat};
+use rustc_front::hir::Expr;
 use rustc_front::intravisit;
 use rustc_front::intravisit::Visitor;
 
+use self::restrictions::RestrictionResult;
+
 mod lifetime;
 mod restrictions;
 mod gather_moves;
@@ -354,12 +356,12 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
 
         // Create the loan record (if needed).
         let loan = match restr {
-            restrictions::Safe => {
+            RestrictionResult::Safe => {
                 // No restrictions---no loan record necessary
                 return;
             }
 
-            restrictions::SafeIf(loan_path, restricted_paths) => {
+            RestrictionResult::SafeIf(loan_path, restricted_paths) => {
                 let loan_scope = match loan_region {
                     ty::ReScope(scope) => scope,
 
index a56a03174f609af1c5e3c83534eca65b40774836..4cb9673785ecb36303ffd689cec92dd222c5ce2a 100644 (file)
@@ -15,6 +15,7 @@ use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
 use rustc::middle::ty;
 use syntax::ast;
 use syntax::codemap;
+use syntax::errors::DiagnosticBuilder;
 use rustc_front::hir;
 
 pub struct MoveErrorCollector<'tcx> {
@@ -68,13 +69,14 @@ fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
                                 errors: &Vec<MoveError<'tcx>>) {
     let grouped_errors = group_errors_with_same_origin(errors);
     for error in &grouped_errors {
-        report_cannot_move_out_of(bccx, error.move_from.clone());
+        let mut err = report_cannot_move_out_of(bccx, error.move_from.clone());
         let mut is_first_note = true;
         for move_to in &error.move_to_places {
-            note_move_destination(bccx, move_to.span,
+            note_move_destination(&mut err, move_to.span,
                                   move_to.name, is_first_note);
             is_first_note = false;
         }
+        err.emit();
     }
 }
 
@@ -112,24 +114,28 @@ fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>)
 
 // (keep in sync with gather_moves::check_and_get_illegal_move_origin )
 fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                       move_from: mc::cmt<'tcx>) {
+                                       move_from: mc::cmt<'tcx>)
+                                       -> DiagnosticBuilder<'a> {
     match move_from.cat {
         Categorization::Deref(_, _, mc::BorrowedPtr(..)) |
         Categorization::Deref(_, _, mc::Implicit(..)) |
         Categorization::Deref(_, _, mc::UnsafePtr(..)) |
         Categorization::StaticItem => {
-            span_err!(bccx, move_from.span, E0507,
-                      "cannot move out of {}",
-                      move_from.descriptive_string(bccx.tcx));
+            struct_span_err!(bccx, move_from.span, E0507,
+                             "cannot move out of {}",
+                             move_from.descriptive_string(bccx.tcx))
         }
 
         Categorization::Interior(ref b, mc::InteriorElement(Kind::Index, _)) => {
             let expr = bccx.tcx.map.expect_expr(move_from.id);
             if let hir::ExprIndex(..) = expr.node {
-                span_err!(bccx, move_from.span, E0508,
-                          "cannot move out of type `{}`, \
-                           a non-copy fixed-size array",
-                          b.ty);
+                struct_span_err!(bccx, move_from.span, E0508,
+                                 "cannot move out of type `{}`, \
+                                  a non-copy fixed-size array",
+                                 b.ty)
+            } else {
+                bccx.span_bug(move_from.span, "this path should not cause illegal move");
+                unreachable!();
             }
         }
 
@@ -138,39 +144,41 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
             match b.ty.sty {
                 ty::TyStruct(def, _) |
                 ty::TyEnum(def, _) if def.has_dtor() => {
-                    span_err!(bccx, move_from.span, E0509,
-                              "cannot move out of type `{}`, \
-                               which defines the `Drop` trait",
-                              b.ty);
+                    struct_span_err!(bccx, move_from.span, E0509,
+                                     "cannot move out of type `{}`, \
+                                      which defines the `Drop` trait",
+                                     b.ty)
                 },
                 _ => {
-                    bccx.span_bug(move_from.span, "this path should not cause illegal move")
+                    bccx.span_bug(move_from.span, "this path should not cause illegal move");
+                    unreachable!();
                 }
             }
         }
         _ => {
-            bccx.span_bug(move_from.span, "this path should not cause illegal move")
+            bccx.span_bug(move_from.span, "this path should not cause illegal move");
+            unreachable!();
         }
     }
 }
 
-fn note_move_destination(bccx: &BorrowckCtxt,
+fn note_move_destination(err: &mut DiagnosticBuilder,
                          move_to_span: codemap::Span,
                          pat_name: ast::Name,
                          is_first_note: bool) {
     if is_first_note {
-        bccx.span_note(
+        err.span_note(
             move_to_span,
             "attempting to move value to here");
-        bccx.fileline_help(
+        err.fileline_help(
             move_to_span,
             &format!("to prevent the move, \
-                     use `ref {0}` or `ref mut {0}` to capture value by \
-                     reference",
-                    pat_name));
+                      use `ref {0}` or `ref mut {0}` to capture value by \
+                      reference",
+                     pat_name));
     } else {
-        bccx.span_note(move_to_span,
-                       &format!("and here (use `ref {0}` or `ref mut {0}`)",
+        err.span_note(move_to_span,
+                      &format!("and here (use `ref {0}` or `ref mut {0}`)",
                                pat_name));
     }
 }
index e7ce93972633be7216b6315b8b157860eb31b981..2a0d8ef276648a30f34d57973a90595b3cd6c870 100644 (file)
@@ -10,8 +10,6 @@
 
 //! Computes the restrictions that result from a borrow.
 
-pub use self::RestrictionResult::*;
-
 use borrowck::*;
 use rustc::middle::expr_use_visitor as euv;
 use rustc::middle::mem_categorization as mc;
@@ -69,19 +67,19 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
                 // are inherently non-aliasable, they can only be
                 // accessed later through the borrow itself and hence
                 // must inherently comply with its terms.
-                Safe
+                RestrictionResult::Safe
             }
 
             Categorization::Local(local_id) => {
                 // R-Variable, locally declared
                 let lp = new_lp(LpVar(local_id));
-                SafeIf(lp.clone(), vec![lp])
+                RestrictionResult::SafeIf(lp.clone(), vec![lp])
             }
 
             Categorization::Upvar(mc::Upvar { id, .. }) => {
                 // R-Variable, captured into closure
                 let lp = new_lp(LpUpvar(id));
-                SafeIf(lp.clone(), vec![lp])
+                RestrictionResult::SafeIf(lp.clone(), vec![lp])
             }
 
             Categorization::Downcast(cmt_base, _) => {
@@ -97,12 +95,16 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
                 // Overwriting the base would not change the type of
                 // the memory, so no additional restrictions are
                 // needed.
+                let opt_variant_id = match cmt_base.cat {
+                    Categorization::Downcast(_, variant_id) => Some(variant_id),
+                    _ => None
+                };
                 let result = self.restrict(cmt_base);
-                self.extend(result, &cmt, LpInterior(i.cleaned()))
+                self.extend(result, &cmt, LpInterior(opt_variant_id, i.cleaned()))
             }
 
             Categorization::StaticItem => {
-                Safe
+                RestrictionResult::Safe
             }
 
             Categorization::Deref(cmt_base, _, pk) => {
@@ -129,11 +131,11 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
                                     cmt: cmt_base,
                                     code: err_borrowed_pointer_too_short(
                                         self.loan_region, lt)});
-                            return Safe;
+                            return RestrictionResult::Safe;
                         }
 
                         match bk {
-                            ty::ImmBorrow => Safe,
+                            ty::ImmBorrow => RestrictionResult::Safe,
                             ty::MutBorrow | ty::UniqueImmBorrow => {
                                 // R-Deref-Mut-Borrowed
                                 //
@@ -146,7 +148,7 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
                         }
                     }
                     // Borrowck is not relevant for raw pointers
-                    mc::UnsafePtr(..) => Safe
+                    mc::UnsafePtr(..) => RestrictionResult::Safe
                 }
             }
         }
@@ -157,12 +159,12 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
               cmt: &mc::cmt<'tcx>,
               elem: LoanPathElem) -> RestrictionResult<'tcx> {
         match result {
-            Safe => Safe,
-            SafeIf(base_lp, mut base_vec) => {
+            RestrictionResult::Safe => RestrictionResult::Safe,
+            RestrictionResult::SafeIf(base_lp, mut base_vec) => {
                 let v = LpExtend(base_lp, cmt.mutbl, elem);
                 let lp = Rc::new(LoanPath::new(v, cmt.ty));
                 base_vec.push(lp.clone());
-                SafeIf(lp, base_vec)
+                RestrictionResult::SafeIf(lp, base_vec)
             }
         }
     }
index b727f10d276ac0f0f400ffe6859ef60a20ff6b1c..631149e69d77ecdd024def52cc4ab33f43169517 100644 (file)
@@ -20,6 +20,7 @@ pub use self::MovedValueUseKind::*;
 
 use self::InteriorKind::*;
 
+use rustc::dep_graph::DepNode;
 use rustc::front::map as hir_map;
 use rustc::front::map::blocks::FnParts;
 use rustc::middle::cfg;
@@ -38,8 +39,9 @@ use rustc::middle::ty::{self, Ty};
 use std::fmt;
 use std::mem;
 use std::rc::Rc;
-use syntax::ast::{self, NodeId};
+use syntax::ast;
 use syntax::codemap::Span;
+use syntax::errors::DiagnosticBuilder;
 
 use rustc_front::hir;
 use rustc_front::hir::{FnDecl, Block};
@@ -108,7 +110,7 @@ pub fn check_crate(tcx: &ty::ctxt) {
         }
     };
 
-    tcx.map.krate().visit_all_items(&mut bccx);
+    tcx.visit_all_items_in_krate(DepNode::BorrowCheck, &mut bccx);
 
     if tcx.sess.borrowck_stats() {
         println!("--- borrowck stats ---");
@@ -377,10 +379,18 @@ impl ToInteriorKind for mc::InteriorKind {
     }
 }
 
+// This can be:
+// - a pointer dereference (`*LV` in README.md)
+// - a field reference, with an optional definition of the containing
+//   enum variant (`LV.f` in README.md)
+// `DefId` is present when the field is part of struct that is in
+// a variant of an enum. For instance in:
+// `enum E { X { foo: u32 }, Y { foo: u32 }}`
+// each `foo` is qualified by the definitition id of the variant (`X` or `Y`).
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 pub enum LoanPathElem {
-    LpDeref(mc::PointerKind),    // `*LV` in README.md
-    LpInterior(InteriorKind),    // `LV.f` in README.md
+    LpDeref(mc::PointerKind),
+    LpInterior(Option<DefId>, InteriorKind),
 }
 
 pub fn closure_to_block(closure_id: ast::NodeId,
@@ -413,8 +423,9 @@ impl<'tcx> LoanPath<'tcx> {
 
     fn has_fork(&self, other: &LoanPath<'tcx>) -> bool {
         match (&self.kind, &other.kind) {
-            (&LpExtend(ref base, _, LpInterior(id)), &LpExtend(ref base2, _, LpInterior(id2))) =>
-                if id == id2 {
+            (&LpExtend(ref base, _, LpInterior(opt_variant_id, id)),
+             &LpExtend(ref base2, _, LpInterior(opt_variant_id2, id2))) =>
+                if id == id2 && opt_variant_id == opt_variant_id2 {
                     base.has_fork(&**base2)
                 } else {
                     true
@@ -428,23 +439,23 @@ impl<'tcx> LoanPath<'tcx> {
     fn depth(&self) -> usize {
         match self.kind {
             LpExtend(ref base, _, LpDeref(_)) => base.depth(),
-            LpExtend(ref base, _, LpInterior(_)) => base.depth() + 1,
+            LpExtend(ref base, _, LpInterior(_, _)) => base.depth() + 1,
             _ => 0,
         }
     }
 
     fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> {
         match (&self.kind, &other.kind) {
-            (&LpExtend(ref base, a, LpInterior(id)),
-             &LpExtend(ref base2, _, LpInterior(id2))) => {
-                if id == id2 {
+            (&LpExtend(ref base, a, LpInterior(opt_variant_id, id)),
+             &LpExtend(ref base2, _, LpInterior(opt_variant_id2, id2))) => {
+                if id == id2 && opt_variant_id == opt_variant_id2 {
                     base.common(&**base2).map(|x| {
                         let xd = x.depth();
                         if base.depth() == xd && base2.depth() == xd {
                             assert_eq!(base.ty, base2.ty);
                             assert_eq!(self.ty, other.ty);
                             LoanPath {
-                                kind: LpExtend(Rc::new(x), a, LpInterior(id)),
+                                kind: LpExtend(Rc::new(x), a, LpInterior(opt_variant_id, id)),
                                 ty: self.ty,
                             }
                         } else {
@@ -509,7 +520,11 @@ pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
 
         Categorization::Interior(ref cmt_base, ik) => {
             opt_loan_path(cmt_base).map(|lp| {
-                new_lp(LpExtend(lp, cmt.mutbl, LpInterior(ik.cleaned())))
+                let opt_variant_id = match cmt_base.cat {
+                    Categorization::Downcast(_, did) =>  Some(did),
+                    _ => None
+                };
+                new_lp(LpExtend(lp, cmt.mutbl, LpInterior(opt_variant_id, ik.cleaned())))
             })
         }
 
@@ -578,10 +593,11 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
         }
 
         // General fallback.
-        self.span_err(
+        let mut db = self.struct_span_err(
             err.span,
             &self.bckerr_to_string(&err));
-        self.note_and_explain_bckerr(err);
+        self.note_and_explain_bckerr(&mut db, err);
+        db.emit();
     }
 
     pub fn report_use_of_moved_value<'b>(&self,
@@ -596,16 +612,17 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
             MovedInCapture => "capture",
         };
 
-        let (ol, moved_lp_msg) = match the_move.kind {
+        let (ol, moved_lp_msg, mut err) = match the_move.kind {
             move_data::Declared => {
-                span_err!(
+                let err = struct_span_err!(
                     self.tcx.sess, use_span, E0381,
                     "{} of possibly uninitialized variable: `{}`",
                     verb,
                     self.loan_path_to_string(lp));
 
                 (self.loan_path_to_string(moved_lp),
-                 String::new())
+                 String::new(),
+                 err)
             }
             _ => {
                 // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
@@ -640,11 +657,11 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                 let msg = if !has_fork && partial { "partially " }
                           else if has_fork && !has_common { "collaterally "}
                           else { "" };
-                span_err!(
+                let err = struct_span_err!(
                     self.tcx.sess, use_span, E0382,
                     "{} of {}moved value: `{}`",
                     verb, msg, nl);
-                (ol, moved_lp_msg)
+                (ol, moved_lp_msg, err)
             }
         };
 
@@ -671,7 +688,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                 // multiple times. Avoid printing the same span and adjust the wording so it makes
                 // more sense that it's from multiple evalutations.
                 if expr_span == use_span {
-                    self.tcx.sess.note(
+                    err.note(
                         &format!("`{}` was previously moved here{} because it has type `{}`, \
                                   which is {}",
                                  ol,
@@ -679,7 +696,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                                  expr_ty,
                                  suggestion));
                 } else {
-                    self.tcx.sess.span_note(
+                    err.span_note(
                         expr_span,
                         &format!("`{}` moved here{} because it has type `{}`, which is {}",
                                  ol,
@@ -692,7 +709,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
             move_data::MovePat => {
                 let pat_ty = self.tcx.node_id_to_type(the_move.id);
                 let span = self.tcx.map.span(the_move.id);
-                self.tcx.sess.span_note(span,
+                err.span_note(span,
                     &format!("`{}` moved here{} because it has type `{}`, \
                              which is moved by default",
                             ol,
@@ -700,14 +717,14 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                             pat_ty));
                 match self.tcx.sess.codemap().span_to_snippet(span) {
                     Ok(string) => {
-                        self.tcx.sess.span_suggestion(
+                        err.span_suggestion(
                             span,
                             &format!("if you would like to borrow the value instead, \
                                       use a `ref` binding as shown:"),
                             format!("ref {}", string));
                     },
                     Err(_) => {
-                        self.tcx.sess.fileline_help(span,
+                        err.fileline_help(span,
                             "use `ref` to override");
                     },
                 }
@@ -733,7 +750,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                                     expr_ty,
                                     ("moved by default",
                                      "make a copy and capture that instead to override"));
-                self.tcx.sess.span_note(
+                err.span_note(
                     expr_span,
                     &format!("`{}` moved into closure environment here{} because it \
                             has type `{}`, which is {}",
@@ -741,9 +758,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                             moved_lp_msg,
                             moved_lp.ty,
                             suggestion));
-                self.tcx.sess.fileline_help(expr_span, help);
+                err.fileline_help(expr_span, help);
             }
         }
+        err.emit();
 
         fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
                                     span: Span,
@@ -778,35 +796,36 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                                                 lp: &LoanPath<'tcx>,
                                                 assign:
                                                 &move_data::Assignment) {
-        span_err!(
+        struct_span_err!(
             self.tcx.sess, span, E0384,
             "re-assignment of immutable variable `{}`",
-            self.loan_path_to_string(lp));
-        self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
+            self.loan_path_to_string(lp))
+            .span_note(assign.span, "prior assignment occurs here")
+            .emit();
     }
 
     pub fn span_err(&self, s: Span, m: &str) {
         self.tcx.sess.span_err(s, m);
     }
 
-    pub fn span_err_with_code(&self, s: Span, msg: &str, code: &str) {
-        self.tcx.sess.span_err_with_code(s, msg, code);
-    }
-
-    pub fn span_bug(&self, s: Span, m: &str) {
-        self.tcx.sess.span_bug(s, m);
+    pub fn struct_span_err(&self, s: Span, m: &str) -> DiagnosticBuilder<'a> {
+        self.tcx.sess.struct_span_err(s, m)
     }
 
-    pub fn span_note(&self, s: Span, m: &str) {
-        self.tcx.sess.span_note(s, m);
+    pub fn struct_span_err_with_code(&self,
+                                     s: Span,
+                                     msg: &str,
+                                     code: &str)
+                                     -> DiagnosticBuilder<'a> {
+        self.tcx.sess.struct_span_err_with_code(s, msg, code)
     }
 
-    pub fn span_end_note(&self, s: Span, m: &str) {
-        self.tcx.sess.span_end_note(s, m);
+    pub fn span_err_with_code(&self, s: Span, msg: &str, code: &str) {
+        self.tcx.sess.span_err_with_code(s, msg, code);
     }
 
-    pub fn fileline_help(&self, s: Span, m: &str) {
-        self.tcx.sess.fileline_help(s, m);
+    pub fn span_bug(&self, s: Span, m: &str) {
+        self.tcx.sess.span_bug(s, m);
     }
 
     pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
@@ -900,19 +919,19 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
             }
         };
 
-        match cause {
+        let mut err = match cause {
             mc::AliasableOther => {
-                span_err!(
+                struct_span_err!(
                     self.tcx.sess, span, E0385,
-                    "{} in an aliasable location", prefix);
+                    "{} in an aliasable location", prefix)
             }
             mc::AliasableReason::UnaliasableImmutable => {
-                span_err!(
+                struct_span_err!(
                     self.tcx.sess, span, E0386,
-                    "{} in an immutable container", prefix);
+                    "{} in an immutable container", prefix)
             }
             mc::AliasableClosure(id) => {
-                span_err!(
+                let mut err = struct_span_err!(
                     self.tcx.sess, span, E0387,
                     "{} in a captured outer variable in an `Fn` closure", prefix);
                 if let BorrowViolation(euv::ClosureCapture(_)) = kind {
@@ -920,31 +939,32 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                     // happen for nested closures, so we know the enclosing
                     // closure incorrectly accepts an `Fn` while it needs to
                     // be `FnMut`.
-                    span_help!(self.tcx.sess, self.tcx.map.span(id),
+                    span_help!(&mut err, self.tcx.map.span(id),
                            "consider changing this to accept closures that implement `FnMut`");
                 } else {
-                    span_help!(self.tcx.sess, self.tcx.map.span(id),
+                    span_help!(&mut err, self.tcx.map.span(id),
                            "consider changing this closure to take self by mutable reference");
                 }
+                err
             }
             mc::AliasableStatic |
             mc::AliasableStaticMut => {
-                span_err!(
+                struct_span_err!(
                     self.tcx.sess, span, E0388,
-                    "{} in a static location", prefix);
+                    "{} in a static location", prefix)
             }
             mc::AliasableBorrowed => {
-                span_err!(
+                struct_span_err!(
                     self.tcx.sess, span, E0389,
-                    "{} in a `&` reference", prefix);
+                    "{} in a `&` reference", prefix)
             }
-        }
+        };
 
         if is_closure {
-            self.tcx.sess.fileline_help(
-                span,
-                "closures behind references must be called via `&mut`");
+            err.fileline_help(span,
+                              "closures behind references must be called via `&mut`");
         }
+        err.emit();
     }
 
     fn report_out_of_scope_escaping_closure_capture(&self,
@@ -953,34 +973,30 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
     {
         let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt);
 
-        span_err!(
-            self.tcx.sess, err.span, E0373,
-            "closure may outlive the current function, \
-             but it borrows {}, \
-             which is owned by the current function",
-            cmt_path_or_string);
-
-        self.tcx.sess.span_note(
-            capture_span,
-            &format!("{} is borrowed here",
-                     cmt_path_or_string));
-
         let suggestion =
             match self.tcx.sess.codemap().span_to_snippet(err.span) {
                 Ok(string) => format!("move {}", string),
                 Err(_) => format!("move |<args>| <body>")
             };
 
-        self.tcx.sess.span_suggestion(
-            err.span,
-            &format!("to force the closure to take ownership of {} \
-                      (and any other referenced variables), \
-                      use the `move` keyword, as shown:",
-                     cmt_path_or_string),
-            suggestion);
+        struct_span_err!(self.tcx.sess, err.span, E0373,
+                         "closure may outlive the current function, \
+                          but it borrows {}, \
+                          which is owned by the current function",
+                         cmt_path_or_string)
+            .span_note(capture_span,
+                       &format!("{} is borrowed here",
+                                cmt_path_or_string))
+            .span_suggestion(err.span,
+                             &format!("to force the closure to take ownership of {} \
+                                       (and any other referenced variables), \
+                                       use the `move` keyword, as shown:",
+                                      cmt_path_or_string),
+                             suggestion)
+            .emit();
     }
 
-    pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
+    pub fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>) {
         let code = err.code;
         match code {
             err_mutbl => {
@@ -994,7 +1010,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                             _ => unreachable!()
                         };
                         if kind == ty::FnClosureKind {
-                            self.tcx.sess.span_help(
+                            db.span_help(
                                 self.tcx.map.span(upvar_id.closure_expr_id),
                                 "consider changing this closure to take \
                                  self by mutable reference");
@@ -1004,7 +1020,7 @@ 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) {
-                                self.tcx.sess.span_suggestion(
+                                db.span_suggestion(
                                     span,
                                     &format!("to make the {} mutable, use `mut` as shown:",
                                              self.cmt_to_string(&err.cmt)),
@@ -1017,16 +1033,18 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
 
             err_out_of_scope(super_scope, sub_scope) => {
                 self.tcx.note_and_explain_region(
+                    db,
                     "reference must be valid for ",
                     sub_scope,
                     "...");
                 self.tcx.note_and_explain_region(
+                    db,
                     "...but borrowed value is only valid for ",
                     super_scope,
                     "");
                 if let Some(span) = statement_scope_span(self.tcx, super_scope) {
-                    self.tcx.sess.span_help(span,
-                        "consider using a `let` binding to increase its lifetime");
+                    db.span_help(span,
+                                 "consider using a `let` binding to increase its lifetime");
                 }
             }
 
@@ -1038,11 +1056,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                     None => self.cmt_to_string(&*err.cmt),
                 };
                 self.tcx.note_and_explain_region(
+                    db,
                     &format!("{} would have to be valid for ",
                             descr),
                     loan_scope,
                     "...");
                 self.tcx.note_and_explain_region(
+                    db,
                     &format!("...but {} is only valid for ", descr),
                     ptr_scope,
                     "");
@@ -1068,7 +1088,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
             }
 
 
-            LpExtend(ref lp_base, _, LpInterior(InteriorField(fname))) => {
+            LpExtend(ref lp_base, _, LpInterior(_, InteriorField(fname))) => {
                 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
                 match fname {
                     mc::NamedField(fname) => {
@@ -1082,7 +1102,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                 }
             }
 
-            LpExtend(ref lp_base, _, LpInterior(InteriorElement(..))) => {
+            LpExtend(ref lp_base, _, LpInterior(_, InteriorElement(..))) => {
                 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
                 out.push_str("[..]");
             }
@@ -1210,7 +1230,7 @@ impl<'tcx> fmt::Debug for LoanPath<'tcx> {
                 write!(f, "{:?}.*", lp)
             }
 
-            LpExtend(ref lp, _, LpInterior(ref interior)) => {
+            LpExtend(ref lp, _, LpInterior(_, ref interior)) => {
                 write!(f, "{:?}.{:?}", lp, interior)
             }
         }
@@ -1242,7 +1262,7 @@ impl<'tcx> fmt::Display for LoanPath<'tcx> {
                 write!(f, "{}.*", lp)
             }
 
-            LpExtend(ref lp, _, LpInterior(ref interior)) => {
+            LpExtend(ref lp, _, LpInterior(_, ref interior)) => {
                 write!(f, "{}.{:?}", lp, interior)
             }
         }
index 3ac6e7c5d69eb9988229858461bc5378ed888d53..735e618cc732b1f0da607b438e7e58c57de0369c 100644 (file)
@@ -20,6 +20,7 @@ use rustc::middle::dataflow::BitwiseOperator;
 use rustc::middle::dataflow::DataFlowOperator;
 use rustc::middle::dataflow::KillFrom;
 use rustc::middle::expr_use_visitor as euv;
+use rustc::middle::expr_use_visitor::MutateMode;
 use rustc::middle::ty;
 use rustc::util::nodemap::{FnvHashMap, NodeSet};
 
@@ -195,7 +196,7 @@ fn loan_path_is_precise(loan_path: &LoanPath) -> bool {
         LpVar(_) | LpUpvar(_) => {
             true
         }
-        LpExtend(_, _, LpInterior(InteriorKind::InteriorElement(..))) => {
+        LpExtend(_, _, LpInterior(_, InteriorKind::InteriorElement(..))) => {
             // Paths involving element accesses a[i] do not refer to a unique
             // location, as there is no accurate tracking of the indices.
             //
@@ -406,10 +407,10 @@ impl<'tcx> MoveData<'tcx> {
         self.fragments.borrow_mut().add_assignment(path_index);
 
         match mode {
-            euv::Init | euv::JustWrite => {
+            MutateMode::Init | MutateMode::JustWrite => {
                 self.assignee_ids.borrow_mut().insert(assignee_id);
             }
-            euv::WriteAndRead => { }
+            MutateMode::WriteAndRead => { }
         }
 
         let assignment = Assignment {
index a5b313e2dd67ee90098f893ae341c0939c7c85be..1f5824f82e486377596c5827d3ba55f67cf729f4 100644 (file)
@@ -293,6 +293,94 @@ let c = &i; // still ok!
 ```
 "##,
 
+E0507: r##"
+You tried to move out of a value which was borrowed. Erroneous code example:
+
+```
+use std::cell::RefCell;
+
+struct TheDarkKnight;
+
+impl TheDarkKnight {
+    fn nothing_is_true(self) {}
+}
+
+fn main() {
+    let x = RefCell::new(TheDarkKnight);
+
+    x.borrow().nothing_is_true(); // error: cannot move out of borrowed content
+}
+```
+
+Here, the `nothing_is_true` method takes the ownership of `self`. However,
+`self` cannot be moved because `.borrow()` only provides an `&TheDarkKnight`,
+which is a borrow of the content owned by the `RefCell`. To fix this error,
+you have three choices:
+
+* Try to avoid moving the variable.
+* Somehow reclaim the ownership.
+* Implement the `Copy` trait on the type.
+
+Examples:
+
+```
+use std::cell::RefCell;
+
+struct TheDarkKnight;
+
+impl TheDarkKnight {
+    fn nothing_is_true(&self) {} // First case, we don't take ownership
+}
+
+fn main() {
+    let x = RefCell::new(TheDarkKnight);
+
+    x.borrow().nothing_is_true(); // ok!
+}
+```
+
+Or:
+
+```
+use std::cell::RefCell;
+
+struct TheDarkKnight;
+
+impl TheDarkKnight {
+    fn nothing_is_true(self) {}
+}
+
+fn main() {
+    let x = RefCell::new(TheDarkKnight);
+    let x = x.into_inner(); // we get back ownership
+
+    x.nothing_is_true(); // ok!
+}
+```
+
+Or:
+
+```
+use std::cell::RefCell;
+
+#[derive(Clone, Copy)] // we implement the Copy trait
+struct TheDarkKnight;
+
+impl TheDarkKnight {
+    fn nothing_is_true(self) {}
+}
+
+fn main() {
+    let x = RefCell::new(TheDarkKnight);
+
+    x.borrow().nothing_is_true(); // ok!
+}
+```
+
+You can find more information about borrowing in the rust-book:
+http://doc.rust-lang.org/stable/book/references-and-borrowing.html
+"##,
+
 }
 
 register_diagnostics! {
@@ -306,7 +394,6 @@ register_diagnostics! {
     E0504, // cannot move `..` into closure because it is borrowed
     E0505, // cannot move out of `..` because it is borrowed
     E0506, // cannot assign to `..` because it is borrowed
-    E0507, // cannot move out of ..
     E0508, // cannot move out of type `..`, a non-copy fixed-size array
     E0509, // cannot move out of type `..`, which defines the `Drop` trait
 }
index be963f30dc1e86b8f0f2dc79b0640f9689ce3c44..eb63f572649a3fada48619c3de97780c4f7f941d 100644 (file)
@@ -23,7 +23,7 @@ use dot;
 use rustc::middle::cfg::CFGIndex;
 use rustc::middle::dataflow::{DataFlowOperator, DataFlowContext, EntryOrExit};
 use std::rc::Rc;
-use std::borrow::IntoCow;
+use dot::IntoCow;
 
 #[derive(Debug, Copy, Clone)]
 pub enum Variant {
index 27cd9530c9be3404b2972aafa8e89c64ed027832..d730b383a80499d7e52f05a8fe2c17f2a144b19d 100644 (file)
@@ -8,11 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_borrowck"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -25,7 +22,6 @@
 #![feature(rustc_diagnostic_macros)]
 #![feature(rustc_private)]
 #![feature(staged_api)]
-#![feature(into_cow)]
 
 #[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
index 77baa84c0236d020e2d7ab133575d68b2f9cded7..6f4dc28e12221c13c423f67e9b6904dde5c029a8 100644 (file)
@@ -9,21 +9,20 @@
 // except according to those terms.
 
 use std::collections::{HashMap, HashSet};
-use std::collections::hash_state::DefaultState;
 use std::default::Default;
-use std::hash::{Hasher, Hash};
+use std::hash::{Hasher, Hash, BuildHasherDefault};
 
-pub type FnvHashMap<K, V> = HashMap<K, V, DefaultState<FnvHasher>>;
-pub type FnvHashSet<V> = HashSet<V, DefaultState<FnvHasher>>;
+pub type FnvHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FnvHasher>>;
+pub type FnvHashSet<V> = HashSet<V, BuildHasherDefault<FnvHasher>>;
 
 #[allow(non_snake_case)]
 pub fn FnvHashMap<K: Hash + Eq, V>() -> FnvHashMap<K, V> {
-    Default::default()
+    HashMap::default()
 }
 
 #[allow(non_snake_case)]
 pub fn FnvHashSet<V: Hash + Eq>() -> FnvHashSet<V> {
-    Default::default()
+    HashSet::default()
 }
 
 /// A speedy hash algorithm for node ids and def ids. The hashmap in
index 4a3810c822b4dd5bf8062b1882c7ecf122793261..1ea09490aed2f4d05d99f64d555e68db2e45de2f 100644 (file)
@@ -77,16 +77,16 @@ impl<E: Debug> Debug for Edge<E> {
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
 pub struct NodeIndex(pub usize);
 
-#[derive(Copy, Clone, PartialEq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
 pub struct EdgeIndex(pub usize);
 
 pub const INVALID_EDGE_INDEX: EdgeIndex = EdgeIndex(usize::MAX);
 
 // Use a private field here to guarantee no more instances are created:
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub struct Direction { repr: usize }
 
 pub const OUTGOING: Direction = Direction { repr: 0 };
@@ -410,4 +410,12 @@ impl<E> Edge<E> {
     pub fn target(&self) -> NodeIndex {
         self.target
     }
+
+    pub fn source_or_target(&self, direction: Direction) -> NodeIndex {
+        if direction == OUTGOING {
+            self.target
+        } else {
+            self.source
+        }
+    }
 }
index 39b3842791fa636cd85d27ac0cf7e8d6320a73d7..b4a46f7d08458ed892852bd6d99d9cde3c616ef3 100644 (file)
 //!
 //! This API is completely unstable and subject to change.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_data_structures"]
 #![unstable(feature = "rustc_private", issue = "27812")]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
-#![cfg_attr(stage0, staged_api)]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
       html_root_url = "https://doc.rust-lang.org/nightly/")]
 
 #![feature(rustc_private, staged_api)]
-#![feature(hashmap_hasher)]
 
 #![cfg_attr(test, feature(test))]
 
@@ -42,6 +38,8 @@ pub mod snapshot_vec;
 pub mod transitive_relation;
 pub mod unify;
 pub mod fnv;
+pub mod tuple_slice;
+pub mod veccell;
 
 // See comments in src/librustc/lib.rs
 #[doc(hidden)]
diff --git a/src/librustc_data_structures/tuple_slice.rs b/src/librustc_data_structures/tuple_slice.rs
new file mode 100644 (file)
index 0000000..f157d82
--- /dev/null
@@ -0,0 +1,60 @@
+// 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::slice;
+
+/// Allows to view uniform tuples as slices
+pub trait TupleSlice<T> {
+    fn as_slice(&self) -> &[T];
+    fn as_mut_slice(&mut self) -> &mut [T];
+}
+
+macro_rules! impl_tuple_slice {
+    ($tuple_type:ty, $size:expr) => {
+        impl<T> TupleSlice<T> for $tuple_type {
+            fn as_slice(&self) -> &[T] {
+                unsafe {
+                    let ptr = &self.0 as *const T;
+                    slice::from_raw_parts(ptr, $size)
+                }
+            }
+
+            fn as_mut_slice(&mut self) -> &mut [T] {
+                unsafe {
+                    let ptr = &mut self.0 as *mut T;
+                    slice::from_raw_parts_mut(ptr, $size)
+                }
+            }
+        }
+    }
+}
+
+impl_tuple_slice!((T,T), 2);
+impl_tuple_slice!((T,T,T), 3);
+impl_tuple_slice!((T,T,T,T), 4);
+impl_tuple_slice!((T,T,T,T,T), 5);
+impl_tuple_slice!((T,T,T,T,T,T), 6);
+impl_tuple_slice!((T,T,T,T,T,T,T), 7);
+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 t3 = (102i32, 103i32, 104i32);
+    assert_eq!(t3.as_slice(), &[102i32, 103i32, 104i32]);
+
+    let t4 = (105i32, 106i32, 107i32, 108i32);
+    assert_eq!(t4.as_slice(), &[105i32, 106i32, 107i32, 108i32]);
+
+    let t5 = (109i32, 110i32, 111i32, 112i32, 113i32);
+    assert_eq!(t5.as_slice(), &[109i32, 110i32, 111i32, 112i32, 113i32]);
+}
index 4d79b1a64c1911464b0f8cf4901e0fd009227239..c6da70eef750a89f0947b0388bb2ba650f396ee4 100644 (file)
@@ -37,6 +37,16 @@ pub trait UnifyKey : Copy + Clone + Debug + PartialEq {
     fn tag(k: Option<Self>) -> &'static str;
 }
 
+/// This trait is implemented for unify values that can be
+/// combined. This relation should be a monoid.
+pub trait Combine {
+    fn combine(&self, other: &Self) -> Self;
+}
+
+impl Combine for () {
+    fn combine(&self, _other: &()) {}
+}
+
 /// Value of a unification key. We implement Tarjan's union-find
 /// algorithm: when two keys are unified, one of them is converted
 /// into a "redirect" pointing at the other. These redirects form a
@@ -243,8 +253,8 @@ impl<K:UnifyKey> sv::SnapshotVecDelegate for Delegate<K> {
 ///////////////////////////////////////////////////////////////////////////
 // Base union-find algorithm, where we are just making sets
 
-impl<'tcx,K> UnificationTable<K>
-    where K : UnifyKey<Value=()>,
+impl<'tcx,K:UnifyKey> UnificationTable<K>
+    where K::Value: Combine
 {
     pub fn union(&mut self, a_id: K, b_id: K) {
         let node_a = self.get(a_id);
@@ -252,7 +262,8 @@ impl<'tcx,K> UnificationTable<K>
         let a_id = node_a.key();
         let b_id = node_b.key();
         if a_id != b_id {
-            self.unify(node_a, node_b, ());
+            let new_value = node_a.value.combine(&node_b.value);
+            self.unify(node_a, node_b, new_value);
         }
     }
 
@@ -260,6 +271,10 @@ impl<'tcx,K> UnificationTable<K>
         self.get(id).key()
     }
 
+    pub fn find_value(&mut self, id: K) -> K::Value {
+        self.get(id).value
+    }
+
     pub fn unioned(&mut self, a_id: K, b_id: K) -> bool {
         self.find(a_id) == self.find(b_id)
     }
diff --git a/src/librustc_data_structures/veccell/mod.rs b/src/librustc_data_structures/veccell/mod.rs
new file mode 100644 (file)
index 0000000..008642d
--- /dev/null
@@ -0,0 +1,47 @@
+// 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 std::cell::UnsafeCell;
+use std::mem;
+
+pub struct VecCell<T> {
+    data: UnsafeCell<Vec<T>>
+}
+
+impl<T> VecCell<T> {
+    pub fn with_capacity(capacity: usize) -> VecCell<T>{
+        VecCell { data: UnsafeCell::new(Vec::with_capacity(capacity)) }
+    }
+
+    #[inline]
+    pub fn push(&self, data: T) -> usize {
+        // The logic here, and in `swap` below, is that the `push`
+        // method on the vector will not recursively access this
+        // `VecCell`. Therefore, we can temporarily obtain mutable
+        // access, secure in the knowledge that even if aliases exist
+        // -- indeed, even if aliases are reachable from within the
+        // vector -- they will not be used for the duration of this
+        // particular fn call. (Note that we also are relying on the
+        // fact that `VecCell` is not `Sync`.)
+        unsafe {
+            let v = self.data.get();
+            (*v).push(data);
+            (*v).len()
+        }
+    }
+
+    pub fn swap(&self, mut data: Vec<T>) -> Vec<T> {
+        unsafe {
+            let v = self.data.get();
+            mem::swap(&mut *v, &mut data);
+        }
+        data
+    }
+}
index 69e065b3e8f971d1b2f1c5fbc468f3dfa06d8612..01ffd0efbe3143c4373eec24af55d7af9725a05d 100644 (file)
@@ -54,6 +54,7 @@ use syntax::parse::token;
 use syntax::util::node_count::NodeCounter;
 use syntax::visit;
 use syntax;
+use syntax_ext;
 
 pub fn compile_input(sess: Session,
                      cstore: &CStore,
@@ -120,7 +121,7 @@ pub fn compile_input(sess: Session,
         }
 
         let arenas = ty::CtxtArenas::new();
-        let ast_map = make_map(&sess, &mut hir_forest);
+        let hir_map = make_map(&sess, &mut hir_forest);
 
         write_out_deps(&sess, &outputs, &id);
 
@@ -129,9 +130,9 @@ pub fn compile_input(sess: Session,
                                 CompileState::state_after_write_deps(input,
                                                                      &sess,
                                                                      outdir,
-                                                                     &ast_map,
+                                                                     &hir_map,
                                                                      &expanded_crate,
-                                                                     &ast_map.krate(),
+                                                                     &hir_map.krate(),
                                                                      &id[..],
                                                                      &lcx));
 
@@ -143,9 +144,17 @@ pub fn compile_input(sess: Session,
              "early lint checks",
              || lint::check_ast_crate(&sess, &expanded_crate));
 
+        let opt_crate = if sess.opts.debugging_opts.keep_ast ||
+                           sess.opts.debugging_opts.save_analysis {
+            Some(&expanded_crate)
+        } else {
+            drop(expanded_crate);
+            None
+        };
+
         phase_3_run_analysis_passes(&sess,
                                     &cstore,
-                                    ast_map,
+                                    hir_map,
                                     &arenas,
                                     &id,
                                     control.make_glob_map,
@@ -156,7 +165,7 @@ pub fn compile_input(sess: Session,
                                                 CompileState::state_after_analysis(input,
                                                                                    &tcx.sess,
                                                                                    outdir,
-                                                                                   &expanded_crate,
+                                                                                   opt_crate,
                                                                                    tcx.map.krate(),
                                                                                    &analysis,
                                                                                    &mir_map,
@@ -340,7 +349,7 @@ impl<'a, 'ast, 'tcx> CompileState<'a, 'ast, 'tcx> {
     fn state_after_write_deps(input: &'a Input,
                               session: &'a Session,
                               out_dir: &'a Option<PathBuf>,
-                              ast_map: &'a hir_map::Map<'ast>,
+                              hir_map: &'a hir_map::Map<'ast>,
                               krate: &'a ast::Crate,
                               hir_crate: &'a hir::Crate,
                               crate_name: &'a str,
@@ -348,7 +357,7 @@ impl<'a, 'ast, 'tcx> CompileState<'a, 'ast, 'tcx> {
                               -> CompileState<'a, 'ast, 'tcx> {
         CompileState {
             crate_name: Some(crate_name),
-            ast_map: Some(ast_map),
+            ast_map: Some(hir_map),
             krate: Some(krate),
             hir_crate: Some(hir_crate),
             lcx: Some(lcx),
@@ -359,7 +368,7 @@ impl<'a, 'ast, 'tcx> CompileState<'a, 'ast, 'tcx> {
     fn state_after_analysis(input: &'a Input,
                             session: &'a Session,
                             out_dir: &'a Option<PathBuf>,
-                            krate: &'a ast::Crate,
+                            krate: Option<&'a ast::Crate>,
                             hir_crate: &'a hir::Crate,
                             analysis: &'a ty::CrateAnalysis,
                             mir_map: &'a MirMap<'tcx>,
@@ -371,7 +380,7 @@ impl<'a, 'ast, 'tcx> CompileState<'a, 'ast, 'tcx> {
             analysis: Some(analysis),
             mir_map: Some(mir_map),
             tcx: Some(tcx),
-            krate: Some(krate),
+            krate: krate,
             hir_crate: Some(hir_crate),
             lcx: Some(lcx),
             crate_name: Some(crate_name),
@@ -419,7 +428,7 @@ pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
         println!("Pre-expansion node count:  {}", count_nodes(&krate));
     }
 
-    if let Some(ref s) = sess.opts.show_span {
+    if let Some(ref s) = sess.opts.debugging_opts.show_span {
         syntax::show_span::run(sess.diagnostic(), s, &krate);
     }
 
@@ -563,12 +572,15 @@ pub fn phase_2_configure_and_expand(sess: &Session,
             recursion_limit: sess.recursion_limit.get(),
             trace_mac: sess.opts.debugging_opts.trace_macros,
         };
-        let (ret, macro_names) = syntax::ext::expand::expand_crate(&sess.parse_sess,
-                                                                    cfg,
-                                                                    macros,
-                                                                    syntax_exts,
-                                                                    &mut feature_gated_cfgs,
-                                                                    krate);
+        let mut ecx = syntax::ext::base::ExtCtxt::new(&sess.parse_sess,
+                                                      krate.config.clone(),
+                                                      cfg,
+                                                      &mut feature_gated_cfgs);
+        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);
         }
@@ -602,7 +614,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         feature_gated_cfgs.sort();
         feature_gated_cfgs.dedup();
         for cfg in &feature_gated_cfgs {
-            cfg.check_and_emit(sess.diagnostic(), &features);
+            cfg.check_and_emit(sess.diagnostic(), &features, sess.codemap());
         }
     });
 
@@ -620,7 +632,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
 
     time(time_passes,
          "checking for inline asm in case the target doesn't support it",
-         || middle::check_no_asm::check_crate(sess, &krate));
+         || ::rustc_passes::no_asm::check_crate(sess, &krate));
 
     // One final feature gating of the true AST that gets compiled
     // later, to make sure we've got everything (e.g. configuration
@@ -635,6 +647,10 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         sess.abort_if_errors();
     });
 
+    time(time_passes,
+         "const fn bodies and arguments",
+         || ::rustc_passes::const_fn::check_crate(sess, &krate));
+
     if sess.opts.debugging_opts.input_stats {
         println!("Post-expansion node count: {}", count_nodes(&krate));
     }
@@ -666,14 +682,12 @@ pub fn assign_node_ids(sess: &Session, krate: ast::Crate) -> ast::Crate {
 }
 
 pub fn make_map<'ast>(sess: &Session,
-                      forest: &'ast mut front::map::Forest)
-                      -> front::map::Map<'ast> {
-    // Construct the 'ast'-map
-    let map = time(sess.time_passes(),
-                   "indexing hir",
-                   move || front::map::map_crate(forest));
-
-    map
+                      forest: &'ast mut hir_map::Forest)
+                      -> hir_map::Map<'ast> {
+    // Construct the HIR map
+    time(sess.time_passes(),
+         "indexing hir",
+         move || hir_map::map_crate(forest))
 }
 
 /// Run the resolution, typechecking, region checking and other
@@ -681,7 +695,7 @@ pub fn make_map<'ast>(sess: &Session,
 /// structures carrying the results of the analysis.
 pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
                                                cstore: &CStore,
-                                               ast_map: front::map::Map<'tcx>,
+                                               hir_map: hir_map::Map<'tcx>,
                                                arenas: &'tcx ty::CtxtArenas<'tcx>,
                                                name: &str,
                                                make_glob_map: resolve::MakeGlobMap,
@@ -690,15 +704,15 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
     where F: for<'a> FnOnce(&'a ty::ctxt<'tcx>, MirMap<'tcx>, ty::CrateAnalysis) -> R
 {
     let time_passes = sess.time_passes();
-    let krate = ast_map.krate();
+    let krate = hir_map.krate();
 
     time(time_passes,
          "external crate/lib resolution",
-         || LocalCrateReader::new(sess, cstore, &ast_map).read_crates(krate));
+         || LocalCrateReader::new(sess, cstore, &hir_map).read_crates(krate));
 
     let lang_items = time(time_passes,
                           "language item collection",
-                          || middle::lang_items::collect_language_items(&sess, &ast_map));
+                          || middle::lang_items::collect_language_items(&sess, &hir_map));
 
     let resolve::CrateMap {
         def_map,
@@ -709,7 +723,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
         glob_map,
     } = time(time_passes,
              "resolution",
-             || resolve::resolve_crate(sess, &ast_map, make_glob_map));
+             || resolve::resolve_crate(sess, &hir_map, make_glob_map));
 
     let named_region_map = time(time_passes,
                                 "lifetime resolution",
@@ -717,7 +731,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
 
     time(time_passes,
          "looking for entry point",
-         || middle::entry::find_entry_point(sess, &ast_map));
+         || middle::entry::find_entry_point(sess, &hir_map));
 
     sess.plugin_registrar_fn.set(time(time_passes, "looking for plugin registrar", || {
         plugin::build::find_plugin_registrar(sess.diagnostic(), krate)
@@ -733,13 +747,13 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
 
     time(time_passes,
          "static item recursion checking",
-         || middle::check_static_recursion::check_crate(sess, krate, &def_map.borrow(), &ast_map));
+         || middle::check_static_recursion::check_crate(sess, krate, &def_map.borrow(), &hir_map));
 
     ty::ctxt::create_and_enter(sess,
                                arenas,
                                def_map,
                                named_region_map,
-                               ast_map,
+                               hir_map,
                                freevars,
                                region_map,
                                lang_items,
@@ -791,7 +805,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
 
                                    time(time_passes,
                                         "rvalue checking",
-                                        || middle::check_rvalues::check_crate(tcx, krate));
+                                        || middle::check_rvalues::check_crate(tcx));
 
                                    // Avoid overwhelming user with errors if type checking failed.
                                    // I'm not sure how helpful this is, to be honest, but it avoids
@@ -991,8 +1005,9 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<c
                              None
                          }
                          _ => {
-                             session.span_err(a.span, "`crate_type` requires a value");
-                             session.note("for example: `#![crate_type=\"lib\"]`");
+                             session.struct_span_err(a.span, "`crate_type` requires a value")
+                                 .note("for example: `#![crate_type=\"lib\"]`")
+                                 .emit();
                              None
                          }
                      }
index e293a9506873ac460a8a066977c19d7da4bc6390..31151e10a5a55c9d2b8be45aaf6e097047ae20db 100644 (file)
 //!
 //! This API is completely unstable and subject to change.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_driver"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -32,7 +29,6 @@
 #![feature(rustc_private)]
 #![feature(set_stdio)]
 #![feature(staged_api)]
-#![feature(raw)] // remove after snapshot
 
 extern crate arena;
 extern crate flate;
@@ -42,6 +38,7 @@ extern crate libc;
 extern crate rustc;
 extern crate rustc_back;
 extern crate rustc_borrowck;
+extern crate rustc_passes;
 extern crate rustc_front;
 extern crate rustc_lint;
 extern crate rustc_plugin;
@@ -57,8 +54,7 @@ extern crate rustc_llvm as llvm;
 extern crate log;
 #[macro_use]
 extern crate syntax;
-
-pub use syntax::diagnostic;
+extern crate syntax_ext;
 
 use driver::CompileController;
 use pretty::{PpMode, UserIdentifiedItem};
@@ -67,7 +63,7 @@ use rustc_resolve as resolve;
 use rustc_trans::back::link;
 use rustc_trans::save;
 use rustc::session::{config, Session, build_session};
-use rustc::session::config::{Input, PrintRequest, OutputType};
+use rustc::session::config::{Input, PrintRequest, OutputType, ErrorOutputType};
 use rustc::middle::cstore::CrateStore;
 use rustc::lint::Lint;
 use rustc::lint;
@@ -75,7 +71,9 @@ use rustc_metadata::loader;
 use rustc_metadata::cstore::CStore;
 use rustc::util::common::time;
 
+use std::cmp::max;
 use std::cmp::Ordering::Equal;
+use std::default::Default;
 use std::env;
 use std::io::{self, Read, Write};
 use std::iter::repeat;
@@ -90,7 +88,8 @@ use rustc::session::early_error;
 
 use syntax::ast;
 use syntax::parse;
-use syntax::diagnostic::Emitter;
+use syntax::errors;
+use syntax::errors::emitter::Emitter;
 use syntax::diagnostics;
 use syntax::parse::token;
 
@@ -105,24 +104,6 @@ pub mod target_features;
 const BUG_REPORT_URL: &'static str = "https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.\
                                       md#bug-reports";
 
-// SNAP 1af31d4
-// This is a terrible hack. Our stage0 is older than 1.4 and does not
-// support DST coercions, so this function performs the corecion
-// manually. This should go away.
-pub fn cstore_to_cratestore(a: Rc<CStore>) -> Rc<for<'s> CrateStore<'s>>
-{
-    use std::mem;
-    use std::raw::TraitObject;
-    unsafe {
-        let TraitObject { vtable, .. } =
-            mem::transmute::<&for<'s> CrateStore<'s>, TraitObject>(&*a);
-        mem::transmute(TraitObject {
-            data: mem::transmute(a),
-            vtable: vtable
-        })
-    }
-}
-
 pub fn run(args: Vec<String>) -> isize {
     monitor(move || run_compiler(&args, &mut RustcDefaultCalls));
     0
@@ -147,7 +128,7 @@ pub fn run_compiler<'a>(args: &[String], callbacks: &mut CompilerCalls<'a>) {
 
     let descriptions = diagnostics_registry();
 
-    do_or_return!(callbacks.early_callback(&matches, &descriptions, sopts.color));
+    do_or_return!(callbacks.early_callback(&matches, &descriptions, sopts.error_format));
 
     let (odir, ofile) = make_output(&matches);
     let (input, input_file_path) = match make_input(&matches.free) {
@@ -159,12 +140,9 @@ pub fn run_compiler<'a>(args: &[String], callbacks: &mut CompilerCalls<'a>) {
     };
 
     let cstore = Rc::new(CStore::new(token::get_ident_interner()));
-    let cstore_ = cstore_to_cratestore(cstore.clone());
-    let mut sess = build_session(sopts, input_file_path, descriptions, cstore_);
+    let sess = build_session(sopts, input_file_path, descriptions,
+                                 cstore.clone());
     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
-    if sess.unstable_options() {
-        sess.opts.show_span = matches.opt_str("show-span");
-    }
     let mut cfg = config::build_configuration(&sess);
     target_features::add_configuration(&mut cfg, &sess);
 
@@ -238,7 +216,7 @@ pub trait CompilerCalls<'a> {
     fn early_callback(&mut self,
                       _: &getopts::Matches,
                       _: &diagnostics::registry::Registry,
-                      _: diagnostic::ColorConfig)
+                      _: ErrorOutputType)
                       -> Compilation {
         Compilation::Continue
     }
@@ -314,7 +292,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
     fn early_callback(&mut self,
                       matches: &getopts::Matches,
                       descriptions: &diagnostics::registry::Registry,
-                      color: diagnostic::ColorConfig)
+                      output: ErrorOutputType)
                       -> Compilation {
         match matches.opt_str("explain") {
             Some(ref code) => {
@@ -329,7 +307,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
                         print!("{}", &description[1..]);
                     }
                     None => {
-                        early_error(color, &format!("no extended information for {}", code));
+                        early_error(output, &format!("no extended information for {}", code));
                     }
                 }
                 return Compilation::Stop;
@@ -356,17 +334,17 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
                     return None;
                 }
                 let cstore = Rc::new(CStore::new(token::get_ident_interner()));
-                let cstore_ = cstore_to_cratestore(cstore.clone());
-                let sess = build_session(sopts.clone(), None, descriptions.clone(), cstore_);
+                let sess = build_session(sopts.clone(), None, descriptions.clone(),
+                                         cstore.clone());
                 rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
                 let should_stop = RustcDefaultCalls::print_crate_info(&sess, None, odir, ofile);
                 if should_stop == Compilation::Stop {
                     return None;
                 }
-                early_error(sopts.color, "no input filename given");
+                early_error(sopts.error_format, "no input filename given");
             }
             1 => panic!("make_input should have provided valid inputs"),
-            _ => early_error(sopts.color, "multiple input filenames provided"),
+            _ => early_error(sopts.error_format, "multiple input filenames provided"),
         }
 
         None
@@ -408,7 +386,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
     fn build_controller(&mut self, sess: &Session) -> CompileController<'a> {
         let mut control = CompileController::basic();
 
-        if sess.opts.parse_only || sess.opts.show_span.is_some() ||
+        if sess.opts.parse_only || sess.opts.debugging_opts.show_span.is_some() ||
            sess.opts.debugging_opts.ast_json_noexpand {
             control.after_parse.stop = Compilation::Stop;
         }
@@ -456,7 +434,7 @@ impl RustcDefaultCalls {
                     println!("{}", String::from_utf8(v).unwrap());
                 }
                 &Input::Str(_) => {
-                    early_error(sess.opts.color, "cannot list metadata for stdin");
+                    early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
                 }
             }
             return Compilation::Stop;
@@ -483,7 +461,7 @@ impl RustcDefaultCalls {
                 PrintRequest::CrateName => {
                     let input = match input {
                         Some(input) => input,
-                        None => early_error(sess.opts.color, "no input file provided"),
+                        None => early_error(ErrorOutputType::default(), "no input file provided"),
                     };
                     let attrs = attrs.as_ref().unwrap();
                     let t_outputs = driver::build_output_filenames(input, odir, ofile, attrs, sess);
@@ -649,11 +627,13 @@ Available lint options:
 
 
 
-    let max_name_len = plugin_groups.iter()
-                                    .chain(&builtin_groups)
-                                    .map(|&(s, _)| s.chars().count())
-                                    .max()
-                                    .unwrap_or(0);
+    let max_name_len = max("warnings".len(),
+                           plugin_groups.iter()
+                                        .chain(&builtin_groups)
+                                        .map(|&(s, _)| s.chars().count())
+                                        .max()
+                                        .unwrap_or(0));
+
     let padded = |x: &str| {
         let mut s = repeat(" ")
                         .take(max_name_len - x.chars().count())
@@ -665,6 +645,7 @@ Available lint options:
     println!("Lint groups provided by rustc:\n");
     println!("    {}  {}", padded("name"), "sub-lints");
     println!("    {}  {}", padded("----"), "---------");
+    println!("    {}  {}", padded("warnings"), "all built-in lints");
 
     let print_lint_groups = |lints: Vec<(&'static str, Vec<lint::LintId>)>| {
         for (name, to) in lints {
@@ -773,7 +754,7 @@ pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
                             &opt.opt_group.short_name
                         };
                         if m.opt_present(opt_name) {
-                            early_error(diagnostic::Auto,
+                            early_error(ErrorOutputType::default(),
                                         &format!("use of unstable option '{}' requires -Z \
                                                   unstable-options",
                                                  opt_name));
@@ -782,7 +763,7 @@ pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
                 }
                 m
             }
-            Err(f) => early_error(diagnostic::Auto, &f.to_string()),
+            Err(f) => early_error(ErrorOutputType::default(), &f.to_string()),
         }
     }
 
@@ -894,25 +875,25 @@ pub fn monitor<F: FnOnce() + Send + 'static>(f: F) {
         }
         Err(value) => {
             // Thread panicked without emitting a fatal diagnostic
-            if !value.is::<diagnostic::FatalError>() {
-                let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None);
+            if !value.is::<errors::FatalError>() {
+                let mut emitter = errors::emitter::BasicEmitter::stderr(errors::ColorConfig::Auto);
 
                 // a .span_bug or .bug call has already printed what
                 // it wants to print.
-                if !value.is::<diagnostic::ExplicitBug>() {
-                    emitter.emit(None, "unexpected panic", None, diagnostic::Bug);
+                if !value.is::<errors::ExplicitBug>() {
+                    emitter.emit(None, "unexpected panic", None, errors::Level::Bug);
                 }
 
                 let xs = ["the compiler unexpectedly panicked. this is a bug.".to_string(),
                           format!("we would appreciate a bug report: {}", BUG_REPORT_URL)];
                 for note in &xs {
-                    emitter.emit(None, &note[..], None, diagnostic::Note)
+                    emitter.emit(None, &note[..], None, errors::Level::Note)
                 }
                 if let None = env::var_os("RUST_BACKTRACE") {
                     emitter.emit(None,
                                  "run with `RUST_BACKTRACE=1` for a backtrace",
                                  None,
-                                 diagnostic::Note);
+                                 errors::Level::Note);
                 }
 
                 println!("{}", str::from_utf8(&data.lock().unwrap()).unwrap());
index 47b89e3be5d43cfbdf77596501ca09ba8320f79e..ba5ecc22e747487123ecc94ccbed98953653cee5 100644 (file)
@@ -204,6 +204,7 @@ impl PpSourceMode {
                                                         let annotation = TypedAnnotation {
                                                             tcx: tcx,
                                                         };
+                                                        let _ignore = tcx.dep_graph.in_ignore();
                                                         f(&annotation,
                                                           payload,
                                                           &ast_map.forest.krate)
index 2fb23c943c76a3fe327376b2369d50efa3974424..8f3366eacb3645ceade26085e26160d1da174f03 100644 (file)
@@ -10,8 +10,6 @@
 
 //! # Standalone Tests for the Inference Module
 
-use diagnostic;
-use diagnostic::Emitter;
 use driver;
 use rustc_lint;
 use rustc_resolve as resolve;
@@ -23,7 +21,7 @@ use rustc_typeck::middle::resolve_lifetime;
 use rustc_typeck::middle::stability;
 use rustc_typeck::middle::subst;
 use rustc_typeck::middle::subst::Subst;
-use rustc_typeck::middle::ty::{self, Ty, RegionEscape};
+use rustc_typeck::middle::ty::{self, Ty, TypeFoldable};
 use rustc_typeck::middle::ty::relate::TypeRelation;
 use rustc_typeck::middle::infer::{self, TypeOrigin};
 use rustc_typeck::middle::infer::lub::Lub;
@@ -34,9 +32,10 @@ use rustc::front::map as hir_map;
 use rustc::session::{self, config};
 use std::rc::Rc;
 use syntax::{abi, ast};
-use syntax::codemap;
 use syntax::codemap::{Span, CodeMap, DUMMY_SP};
-use syntax::diagnostic::{Level, RenderSpan, Bug, Fatal, Error, Warning, Note, Help};
+use syntax::errors;
+use syntax::errors::emitter::Emitter;
+use syntax::errors::{Level, RenderSpan};
 use syntax::parse::token;
 use syntax::feature_gate::UnstableFeatures;
 
@@ -60,8 +59,8 @@ struct ExpectErrorEmitter {
 
 fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) {
     match lvl {
-        Bug | Fatal | Error => {}
-        Warning | Note | Help => {
+        Level::Bug | Level::Fatal | Level::Error => {}
+        _ => {
             return;
         }
     }
@@ -79,14 +78,14 @@ fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) {
 
 impl Emitter for ExpectErrorEmitter {
     fn emit(&mut self,
-            _cmsp: Option<(&codemap::CodeMap, Span)>,
+            _sp: Option<Span>,
             msg: &str,
             _: Option<&str>,
             lvl: Level) {
         remove_message(self, msg, lvl);
     }
 
-    fn custom_emit(&mut self, _cm: &codemap::CodeMap, _sp: RenderSpan, msg: &str, lvl: Level) {
+    fn custom_emit(&mut self, _sp: RenderSpan, msg: &str, lvl: Level) {
         remove_message(self, msg, lvl);
     }
 }
@@ -105,13 +104,11 @@ fn test_env<F>(source_string: &str,
     let mut options = config::basic_options();
     options.debugging_opts.verbose = true;
     options.unstable_features = UnstableFeatures::Allow;
-    let codemap = CodeMap::new();
-    let diagnostic_handler = diagnostic::Handler::with_emitter(true, emitter);
-    let span_diagnostic_handler = diagnostic::SpanHandler::new(diagnostic_handler, codemap);
+    let diagnostic_handler = errors::Handler::with_emitter(true, false, emitter);
 
     let cstore = Rc::new(CStore::new(token::get_ident_interner()));
-    let sess = session::build_session_(options, None, span_diagnostic_handler,
-                                       cstore.clone());
+    let sess = session::build_session_(options, None, diagnostic_handler,
+                                       Rc::new(CodeMap::new()), cstore.clone());
     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
     let krate_config = Vec::new();
     let input = config::Input::Str(source_string.to_string());
@@ -292,7 +289,6 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
                           -> ty::Region {
         let name = token::intern(name);
         ty::ReEarlyBound(ty::EarlyBoundRegion {
-            def_id: self.infcx.tcx.map.local_def_id(ast::DUMMY_NODE_ID),
             space: space,
             index: index,
             name: name,
@@ -366,13 +362,6 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
         self.infcx.glb(true, trace)
     }
 
-    pub fn make_lub_ty(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> Ty<'tcx> {
-        match self.lub().relate(&t1, &t2) {
-            Ok(t) => t,
-            Err(ref e) => panic!("unexpected error computing LUB: {}", e),
-        }
-    }
-
     /// Checks that `t1 <: t2` is true (this may register additional
     /// region checks).
     pub fn check_sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) {
index 08280e98ae4c7c2eb1c338da925edeac7af395a5..e456b1eadf5d7460dc2d18c5d97a15a5f8493e85 100644 (file)
@@ -17,7 +17,6 @@ use syntax::ast::{MetaWord, MetaList, MetaNameValue};
 use syntax::attr::ThinAttributesExt;
 use hir;
 use syntax::codemap::{respan, Span, Spanned};
-use syntax::owned_slice::OwnedSlice;
 use syntax::ptr::P;
 use syntax::parse::token;
 use syntax::util::move_map::MoveMap;
@@ -35,7 +34,7 @@ pub trait Folder : Sized {
         noop_fold_crate(c, self)
     }
 
-    fn fold_meta_items(&mut self, meta_items: Vec<P<MetaItem>>) -> Vec<P<MetaItem>> {
+    fn fold_meta_items(&mut self, meta_items: HirVec<P<MetaItem>>) -> HirVec<P<MetaItem>> {
         noop_fold_meta_items(meta_items, self)
     }
 
@@ -199,11 +198,11 @@ pub trait Folder : Sized {
         noop_fold_variant_data(vdata, self)
     }
 
-    fn fold_lifetimes(&mut self, lts: Vec<Lifetime>) -> Vec<Lifetime> {
+    fn fold_lifetimes(&mut self, lts: HirVec<Lifetime>) -> HirVec<Lifetime> {
         noop_fold_lifetimes(lts, self)
     }
 
-    fn fold_lifetime_defs(&mut self, lts: Vec<LifetimeDef>) -> Vec<LifetimeDef> {
+    fn fold_lifetime_defs(&mut self, lts: HirVec<LifetimeDef>) -> HirVec<LifetimeDef> {
         noop_fold_lifetime_defs(lts, self)
     }
 
@@ -211,7 +210,7 @@ pub trait Folder : Sized {
         noop_fold_ty_param(tp, self)
     }
 
-    fn fold_ty_params(&mut self, tps: OwnedSlice<TyParam>) -> OwnedSlice<TyParam> {
+    fn fold_ty_params(&mut self, tps: HirVec<TyParam>) -> HirVec<TyParam> {
         noop_fold_ty_params(tps, self)
     }
 
@@ -220,12 +219,12 @@ pub trait Folder : Sized {
     }
 
     fn fold_opt_bounds(&mut self,
-                       b: Option<OwnedSlice<TyParamBound>>)
-                       -> Option<OwnedSlice<TyParamBound>> {
+                       b: Option<TyParamBounds>)
+                       -> Option<TyParamBounds> {
         noop_fold_opt_bounds(b, self)
     }
 
-    fn fold_bounds(&mut self, b: OwnedSlice<TyParamBound>) -> OwnedSlice<TyParamBound> {
+    fn fold_bounds(&mut self, b: TyParamBounds) -> TyParamBounds {
         noop_fold_bounds(b, self)
     }
 
@@ -264,9 +263,9 @@ pub trait Folder : Sized {
     }
 }
 
-pub fn noop_fold_meta_items<T: Folder>(meta_items: Vec<P<MetaItem>>,
+pub fn noop_fold_meta_items<T: Folder>(meta_items: HirVec<P<MetaItem>>,
                                        fld: &mut T)
-                                       -> Vec<P<MetaItem>> {
+                                       -> HirVec<P<MetaItem>> {
     meta_items.move_map(|x| fld.fold_meta_item(x))
 }
 
@@ -305,7 +304,7 @@ pub fn noop_fold_view_path<T: Folder>(view_path: P<ViewPath>, fld: &mut T) -> P<
     })
 }
 
-pub fn fold_attrs<T: Folder>(attrs: Vec<Attribute>, fld: &mut T) -> Vec<Attribute> {
+pub fn fold_attrs<T: Folder>(attrs: HirVec<Attribute>, fld: &mut T) -> HirVec<Attribute> {
     attrs.move_flat_map(|x| fld.fold_attribute(x))
 }
 
@@ -478,7 +477,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, fld)),
+            attrs: attrs.map_thin_attrs(|attrs| fold_attrs(attrs.into(), fld).into()),
         }
     })
 }
@@ -576,9 +575,9 @@ pub fn noop_fold_ty_param<T: Folder>(tp: TyParam, fld: &mut T) -> TyParam {
     }
 }
 
-pub fn noop_fold_ty_params<T: Folder>(tps: OwnedSlice<TyParam>,
+pub fn noop_fold_ty_params<T: Folder>(tps: HirVec<TyParam>,
                                       fld: &mut T)
-                                      -> OwnedSlice<TyParam> {
+                                      -> HirVec<TyParam> {
     tps.move_map(|tp| fld.fold_ty_param(tp))
 }
 
@@ -597,11 +596,13 @@ pub fn noop_fold_lifetime_def<T: Folder>(l: LifetimeDef, fld: &mut T) -> Lifetim
     }
 }
 
-pub fn noop_fold_lifetimes<T: Folder>(lts: Vec<Lifetime>, fld: &mut T) -> Vec<Lifetime> {
+pub fn noop_fold_lifetimes<T: Folder>(lts: HirVec<Lifetime>, fld: &mut T) -> HirVec<Lifetime> {
     lts.move_map(|l| fld.fold_lifetime(l))
 }
 
-pub fn noop_fold_lifetime_defs<T: Folder>(lts: Vec<LifetimeDef>, fld: &mut T) -> Vec<LifetimeDef> {
+pub fn noop_fold_lifetime_defs<T: Folder>(lts: HirVec<LifetimeDef>,
+                                          fld: &mut T)
+                                          -> HirVec<LifetimeDef> {
     lts.move_map(|l| fld.fold_lifetime_def(l))
 }
 
@@ -726,9 +727,9 @@ pub fn noop_fold_mt<T: Folder>(MutTy { ty, mutbl }: MutTy, folder: &mut T) -> Mu
     }
 }
 
-pub fn noop_fold_opt_bounds<T: Folder>(b: Option<OwnedSlice<TyParamBound>>,
+pub fn noop_fold_opt_bounds<T: Folder>(b: Option<TyParamBounds>,
                                        folder: &mut T)
-                                       -> Option<OwnedSlice<TyParamBound>> {
+                                       -> Option<TyParamBounds> {
     b.map(|bounds| folder.fold_bounds(bounds))
 }
 
@@ -1042,6 +1043,9 @@ pub fn noop_fold_expr<T: Folder>(Expr { id, node, span, attrs }: Expr, folder: &
             ExprCast(expr, ty) => {
                 ExprCast(folder.fold_expr(expr), folder.fold_ty(ty))
             }
+            ExprType(expr, ty) => {
+                ExprType(folder.fold_expr(expr), folder.fold_ty(ty))
+            }
             ExprAddrOf(m, ohs) => ExprAddrOf(m, folder.fold_expr(ohs)),
             ExprIf(cond, tr, fl) => {
                 ExprIf(folder.fold_expr(cond),
@@ -1117,7 +1121,14 @@ pub fn noop_fold_expr<T: Folder>(Expr { id, node, span, attrs }: Expr, folder: &
                 expn_id,
             }) => ExprInlineAsm(InlineAsm {
                 inputs: inputs.move_map(|(c, input)| (c, folder.fold_expr(input))),
-                outputs: outputs.move_map(|(c, out, is_rw)| (c, folder.fold_expr(out), is_rw)),
+                outputs: outputs.move_map(|out| {
+                    InlineAsmOutput {
+                        constraint: out.constraint,
+                        expr: folder.fold_expr(out.expr),
+                        is_rw: out.is_rw,
+                        is_indirect: out.is_indirect,
+                    }
+                }),
                 asm: asm,
                 asm_str_style: asm_str_style,
                 clobbers: clobbers,
@@ -1133,7 +1144,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, folder)),
+        attrs: attrs.map_thin_attrs(|attrs| fold_attrs(attrs.into(), folder).into()),
     }
 }
 
index 10e5a091c0d2ad51d5bbf8e616072ef98a02c457..2625e34c820e40f339e3bf47d878f05a9b3afb29 100644 (file)
@@ -40,9 +40,8 @@ use std::collections::BTreeMap;
 use syntax::codemap::{self, Span, Spanned, DUMMY_SP, ExpnId};
 use syntax::abi::Abi;
 use syntax::ast::{Name, NodeId, DUMMY_NODE_ID, TokenTree, AsmDialect};
-use syntax::ast::{Attribute, Lit, StrStyle, FloatTy, IntTy, UintTy, CrateConfig};
+use syntax::ast::{Attribute, Lit, StrStyle, FloatTy, IntTy, UintTy, MetaItem};
 use syntax::attr::ThinAttributes;
-use syntax::owned_slice::OwnedSlice;
 use syntax::parse::token::InternedString;
 use syntax::ptr::P;
 
@@ -53,6 +52,22 @@ use std::fmt;
 use std::hash::{Hash, Hasher};
 use serialize::{Encodable, Decodable, Encoder, Decoder};
 
+/// HIR doesn't commit to a concrete storage type and have its own alias for a vector.
+/// It can be `Vec`, `P<[T]>` or potentially `Box<[T]>`, or some other container with similar
+/// behavior. Unlike AST, HIR is mostly a static structure, so we can use an owned slice instead
+/// of `Vec` to avoid keeping extra capacity.
+pub type HirVec<T> = P<[T]>;
+
+macro_rules! hir_vec {
+    ($elem:expr; $n:expr) => (
+        $crate::hir::HirVec::from(vec![$elem; $n])
+    );
+    ($($x:expr),*) => (
+        $crate::hir::HirVec::from(vec![$($x),*])
+    );
+    ($($x:expr,)*) => (vec![$($x),*])
+}
+
 /// Identifier in HIR
 #[derive(Clone, Copy, Eq)]
 pub struct Ident {
@@ -130,7 +145,7 @@ impl fmt::Debug for Lifetime {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct LifetimeDef {
     pub lifetime: Lifetime,
-    pub bounds: Vec<Lifetime>,
+    pub bounds: HirVec<Lifetime>,
 }
 
 /// A "Path" is essentially Rust's notion of a name; for instance:
@@ -143,7 +158,7 @@ pub struct Path {
     /// module (like paths in an import).
     pub global: bool,
     /// The segments in the path: the things separated by `::`.
-    pub segments: Vec<PathSegment>,
+    pub segments: HirVec<PathSegment>,
 }
 
 impl fmt::Debug for Path {
@@ -192,9 +207,9 @@ pub enum PathParameters {
 impl PathParameters {
     pub fn none() -> PathParameters {
         AngleBracketedParameters(AngleBracketedParameterData {
-            lifetimes: Vec::new(),
-            types: OwnedSlice::empty(),
-            bindings: OwnedSlice::empty(),
+            lifetimes: HirVec::new(),
+            types: HirVec::new(),
+            bindings: HirVec::new(),
         })
     }
 
@@ -224,7 +239,7 @@ impl PathParameters {
 
     /// Returns the types that the user wrote. Note that these do not necessarily map to the type
     /// parameters in the parenthesized case.
-    pub fn types(&self) -> Vec<&P<Ty>> {
+    pub fn types(&self) -> HirVec<&P<Ty>> {
         match *self {
             AngleBracketedParameters(ref data) => {
                 data.types.iter().collect()
@@ -238,24 +253,24 @@ impl PathParameters {
         }
     }
 
-    pub fn lifetimes(&self) -> Vec<&Lifetime> {
+    pub fn lifetimes(&self) -> HirVec<&Lifetime> {
         match *self {
             AngleBracketedParameters(ref data) => {
                 data.lifetimes.iter().collect()
             }
             ParenthesizedParameters(_) => {
-                Vec::new()
+                HirVec::new()
             }
         }
     }
 
-    pub fn bindings(&self) -> Vec<&TypeBinding> {
+    pub fn bindings(&self) -> HirVec<&TypeBinding> {
         match *self {
             AngleBracketedParameters(ref data) => {
                 data.bindings.iter().collect()
             }
             ParenthesizedParameters(_) => {
-                Vec::new()
+                HirVec::new()
             }
         }
     }
@@ -265,12 +280,12 @@ impl PathParameters {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct AngleBracketedParameterData {
     /// The lifetime parameters for this path segment.
-    pub lifetimes: Vec<Lifetime>,
+    pub lifetimes: HirVec<Lifetime>,
     /// The type parameters for this path segment, if present.
-    pub types: OwnedSlice<P<Ty>>,
+    pub types: HirVec<P<Ty>>,
     /// Bindings (equality constraints) on associated types, if present.
     /// E.g., `Foo<A=Bar>`.
-    pub bindings: OwnedSlice<TypeBinding>,
+    pub bindings: HirVec<TypeBinding>,
 }
 
 impl AngleBracketedParameterData {
@@ -286,7 +301,7 @@ pub struct ParenthesizedParameterData {
     pub span: Span,
 
     /// `(A,B)`
-    pub inputs: Vec<P<Ty>>,
+    pub inputs: HirVec<P<Ty>>,
 
     /// `C`
     pub output: Option<P<Ty>>,
@@ -310,7 +325,7 @@ pub enum TraitBoundModifier {
     Maybe,
 }
 
-pub type TyParamBounds = OwnedSlice<TyParamBound>;
+pub type TyParamBounds = HirVec<TyParamBound>;
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct TyParam {
@@ -325,8 +340,8 @@ pub struct TyParam {
 /// of a function, enum, trait, etc.
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Generics {
-    pub lifetimes: Vec<LifetimeDef>,
-    pub ty_params: OwnedSlice<TyParam>,
+    pub lifetimes: HirVec<LifetimeDef>,
+    pub ty_params: HirVec<TyParam>,
     pub where_clause: WhereClause,
 }
 
@@ -346,7 +361,7 @@ impl Generics {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct WhereClause {
     pub id: NodeId,
-    pub predicates: Vec<WherePredicate>,
+    pub predicates: HirVec<WherePredicate>,
 }
 
 /// A single predicate in a `where` clause
@@ -365,11 +380,11 @@ pub enum WherePredicate {
 pub struct WhereBoundPredicate {
     pub span: Span,
     /// Any lifetimes from a `for` binding
-    pub bound_lifetimes: Vec<LifetimeDef>,
+    pub bound_lifetimes: HirVec<LifetimeDef>,
     /// The type being bounded
     pub bounded_ty: P<Ty>,
     /// Trait and lifetime bounds (`Clone+Send+'static`)
-    pub bounds: OwnedSlice<TyParamBound>,
+    pub bounds: TyParamBounds,
 }
 
 /// A lifetime predicate, e.g. `'a: 'b+'c`
@@ -377,7 +392,7 @@ pub struct WhereBoundPredicate {
 pub struct WhereRegionPredicate {
     pub span: Span,
     pub lifetime: Lifetime,
-    pub bounds: Vec<Lifetime>,
+    pub bounds: HirVec<Lifetime>,
 }
 
 /// An equality predicate (unsupported), e.g. `T=int`
@@ -389,13 +404,15 @@ pub struct WhereEqPredicate {
     pub ty: P<Ty>,
 }
 
+pub type CrateConfig = HirVec<P<MetaItem>>;
+
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)]
 pub struct Crate {
     pub module: Mod,
-    pub attrs: Vec<Attribute>,
+    pub attrs: HirVec<Attribute>,
     pub config: CrateConfig,
     pub span: Span,
-    pub exported_macros: Vec<MacroDef>,
+    pub exported_macros: HirVec<MacroDef>,
 
     // NB: We use a BTreeMap here so that `visit_all_items` iterates
     // over the ids in increasing order. In principle it should not
@@ -432,20 +449,20 @@ impl Crate {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct MacroDef {
     pub name: Name,
-    pub attrs: Vec<Attribute>,
+    pub attrs: HirVec<Attribute>,
     pub id: NodeId,
     pub span: Span,
     pub imported_from: Option<Name>,
     pub export: bool,
     pub use_locally: bool,
     pub allow_internal_unstable: bool,
-    pub body: Vec<TokenTree>,
+    pub body: HirVec<TokenTree>,
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Block {
     /// Statements in a block
-    pub stmts: Vec<Stmt>,
+    pub stmts: HirVec<Stmt>,
     /// An expression at the end of the block
     /// without a semicolon, if any
     pub expr: Option<P<Expr>>,
@@ -504,7 +521,7 @@ pub enum Pat_ {
     PatIdent(BindingMode, Spanned<Ident>, Option<P<Pat>>),
 
     /// "None" means a `Variant(..)` pattern where we don't bind the fields to names.
-    PatEnum(Path, Option<Vec<P<Pat>>>),
+    PatEnum(Path, Option<HirVec<P<Pat>>>),
 
     /// An associated const named using the qualified path `<T>::CONST` or
     /// `<T as Trait>::CONST`. Associated consts from inherent impls can be
@@ -514,9 +531,9 @@ pub enum Pat_ {
 
     /// Destructuring of a struct, e.g. `Foo {x, y, ..}`
     /// The `bool` is `true` in the presence of a `..`
-    PatStruct(Path, Vec<Spanned<FieldPat>>, bool),
+    PatStruct(Path, HirVec<Spanned<FieldPat>>, bool),
     /// A tuple pattern `(a, b)`
-    PatTup(Vec<P<Pat>>),
+    PatTup(HirVec<P<Pat>>),
     /// A `box` pattern
     PatBox(P<Pat>),
     /// A reference pattern, e.g. `&mut (a, b)`
@@ -527,7 +544,7 @@ pub enum Pat_ {
     PatRange(P<Expr>, P<Expr>),
     /// `[a, b, ..i, y, z]` is represented as:
     ///     `PatVec(box [a, b], Some(i), box [y, z])`
-    PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
+    PatVec(HirVec<P<Pat>>, Option<P<Pat>>, HirVec<P<Pat>>),
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
@@ -641,8 +658,8 @@ pub enum Decl_ {
 /// represents one arm of a 'match'
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Arm {
-    pub attrs: Vec<Attribute>,
-    pub pats: Vec<P<Pat>>,
+    pub attrs: HirVec<Attribute>,
+    pub pats: HirVec<P<Pat>>,
     pub guard: Option<P<Expr>>,
     pub body: P<Expr>,
 }
@@ -691,12 +708,12 @@ pub enum Expr_ {
     /// A `box x` expression.
     ExprBox(P<Expr>),
     /// An array (`[a, b, c, d]`)
-    ExprVec(Vec<P<Expr>>),
+    ExprVec(HirVec<P<Expr>>),
     /// A function call
     ///
     /// The first field resolves to the function itself,
     /// and the second field is the list of arguments
-    ExprCall(P<Expr>, Vec<P<Expr>>),
+    ExprCall(P<Expr>, HirVec<P<Expr>>),
     /// A method call (`x.foo::<Bar, Baz>(a, b, c, d)`)
     ///
     /// The `Spanned<Name>` is the identifier for the method name.
@@ -709,9 +726,9 @@ pub enum Expr_ {
     ///
     /// Thus, `x.foo::<Bar, Baz>(a, b, c, d)` is represented as
     /// `ExprMethodCall(foo, [Bar, Baz], [x, a, b, c, d])`.
-    ExprMethodCall(Spanned<Name>, Vec<P<Ty>>, Vec<P<Expr>>),
+    ExprMethodCall(Spanned<Name>, HirVec<P<Ty>>, HirVec<P<Expr>>),
     /// A tuple (`(a, b, c ,d)`)
-    ExprTup(Vec<P<Expr>>),
+    ExprTup(HirVec<P<Expr>>),
     /// A binary operation (For example: `a + b`, `a * b`)
     ExprBinary(BinOp, P<Expr>, P<Expr>),
     /// A unary operation (For example: `!x`, `*x`)
@@ -720,6 +737,7 @@ pub enum Expr_ {
     ExprLit(P<Lit>),
     /// A cast (`foo as f64`)
     ExprCast(P<Expr>, P<Ty>),
+    ExprType(P<Expr>, P<Ty>),
     /// An `if` block, with an optional else block
     ///
     /// `if expr { block } else { expr }`
@@ -734,7 +752,7 @@ pub enum Expr_ {
     ExprLoop(P<Block>, Option<Ident>),
     /// 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>, Vec<Arm>, MatchSource),
+    ExprMatch(P<Expr>, HirVec<Arm>, MatchSource),
     /// A closure (for example, `move |a, b, c| {a + b + c}`)
     ExprClosure(CaptureClause, P<FnDecl>, P<Block>),
     /// A block (`{ ... }`)
@@ -761,7 +779,7 @@ pub enum Expr_ {
     /// parameters, e.g. foo::bar::<baz>.
     ///
     /// Optionally "qualified",
-    /// e.g. `<Vec<T> as SomeTrait>::SomeType`.
+    /// e.g. `<HirVec<T> as SomeTrait>::SomeType`.
     ExprPath(Option<QSelf>, Path),
 
     /// A referencing operation (`&a` or `&mut a`)
@@ -780,7 +798,7 @@ pub enum Expr_ {
     ///
     /// For example, `Foo {x: 1, y: 2}`, or
     /// `Foo {x: 1, .. base}`, where `base` is the `Option<Expr>`.
-    ExprStruct(Path, Vec<Field>, Option<P<Expr>>),
+    ExprStruct(Path, HirVec<Field>, Option<P<Expr>>),
 
     /// A vector literal constructed from one repeated element.
     ///
@@ -794,11 +812,11 @@ pub enum Expr_ {
 /// separately. `position` represents the index of the associated
 /// item qualified with this Self type.
 ///
-///     <Vec<T> as a::b::Trait>::AssociatedItem
+///     <HirVec<T> as a::b::Trait>::AssociatedItem
 ///      ^~~~~     ~~~~~~~~~~~~~~^
 ///      ty        position = 3
 ///
-///     <Vec<T>>::AssociatedItem
+///     <HirVec<T>>::AssociatedItem
 ///      ^~~~~    ^
 ///      ty       position = 0
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -851,7 +869,7 @@ pub struct MethodSig {
 pub struct TraitItem {
     pub id: NodeId,
     pub name: Name,
-    pub attrs: Vec<Attribute>,
+    pub attrs: HirVec<Attribute>,
     pub node: TraitItem_,
     pub span: Span,
 }
@@ -868,7 +886,7 @@ pub struct ImplItem {
     pub id: NodeId,
     pub name: Name,
     pub vis: Visibility,
-    pub attrs: Vec<Attribute>,
+    pub attrs: HirVec<Attribute>,
     pub node: ImplItemKind,
     pub span: Span,
 }
@@ -919,7 +937,7 @@ pub enum PrimTy {
 pub struct BareFnTy {
     pub unsafety: Unsafety,
     pub abi: Abi,
-    pub lifetimes: Vec<LifetimeDef>,
+    pub lifetimes: HirVec<LifetimeDef>,
     pub decl: P<FnDecl>,
 }
 
@@ -936,9 +954,9 @@ pub enum Ty_ {
     /// A bare function (e.g. `fn(usize) -> bool`)
     TyBareFn(P<BareFnTy>),
     /// A tuple (`(A, B, C, D,...)`)
-    TyTup(Vec<P<Ty>>),
+    TyTup(HirVec<P<Ty>>),
     /// A path (`module::module::...::Type`), optionally
-    /// "qualified", e.g. `<Vec<T> as SomeTrait>::SomeType`.
+    /// "qualified", e.g. `<HirVec<T> as SomeTrait>::SomeType`.
     ///
     /// Type parameters are stored in the Path itself
     TyPath(Option<QSelf>, Path),
@@ -953,13 +971,21 @@ pub enum Ty_ {
     TyInfer,
 }
 
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+pub struct InlineAsmOutput {
+    pub constraint: InternedString,
+    pub expr: P<Expr>,
+    pub is_rw: bool,
+    pub is_indirect: bool,
+}
+
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct InlineAsm {
     pub asm: InternedString,
     pub asm_str_style: StrStyle,
-    pub outputs: Vec<(InternedString, P<Expr>, bool)>,
-    pub inputs: Vec<(InternedString, P<Expr>)>,
-    pub clobbers: Vec<InternedString>,
+    pub outputs: HirVec<InlineAsmOutput>,
+    pub inputs: HirVec<(InternedString, P<Expr>)>,
+    pub clobbers: HirVec<InternedString>,
     pub volatile: bool,
     pub alignstack: bool,
     pub dialect: AsmDialect,
@@ -1000,7 +1026,7 @@ impl Arg {
 /// Represents the header (not the body) of a function declaration
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct FnDecl {
-    pub inputs: Vec<Arg>,
+    pub inputs: HirVec<Arg>,
     pub output: FunctionRetTy,
     pub variadic: bool,
 }
@@ -1091,24 +1117,24 @@ pub struct Mod {
     /// For `mod foo;`, the inner span ranges from the first token
     /// to the last token in the external file.
     pub inner: Span,
-    pub item_ids: Vec<ItemId>,
+    pub item_ids: HirVec<ItemId>,
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct ForeignMod {
     pub abi: Abi,
-    pub items: Vec<ForeignItem>,
+    pub items: HirVec<ForeignItem>,
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct EnumDef {
-    pub variants: Vec<Variant>,
+    pub variants: HirVec<Variant>,
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Variant_ {
     pub name: Name,
-    pub attrs: Vec<Attribute>,
+    pub attrs: HirVec<Attribute>,
     pub data: VariantData,
     /// Explicit discriminant, eg `Foo = 1`
     pub disr_expr: Option<P<Expr>>,
@@ -1169,7 +1195,7 @@ pub enum ViewPath_ {
     ViewPathGlob(Path),
 
     /// `foo::bar::{a,b,c}`
-    ViewPathList(Path, Vec<PathListItem>),
+    ViewPathList(Path, HirVec<PathListItem>),
 }
 
 /// TraitRef's appear in impls.
@@ -1187,7 +1213,7 @@ pub struct TraitRef {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct PolyTraitRef {
     /// The `'a` in `<'a> Foo<&'a T>`
-    pub bound_lifetimes: Vec<LifetimeDef>,
+    pub bound_lifetimes: HirVec<LifetimeDef>,
 
     /// The `Foo<&'a T>` in `<'a> Foo<&'a T>`
     pub trait_ref: TraitRef,
@@ -1215,7 +1241,7 @@ pub struct StructField_ {
     pub kind: StructFieldKind,
     pub id: NodeId,
     pub ty: P<Ty>,
-    pub attrs: Vec<Attribute>,
+    pub attrs: HirVec<Attribute>,
 }
 
 impl StructField_ {
@@ -1264,8 +1290,8 @@ impl StructFieldKind {
 /// Id of the whole struct lives in `Item`.
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum VariantData {
-    Struct(Vec<StructField>, NodeId),
-    Tuple(Vec<StructField>, NodeId),
+    Struct(HirVec<StructField>, NodeId),
+    Tuple(HirVec<StructField>, NodeId),
     Unit(NodeId),
 }
 
@@ -1320,7 +1346,7 @@ pub struct ItemId {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Item {
     pub name: Name,
-    pub attrs: Vec<Attribute>,
+    pub attrs: HirVec<Attribute>,
     pub id: NodeId,
     pub node: Item_,
     pub vis: Visibility,
@@ -1353,7 +1379,7 @@ pub enum Item_ {
     /// A struct definition, e.g. `struct Foo<A> {x: A}`
     ItemStruct(VariantData, Generics),
     /// Represents a Trait Declaration
-    ItemTrait(Unsafety, Generics, TyParamBounds, Vec<TraitItem>),
+    ItemTrait(Unsafety, Generics, TyParamBounds, HirVec<TraitItem>),
 
     // Default trait implementations
     ///
@@ -1365,7 +1391,7 @@ pub enum Item_ {
              Generics,
              Option<TraitRef>, // (optional) trait this impl implements
              P<Ty>, // self
-             Vec<ImplItem>),
+             HirVec<ImplItem>),
 }
 
 impl Item_ {
@@ -1391,7 +1417,7 @@ impl Item_ {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct ForeignItem {
     pub name: Name,
-    pub attrs: Vec<Attribute>,
+    pub attrs: HirVec<Attribute>,
     pub node: ForeignItem_,
     pub id: NodeId,
     pub span: Span,
index e93f5cbd0fb63c4f74686dc35e151f3288418ca4..03b021cfa6395aed86e8ee1bd5daecccd20e0594 100644 (file)
@@ -10,7 +10,7 @@
 
 //! HIR walker. Each overridden visit method has full control over what
 //! happens with its node, it can do its own traversal of the node's children,
-//! call `visit::walk_*` to apply the default traversal algorithm, or prevent
+//! call `intravisit::walk_*` to apply the default traversal algorithm, or prevent
 //! deeper traversal by doing nothing.
 //!
 //! When visiting the HIR, the contents of nested items are NOT visited
@@ -45,7 +45,7 @@ pub enum FnKind<'a> {
 /// Each method of the Visitor trait is a hook to be potentially
 /// overridden.  Each method's default implementation recursively visits
 /// the substructure of the input via the corresponding `walk` method;
-/// e.g. the `visit_mod` method by default calls `visit::walk_mod`.
+/// e.g. the `visit_mod` method by default calls `intravisit::walk_mod`.
 ///
 /// Note that this visitor does NOT visit nested items by default
 /// (this is why the module is called `intravisit`, to distinguish it
@@ -732,7 +732,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
             visitor.visit_expr(subexpression)
         }
         ExprLit(_) => {}
-        ExprCast(ref subexpression, ref typ) => {
+        ExprCast(ref subexpression, ref typ) | ExprType(ref subexpression, ref typ) => {
             visitor.visit_expr(subexpression);
             visitor.visit_ty(typ)
         }
@@ -803,8 +803,8 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
             for &(_, ref input) in &ia.inputs {
                 visitor.visit_expr(&input)
             }
-            for &(_, ref output, _) in &ia.outputs {
-                visitor.visit_expr(&output)
+            for output in &ia.outputs {
+                visitor.visit_expr(&output.expr)
             }
         }
     }
index 3bfa645afc7d92f9b3f85434484edda66dddcf06..b12c41d060a077cd488d9fbcfa739585539aa6e5 100644 (file)
 //!
 //! This API is completely unstable and subject to change.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_front"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -47,6 +44,7 @@ extern crate rustc_bitflags;
 
 extern crate serialize as rustc_serialize; // used by deriving
 
+#[macro_use]
 pub mod hir;
 pub mod lowering;
 pub mod fold;
index 7dbd6dbf0b8de0491e9c4ab7ab08f8a3bc95d697..81d3367ab00dc97ccccb3b2c2ff564315b51c477 100644 (file)
@@ -70,7 +70,6 @@ use syntax::attr::{ThinAttributes, ThinAttributesExt};
 use syntax::ext::mtwt;
 use syntax::ptr::P;
 use syntax::codemap::{respan, Spanned, Span};
-use syntax::owned_slice::OwnedSlice;
 use syntax::parse::token;
 use syntax::std_inject;
 use syntax::visit::{self, Visitor};
@@ -148,6 +147,10 @@ pub fn lower_ident(_lctx: &LoweringContext, ident: Ident) -> hir::Ident {
     }
 }
 
+pub fn lower_attrs(_lctx: &LoweringContext, attrs: &Vec<Attribute>) -> hir::HirVec<Attribute> {
+    attrs.clone().into()
+}
+
 pub fn lower_view_path(lctx: &LoweringContext, view_path: &ViewPath) -> P<hir::ViewPath> {
     P(Spanned {
         node: match view_path.node {
@@ -187,7 +190,7 @@ pub fn lower_view_path(lctx: &LoweringContext, view_path: &ViewPath) -> P<hir::V
 
 pub fn lower_arm(lctx: &LoweringContext, arm: &Arm) -> hir::Arm {
     hir::Arm {
-        attrs: arm.attrs.clone(),
+        attrs: lower_attrs(lctx, &arm.attrs),
         pats: arm.pats.iter().map(|x| lower_pat(lctx, x)).collect(),
         guard: arm.guard.as_ref().map(|ref x| lower_expr(lctx, x)),
         body: lower_expr(lctx, &arm.body),
@@ -276,7 +279,7 @@ pub fn lower_variant(lctx: &LoweringContext, v: &Variant) -> hir::Variant {
     Spanned {
         node: hir::Variant_ {
             name: v.node.name.name,
-            attrs: v.node.attrs.clone(),
+            attrs: lower_attrs(lctx, &v.node.attrs),
             data: lower_variant_data(lctx, &v.node.data),
             disr_expr: v.node.disr_expr.as_ref().map(|e| lower_expr(lctx, e)),
         },
@@ -316,9 +319,9 @@ pub fn lower_path_parameters(lctx: &LoweringContext,
                              path_parameters: &PathParameters)
                              -> hir::PathParameters {
     match *path_parameters {
-        AngleBracketedParameters(ref data) =>
+        PathParameters::AngleBracketed(ref data) =>
             hir::AngleBracketedParameters(lower_angle_bracketed_parameter_data(lctx, data)),
-        ParenthesizedParameters(ref data) =>
+        PathParameters::Parenthesized(ref data) =>
             hir::ParenthesizedParameters(lower_parenthesized_parameter_data(lctx, data)),
     }
 }
@@ -430,8 +433,8 @@ pub fn lower_ty_param(lctx: &LoweringContext, tp: &TyParam) -> hir::TyParam {
 }
 
 pub fn lower_ty_params(lctx: &LoweringContext,
-                       tps: &OwnedSlice<TyParam>)
-                       -> OwnedSlice<hir::TyParam> {
+                       tps: &P<[TyParam]>)
+                       -> hir::HirVec<hir::TyParam> {
     tps.iter().map(|tp| lower_ty_param(lctx, tp)).collect()
 }
 
@@ -450,13 +453,13 @@ pub fn lower_lifetime_def(lctx: &LoweringContext, l: &LifetimeDef) -> hir::Lifet
     }
 }
 
-pub fn lower_lifetimes(lctx: &LoweringContext, lts: &Vec<Lifetime>) -> Vec<hir::Lifetime> {
+pub fn lower_lifetimes(lctx: &LoweringContext, lts: &Vec<Lifetime>) -> hir::HirVec<hir::Lifetime> {
     lts.iter().map(|l| lower_lifetime(lctx, l)).collect()
 }
 
 pub fn lower_lifetime_defs(lctx: &LoweringContext,
                            lts: &Vec<LifetimeDef>)
-                           -> Vec<hir::LifetimeDef> {
+                           -> hir::HirVec<hir::LifetimeDef> {
     lts.iter().map(|l| lower_lifetime_def(lctx, l)).collect()
 }
 
@@ -561,7 +564,7 @@ pub fn lower_struct_field(lctx: &LoweringContext, f: &StructField) -> hir::Struc
             id: f.node.id,
             kind: lower_struct_field_kind(lctx, &f.node.kind),
             ty: lower_ty(lctx, &f.node.ty),
-            attrs: f.node.attrs.clone(),
+            attrs: lower_attrs(lctx, &f.node.attrs),
         },
         span: f.span,
     }
@@ -583,8 +586,8 @@ pub fn lower_mt(lctx: &LoweringContext, mt: &MutTy) -> hir::MutTy {
 }
 
 pub fn lower_opt_bounds(lctx: &LoweringContext,
-                        b: &Option<OwnedSlice<TyParamBound>>)
-                        -> Option<OwnedSlice<hir::TyParamBound>> {
+                        b: &Option<TyParamBounds>)
+                        -> Option<hir::TyParamBounds> {
     b.as_ref().map(|ref bounds| lower_bounds(lctx, bounds))
 }
 
@@ -674,7 +677,7 @@ pub fn lower_trait_item(lctx: &LoweringContext, i: &TraitItem) -> hir::TraitItem
     hir::TraitItem {
         id: i.id,
         name: i.ident.name,
-        attrs: i.attrs.clone(),
+        attrs: lower_attrs(lctx, &i.attrs),
         node: match i.node {
             ConstTraitItem(ref ty, ref default) => {
                 hir::ConstTraitItem(lower_ty(lctx, ty),
@@ -697,7 +700,7 @@ pub fn lower_impl_item(lctx: &LoweringContext, i: &ImplItem) -> hir::ImplItem {
     hir::ImplItem {
         id: i.id,
         name: i.ident.name,
-        attrs: i.attrs.clone(),
+        attrs: lower_attrs(lctx, &i.attrs),
         vis: lower_visibility(lctx, i.vis),
         node: match i.node {
             ImplItemKind::Const(ref ty, ref expr) => {
@@ -741,25 +744,25 @@ pub fn lower_crate(lctx: &LoweringContext, c: &Crate) -> hir::Crate {
 
     hir::Crate {
         module: lower_mod(lctx, &c.module),
-        attrs: c.attrs.clone(),
-        config: c.config.clone(),
+        attrs: lower_attrs(lctx, &c.attrs),
+        config: c.config.clone().into(),
         span: c.span,
         exported_macros: c.exported_macros.iter().map(|m| lower_macro_def(lctx, m)).collect(),
         items: items,
     }
 }
 
-pub fn lower_macro_def(_lctx: &LoweringContext, m: &MacroDef) -> hir::MacroDef {
+pub fn lower_macro_def(lctx: &LoweringContext, m: &MacroDef) -> hir::MacroDef {
     hir::MacroDef {
         name: m.ident.name,
-        attrs: m.attrs.clone(),
+        attrs: lower_attrs(lctx, &m.attrs),
         id: m.id,
         span: m.span,
         imported_from: m.imported_from.map(|x| x.name),
         export: m.export,
         use_locally: m.use_locally,
         allow_internal_unstable: m.allow_internal_unstable,
-        body: m.body.clone(),
+        body: m.body.clone().into(),
     }
 }
 
@@ -773,7 +776,7 @@ pub fn lower_item(lctx: &LoweringContext, i: &Item) -> hir::Item {
     hir::Item {
         id: i.id,
         name: i.ident.name,
-        attrs: i.attrs.clone(),
+        attrs: lower_attrs(lctx, &i.attrs),
         node: node,
         vis: lower_visibility(lctx, i.vis),
         span: i.span,
@@ -784,7 +787,7 @@ pub fn lower_foreign_item(lctx: &LoweringContext, i: &ForeignItem) -> hir::Forei
     hir::ForeignItem {
         id: i.id,
         name: i.ident.name,
-        attrs: i.attrs.clone(),
+        attrs: lower_attrs(lctx, &i.attrs),
         node: match i.node {
             ForeignItemFn(ref fdec, ref generics) => {
                 hir::ForeignItemFn(lower_fn_decl(lctx, fdec), lower_generics(lctx, generics))
@@ -1021,7 +1024,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                     // let placer = <placer_expr> ;
                     let s1 = {
                         let placer_expr = signal_block_expr(lctx,
-                                                            vec![],
+                                                            hir_vec![],
                                                             placer_expr,
                                                             e.span,
                                                             hir::PopUnstableBlock,
@@ -1032,14 +1035,14 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                     // let mut place = Placer::make_place(placer);
                     let s2 = {
                         let placer = expr_ident(lctx, e.span, placer_ident, None);
-                        let call = make_call(lctx, &make_place, vec![placer]);
+                        let call = make_call(lctx, &make_place, hir_vec![placer]);
                         mk_stmt_let_mut(lctx, place_ident, call)
                     };
 
                     // let p_ptr = Place::pointer(&mut place);
                     let s3 = {
                         let agent = expr_ident(lctx, e.span, place_ident, None);
-                        let args = vec![expr_mut_addr_of(lctx, e.span, agent, None)];
+                        let args = hir_vec![expr_mut_addr_of(lctx, e.span, agent, None)];
                         let call = make_call(lctx, &place_pointer, args);
                         mk_stmt_let(lctx, p_ptr_ident, call)
                     };
@@ -1047,13 +1050,13 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                     // pop_unsafe!(EXPR));
                     let pop_unsafe_expr = {
                         let value_expr = signal_block_expr(lctx,
-                                                           vec![],
+                                                           hir_vec![],
                                                            value_expr,
                                                            e.span,
                                                            hir::PopUnstableBlock,
                                                            None);
                         signal_block_expr(lctx,
-                                          vec![],
+                                          hir_vec![],
                                           value_expr,
                                           e.span,
                                           hir::PopUnsafeBlock(hir::CompilerGenerated), None)
@@ -1067,21 +1070,21 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                         let ptr = expr_ident(lctx, e.span, p_ptr_ident, None);
                         let call_move_val_init =
                             hir::StmtSemi(
-                                make_call(lctx, &move_val_init, vec![ptr, pop_unsafe_expr]),
+                                make_call(lctx, &move_val_init, hir_vec![ptr, pop_unsafe_expr]),
                                 lctx.next_id());
                         let call_move_val_init = respan(e.span, call_move_val_init);
 
                         let place = expr_ident(lctx, e.span, place_ident, None);
-                        let call = make_call(lctx, &inplace_finalize, vec![place]);
+                        let call = make_call(lctx, &inplace_finalize, hir_vec![place]);
                         signal_block_expr(lctx,
-                                          vec![call_move_val_init],
+                                          hir_vec![call_move_val_init],
                                           call,
                                           e.span,
                                           hir::PushUnsafeBlock(hir::CompilerGenerated), None)
                     };
 
                     signal_block_expr(lctx,
-                                      vec![s1, s2, s3],
+                                      hir_vec![s1, s2, s3],
                                       expr,
                                       e.span,
                                       hir::PushUnstableBlock,
@@ -1125,6 +1128,10 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                 let expr = lower_expr(lctx, expr);
                 hir::ExprCast(expr, lower_ty(lctx, ty))
             }
+            ExprType(ref expr, ref ty) => {
+                let expr = lower_expr(lctx, expr);
+                hir::ExprType(expr, lower_ty(lctx, ty))
+            }
             ExprAddrOf(m, ref ohs) => {
                 let m = lower_mutability(lctx, m);
                 let ohs = lower_expr(lctx, ohs);
@@ -1142,7 +1149,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                                 let els = lower_expr(lctx, els);
                                 let id = lctx.next_id();
                                 let blk = P(hir::Block {
-                                    stmts: vec![],
+                                    stmts: hir_vec![],
                                     expr: Some(els),
                                     id: id,
                                     rules: hir::DefaultBlock,
@@ -1228,13 +1235,18 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                               .map(|&(ref c, ref input)| (c.clone(), lower_expr(lctx, input)))
                               .collect(),
                 outputs: outputs.iter()
-                                .map(|&(ref c, ref out, ref is_rw)| {
-                                    (c.clone(), lower_expr(lctx, out), *is_rw)
+                                .map(|out| {
+                                    hir::InlineAsmOutput {
+                                        constraint: out.constraint.clone(),
+                                        expr: lower_expr(lctx, &out.expr),
+                                        is_rw: out.is_rw,
+                                        is_indirect: out.is_indirect,
+                                    }
                                 })
                                 .collect(),
                 asm: asm.clone(),
                 asm_str_style: asm_str_style,
-                clobbers: clobbers.clone(),
+                clobbers: clobbers.clone().into(),
                 volatile: volatile,
                 alignstack: alignstack,
                 dialect: dialect,
@@ -1271,7 +1283,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                     let pat_arm = {
                         let body = lower_block(lctx, body);
                         let body_expr = expr_block(lctx, body, None);
-                        arm(vec![lower_pat(lctx, pat)], body_expr)
+                        arm(hir_vec![lower_pat(lctx, pat)], body_expr)
                     };
 
                     // `[_ if <else_opt_if_cond> => <else_opt_if_body>,]`
@@ -1286,8 +1298,8 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                                         hir::ExprIf(cond, then, else_opt) => {
                                             let pat_under = pat_wild(lctx, e.span);
                                             arms.push(hir::Arm {
-                                                attrs: vec![],
-                                                pats: vec![pat_under],
+                                                attrs: hir_vec![],
+                                                pats: hir_vec![pat_under],
                                                 guard: Some(cond),
                                                 body: expr_block(lctx, then, None),
                                             });
@@ -1321,8 +1333,8 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                         let pat_under = pat_wild(lctx, e.span);
                         let else_expr =
                             else_opt.unwrap_or_else(
-                                || expr_tuple(lctx, e.span, vec![], None));
-                        arm(vec![pat_under], else_expr)
+                                || expr_tuple(lctx, e.span, hir_vec![], None));
+                        arm(hir_vec![pat_under], else_expr)
                     };
 
                     let mut arms = Vec::with_capacity(else_if_arms.len() + 2);
@@ -1335,7 +1347,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                     expr(lctx,
                          e.span,
                          hir::ExprMatch(sub_expr,
-                                        arms,
+                                        arms.into(),
                                         hir::MatchSource::IfLetDesugar {
                                             contains_else_clause: contains_else_clause,
                                         }),
@@ -1360,18 +1372,18 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                     let pat_arm = {
                         let body = lower_block(lctx, body);
                         let body_expr = expr_block(lctx, body, None);
-                        arm(vec![lower_pat(lctx, pat)], body_expr)
+                        arm(hir_vec![lower_pat(lctx, pat)], body_expr)
                     };
 
                     // `_ => break`
                     let break_arm = {
                         let pat_under = pat_wild(lctx, e.span);
                         let break_expr = expr_break(lctx, e.span, None);
-                        arm(vec![pat_under], break_expr)
+                        arm(hir_vec![pat_under], break_expr)
                     };
 
                     // `match <sub_expr> { ... }`
-                    let arms = vec![pat_arm, break_arm];
+                    let arms = hir_vec![pat_arm, break_arm];
                     let sub_expr = lower_expr(lctx, sub_expr);
                     let match_expr = expr(lctx,
                                           e.span,
@@ -1427,14 +1439,14 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                         let pat = lower_pat(lctx, pat);
                         let some_pat = pat_some(lctx, e.span, pat);
 
-                        arm(vec![some_pat], body_expr)
+                        arm(hir_vec![some_pat], body_expr)
                     };
 
                     // `::std::option::Option::None => break`
                     let break_arm = {
                         let break_expr = expr_break(lctx, e.span, None);
 
-                        arm(vec![pat_none(lctx, e.span)], break_expr)
+                        arm(hir_vec![pat_none(lctx, e.span)], break_expr)
                     };
 
                     // `match ::std::iter::Iterator::next(&mut iter) { ... }`
@@ -1450,9 +1462,9 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                         let next_expr = expr_call(lctx,
                                                   e.span,
                                                   next_path,
-                                                  vec![ref_mut_iter],
+                                                  hir_vec![ref_mut_iter],
                                                   None);
-                        let arms = vec![pat_arm, break_arm];
+                        let arms = hir_vec![pat_arm, break_arm];
 
                         expr(lctx,
                              e.span,
@@ -1472,7 +1484,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                                                               e.span,
                                                               iter,
                                                               hir::BindByValue(hir::MutMutable));
-                        arm(vec![iter_pat], loop_expr)
+                        arm(hir_vec![iter_pat], loop_expr)
                     };
 
                     // `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
@@ -1484,21 +1496,22 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                         };
 
                         let into_iter = expr_path(lctx, into_iter_path, None);
-                        expr_call(lctx, e.span, into_iter, vec![head], None)
+                        expr_call(lctx, e.span, into_iter, hir_vec![head], None)
                     };
 
                     let match_expr = expr_match(lctx,
                                                 e.span,
                                                 into_iter_expr,
-                                                vec![iter_arm],
+                                                hir_vec![iter_arm],
                                                 hir::MatchSource::ForLoopDesugar,
                                                 None);
 
-                    // `{ let result = ...; result }`
-                    let result_ident = lctx.str_to_ident("result");
+                    // `{ let _result = ...; _result }`
+                    // underscore prevents an unused_variables lint if the head diverges
+                    let result_ident = lctx.str_to_ident("_result");
                     let let_stmt = stmt_let(lctx, e.span, false, result_ident, match_expr, None);
                     let result = expr_ident(lctx, e.span, result_ident, None);
-                    let block = block_all(lctx, e.span, vec![let_stmt], Some(result));
+                    let block = block_all(lctx, e.span, hir_vec![let_stmt], Some(result));
                     // add the attributes to the outer returned expr node
                     expr_block(lctx, block, e.attrs.clone())
                 });
@@ -1558,8 +1571,8 @@ pub fn lower_block_check_mode(lctx: &LoweringContext, b: &BlockCheckMode) -> hir
 
 pub fn lower_binding_mode(lctx: &LoweringContext, b: &BindingMode) -> hir::BindingMode {
     match *b {
-        BindByRef(m) => hir::BindByRef(lower_mutability(lctx, m)),
-        BindByValue(m) => hir::BindByValue(lower_mutability(lctx, m)),
+        BindingMode::ByRef(m) => hir::BindByRef(lower_mutability(lctx, m)),
+        BindingMode::ByValue(m) => hir::BindByValue(lower_mutability(lctx, m)),
     }
 }
 
@@ -1597,9 +1610,9 @@ pub fn lower_trait_bound_modifier(_lctx: &LoweringContext,
 
 // Helper methods for building HIR.
 
-fn arm(pats: Vec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm {
+fn arm(pats: hir::HirVec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm {
     hir::Arm {
-        attrs: vec![],
+        attrs: hir_vec![],
         pats: pats,
         guard: None,
         body: expr,
@@ -1614,7 +1627,7 @@ fn expr_break(lctx: &LoweringContext, span: Span,
 fn expr_call(lctx: &LoweringContext,
              span: Span,
              e: P<hir::Expr>,
-             args: Vec<P<hir::Expr>>,
+             args: hir::HirVec<P<hir::Expr>>,
              attrs: ThinAttributes)
              -> P<hir::Expr> {
     expr(lctx, span, hir::ExprCall(e, args), attrs)
@@ -1638,7 +1651,7 @@ fn expr_path(lctx: &LoweringContext, path: hir::Path,
 fn expr_match(lctx: &LoweringContext,
               span: Span,
               arg: P<hir::Expr>,
-              arms: Vec<hir::Arm>,
+              arms: hir::HirVec<hir::Arm>,
               source: hir::MatchSource,
               attrs: ThinAttributes)
               -> P<hir::Expr> {
@@ -1650,7 +1663,7 @@ fn expr_block(lctx: &LoweringContext, b: P<hir::Block>,
     expr(lctx, b.span, hir::ExprBlock(b), attrs)
 }
 
-fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: Vec<P<hir::Expr>>,
+fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: hir::HirVec<P<hir::Expr>>,
               attrs: ThinAttributes) -> P<hir::Expr> {
     expr(lctx, sp, hir::ExprTup(exprs), attrs)
 }
@@ -1690,12 +1703,12 @@ fn stmt_let(lctx: &LoweringContext,
 }
 
 fn block_expr(lctx: &LoweringContext, expr: P<hir::Expr>) -> P<hir::Block> {
-    block_all(lctx, expr.span, Vec::new(), Some(expr))
+    block_all(lctx, expr.span, hir::HirVec::new(), Some(expr))
 }
 
 fn block_all(lctx: &LoweringContext,
              span: Span,
-             stmts: Vec<hir::Stmt>,
+             stmts: hir::HirVec<hir::Stmt>,
              expr: Option<P<hir::Expr>>)
              -> P<hir::Block> {
     P(hir::Block {
@@ -1710,19 +1723,19 @@ fn block_all(lctx: &LoweringContext,
 fn pat_some(lctx: &LoweringContext, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
     let some = std_path(lctx, &["option", "Option", "Some"]);
     let path = path_global(span, some);
-    pat_enum(lctx, span, path, vec![pat])
+    pat_enum(lctx, span, path, hir_vec![pat])
 }
 
 fn pat_none(lctx: &LoweringContext, span: Span) -> P<hir::Pat> {
     let none = std_path(lctx, &["option", "Option", "None"]);
     let path = path_global(span, none);
-    pat_enum(lctx, span, path, vec![])
+    pat_enum(lctx, span, path, hir_vec![])
 }
 
 fn pat_enum(lctx: &LoweringContext,
             span: Span,
             path: hir::Path,
-            subpats: Vec<P<hir::Pat>>)
+            subpats: hir::HirVec<P<hir::Pat>>)
             -> P<hir::Pat> {
     let pt = hir::PatEnum(path, Some(subpats));
     pat(lctx, span, pt)
@@ -1763,19 +1776,19 @@ fn path_ident(span: Span, id: hir::Ident) -> hir::Path {
 }
 
 fn path(span: Span, strs: Vec<hir::Ident>) -> hir::Path {
-    path_all(span, false, strs, Vec::new(), Vec::new(), Vec::new())
+    path_all(span, false, strs, hir::HirVec::new(), hir::HirVec::new(), hir::HirVec::new())
 }
 
 fn path_global(span: Span, strs: Vec<hir::Ident>) -> hir::Path {
-    path_all(span, true, strs, Vec::new(), Vec::new(), Vec::new())
+    path_all(span, true, strs, hir::HirVec::new(), hir::HirVec::new(), hir::HirVec::new())
 }
 
 fn path_all(sp: Span,
             global: bool,
             mut idents: Vec<hir::Ident>,
-            lifetimes: Vec<hir::Lifetime>,
-            types: Vec<P<hir::Ty>>,
-            bindings: Vec<hir::TypeBinding>)
+            lifetimes: hir::HirVec<hir::Lifetime>,
+            types: hir::HirVec<P<hir::Ty>>,
+            bindings: hir::HirVec<hir::TypeBinding>)
             -> hir::Path {
     let last_identifier = idents.pop().unwrap();
     let mut segments: Vec<hir::PathSegment> = idents.into_iter()
@@ -1790,14 +1803,14 @@ fn path_all(sp: Span,
         identifier: last_identifier,
         parameters: hir::AngleBracketedParameters(hir::AngleBracketedParameterData {
             lifetimes: lifetimes,
-            types: OwnedSlice::from_vec(types),
-            bindings: OwnedSlice::from_vec(bindings),
+            types: types,
+            bindings: bindings,
         }),
     });
     hir::Path {
         span: sp,
         global: global,
-        segments: segments,
+        segments: segments.into(),
     }
 }
 
@@ -1818,7 +1831,7 @@ fn core_path(lctx: &LoweringContext, span: Span, components: &[&str]) -> hir::Pa
 }
 
 fn signal_block_expr(lctx: &LoweringContext,
-                     stmts: Vec<hir::Stmt>,
+                     stmts: hir::HirVec<hir::Stmt>,
                      expr: P<hir::Expr>,
                      span: Span,
                      rule: hir::BlockCheckMode,
index 721d60999e12de5c237ded9d43fad36d2c5879ef..c5ce76c1b6e69c20d055f82fed7a67dbbc7204b0 100644 (file)
@@ -12,9 +12,8 @@ pub use self::AnnNode::*;
 
 use syntax::abi;
 use syntax::ast;
-use syntax::owned_slice::OwnedSlice;
 use syntax::codemap::{self, CodeMap, BytePos, Spanned};
-use syntax::diagnostic;
+use syntax::errors;
 use syntax::parse::token::{self, BinOpToken};
 use syntax::parse::lexer::comments;
 use syntax::parse;
@@ -121,7 +120,7 @@ pub const default_columns: usize = 78;
 /// it can scan the input text for comments and literals to
 /// copy forward.
 pub fn print_crate<'a>(cm: &'a CodeMap,
-                       span_diagnostic: &diagnostic::SpanHandler,
+                       span_diagnostic: &errors::Handler,
                        krate: &hir::Crate,
                        filename: String,
                        input: &mut Read,
@@ -142,7 +141,7 @@ pub fn print_crate<'a>(cm: &'a CodeMap,
 
 impl<'a> State<'a> {
     pub fn new_from_input(cm: &'a CodeMap,
-                          span_diagnostic: &diagnostic::SpanHandler,
+                          span_diagnostic: &errors::Handler,
                           filename: String,
                           input: &mut Read,
                           out: Box<Write + 'a>,
@@ -336,7 +335,8 @@ fn needs_parentheses(expr: &hir::Expr) -> bool {
         hir::ExprBinary(..) |
         hir::ExprClosure(..) |
         hir::ExprAssignOp(..) |
-        hir::ExprCast(..) => true,
+        hir::ExprCast(..) |
+        hir::ExprType(..) => true,
         _ => false,
     }
 }
@@ -519,10 +519,10 @@ impl<'a> State<'a> {
             hir::TyBareFn(ref f) => {
                 let generics = hir::Generics {
                     lifetimes: f.lifetimes.clone(),
-                    ty_params: OwnedSlice::empty(),
+                    ty_params: hir::HirVec::new(),
                     where_clause: hir::WhereClause {
                         id: ast::DUMMY_NODE_ID,
-                        predicates: Vec::new(),
+                        predicates: hir::HirVec::new(),
                     },
                 };
                 try!(self.print_ty_fn(f.abi, f.unsafety, &*f.decl, None, &generics, None));
@@ -1354,6 +1354,11 @@ impl<'a> State<'a> {
                 try!(self.word_space("as"));
                 try!(self.print_type(&**ty));
             }
+            hir::ExprType(ref expr, ref ty) => {
+                try!(self.print_expr(&**expr));
+                try!(self.word_space(":"));
+                try!(self.print_type(&**ty));
+            }
             hir::ExprIf(ref test, ref blk, ref elseopt) => {
                 try!(self.print_if(&**test, &**blk, elseopt.as_ref().map(|e| &**e)));
             }
@@ -1502,15 +1507,15 @@ impl<'a> State<'a> {
                 try!(self.print_string(&a.asm, a.asm_str_style));
                 try!(self.word_space(":"));
 
-                try!(self.commasep(Inconsistent, &a.outputs, |s, &(ref co, ref o, is_rw)| {
-                    match co.slice_shift_char() {
-                        Some(('=', operand)) if is_rw => {
+                try!(self.commasep(Inconsistent, &a.outputs, |s, out| {
+                    match out.constraint.slice_shift_char() {
+                        Some(('=', operand)) if out.is_rw => {
                             try!(s.print_string(&format!("+{}", operand), ast::CookedStr))
                         }
-                        _ => try!(s.print_string(&co, ast::CookedStr)),
+                        _ => try!(s.print_string(&out.constraint, ast::CookedStr)),
                     }
                     try!(s.popen());
-                    try!(s.print_expr(&**o));
+                    try!(s.print_expr(&*out.expr));
                     try!(s.pclose());
                     Ok(())
                 }));
@@ -2257,11 +2262,11 @@ impl<'a> State<'a> {
             try!(self.print_generics(generics));
         }
         let generics = hir::Generics {
-            lifetimes: Vec::new(),
-            ty_params: OwnedSlice::empty(),
+            lifetimes: hir::HirVec::new(),
+            ty_params: hir::HirVec::new(),
             where_clause: hir::WhereClause {
                 id: ast::DUMMY_NODE_ID,
-                predicates: Vec::new(),
+                predicates: hir::HirVec::new(),
             },
         };
         try!(self.print_fn(decl,
index 4dc08f7a0485acdc5c7dbe3fc0d3167efcdf36bd..57ffefd3be4354134a4e46f8e58a52771d41bb52 100644 (file)
@@ -15,7 +15,6 @@ use syntax::ast_util;
 use syntax::ast::{Name, NodeId, DUMMY_NODE_ID};
 use syntax::codemap::Span;
 use syntax::ptr::P;
-use syntax::owned_slice::OwnedSlice;
 
 pub fn walk_pat<F>(pat: &Pat, mut it: F) -> bool
     where F: FnMut(&Pat) -> bool
@@ -335,11 +334,11 @@ pub fn is_path(e: P<Expr>) -> bool {
 
 pub fn empty_generics() -> Generics {
     Generics {
-        lifetimes: Vec::new(),
-        ty_params: OwnedSlice::empty(),
+        lifetimes: HirVec::new(),
+        ty_params: HirVec::new(),
         where_clause: WhereClause {
             id: DUMMY_NODE_ID,
-            predicates: Vec::new(),
+            predicates: HirVec::new(),
         },
     }
 }
@@ -350,13 +349,13 @@ pub fn ident_to_path(s: Span, ident: Ident) -> Path {
     hir::Path {
         span: s,
         global: false,
-        segments: vec!(hir::PathSegment {
+        segments: hir_vec![hir::PathSegment {
             identifier: ident,
             parameters: hir::AngleBracketedParameters(hir::AngleBracketedParameterData {
-                lifetimes: Vec::new(),
-                types: OwnedSlice::empty(),
-                bindings: OwnedSlice::empty(),
+                lifetimes: HirVec::new(),
+                types: HirVec::new(),
+                bindings: HirVec::new(),
             }),
-        }),
+        }],
     }
 }
index 90d9bcfffee7279d66094ae5642fe3626ad0f1f4..09e6f454fb4f51c5ffaccba9fdccff42e83da4b7 100644 (file)
@@ -575,74 +575,71 @@ impl LateLintPass for MissingDebugImplementations {
 declare_lint! {
     DEPRECATED,
     Warn,
-    "detects use of #[rustc_deprecated] items"
+    "detects use of deprecated items"
 }
 
-/// Checks for use of items with `#[rustc_deprecated]` attributes
+/// Checks for use of items with `#[deprecated]` or `#[rustc_deprecated]` attributes
 #[derive(Copy, Clone)]
-pub struct Stability;
+pub struct Deprecated;
 
-impl Stability {
-    fn lint(&self, cx: &LateContext, _id: DefId,
-            span: Span, stability: &Option<&attr::Stability>) {
+impl Deprecated {
+    fn lint(&self, cx: &LateContext, _id: DefId, span: Span,
+            stability: &Option<&attr::Stability>, deprecation: &Option<attr::Deprecation>) {
         // Deprecated attributes apply in-crate and cross-crate.
-        let (lint, label) = match *stability {
-            Some(&attr::Stability { depr: Some(_), .. }) =>
-                (DEPRECATED, "deprecated"),
-            _ => return
-        };
-
-        output(cx, span, stability, lint, label);
+        if let Some(&attr::Stability{rustc_depr: Some(attr::RustcDeprecation{ref reason, ..}), ..})
+                = *stability {
+            output(cx, DEPRECATED, span, Some(&reason))
+        } else if let Some(attr::Deprecation{ref note, ..}) = *deprecation {
+            output(cx, DEPRECATED, span, note.as_ref().map(|x| &**x))
+        }
 
-        fn output(cx: &LateContext, span: Span, stability: &Option<&attr::Stability>,
-                  lint: &'static Lint, label: &'static str) {
-            let msg = match *stability {
-                Some(&attr::Stability {depr: Some(attr::Deprecation {ref reason, ..}), ..}) => {
-                    format!("use of {} item: {}", label, reason)
-                }
-                _ => format!("use of {} item", label)
+        fn output(cx: &LateContext, lint: &'static Lint, span: Span, note: Option<&str>) {
+            let msg = if let Some(note) = note {
+                format!("use of deprecated item: {}", note)
+            } else {
+                format!("use of deprecated item")
             };
 
-            cx.span_lint(lint, span, &msg[..]);
+            cx.span_lint(lint, span, &msg);
         }
     }
 }
 
-impl LintPass for Stability {
+impl LintPass for Deprecated {
     fn get_lints(&self) -> LintArray {
         lint_array!(DEPRECATED)
     }
 }
 
-impl LateLintPass for Stability {
+impl LateLintPass for Deprecated {
     fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
         stability::check_item(cx.tcx, item, false,
-                              &mut |id, sp, stab|
-                                self.lint(cx, id, sp, &stab));
+                              &mut |id, sp, stab, depr|
+                                self.lint(cx, id, sp, &stab, &depr));
     }
 
     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
         stability::check_expr(cx.tcx, e,
-                              &mut |id, sp, stab|
-                                self.lint(cx, id, sp, &stab));
+                              &mut |id, sp, stab, depr|
+                                self.lint(cx, id, sp, &stab, &depr));
     }
 
     fn check_path(&mut self, cx: &LateContext, path: &hir::Path, id: ast::NodeId) {
         stability::check_path(cx.tcx, path, id,
-                              &mut |id, sp, stab|
-                                self.lint(cx, id, sp, &stab));
+                              &mut |id, sp, stab, depr|
+                                self.lint(cx, id, sp, &stab, &depr));
     }
 
     fn check_path_list_item(&mut self, cx: &LateContext, item: &hir::PathListItem) {
         stability::check_path_list_item(cx.tcx, item,
-                                         &mut |id, sp, stab|
-                                           self.lint(cx, id, sp, &stab));
+                                         &mut |id, sp, stab, depr|
+                                           self.lint(cx, id, sp, &stab, &depr));
     }
 
     fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
         stability::check_pat(cx.tcx, pat,
-                             &mut |id, sp, stab|
-                                self.lint(cx, id, sp, &stab));
+                             &mut |id, sp, stab, depr|
+                                self.lint(cx, id, sp, &stab, &depr));
     }
 }
 
@@ -755,19 +752,19 @@ impl LateLintPass for UnconditionalRecursion {
         // no break */ }`) shouldn't be linted unless it actually
         // recurs.
         if !reached_exit_without_self_call && !self_call_spans.is_empty() {
-            cx.span_lint(UNCONDITIONAL_RECURSION, sp,
-                         "function cannot return without recurring");
+            let mut db = cx.struct_span_lint(UNCONDITIONAL_RECURSION, sp,
+                                             "function cannot return without recurring");
 
             // FIXME #19668: these could be span_lint_note's instead of this manual guard.
             if cx.current_level(UNCONDITIONAL_RECURSION) != Level::Allow {
-                let sess = cx.sess();
                 // offer some help to the programmer.
                 for call in &self_call_spans {
-                    sess.span_note(*call, "recursive call site")
+                    db.span_note(*call, "recursive call site");
                 }
-                sess.fileline_help(sp, "a `loop` may express intention \
-                                        better if this is on purpose")
+                db.fileline_help(sp, "a `loop` may express intention \
+                                      better if this is on purpose");
             }
+            db.emit();
         }
 
         // all done
@@ -969,6 +966,12 @@ declare_lint! {
     "const items will not have their symbols exported"
 }
 
+declare_lint! {
+    NO_MANGLE_GENERIC_ITEMS,
+    Warn,
+    "generic items must be mangled"
+}
+
 #[derive(Copy, Clone)]
 pub struct InvalidNoMangleItems;
 
@@ -976,19 +979,26 @@ impl LintPass for InvalidNoMangleItems {
     fn get_lints(&self) -> LintArray {
         lint_array!(PRIVATE_NO_MANGLE_FNS,
                     PRIVATE_NO_MANGLE_STATICS,
-                    NO_MANGLE_CONST_ITEMS)
+                    NO_MANGLE_CONST_ITEMS,
+                    NO_MANGLE_GENERIC_ITEMS)
     }
 }
 
 impl LateLintPass for InvalidNoMangleItems {
     fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
         match it.node {
-            hir::ItemFn(..) => {
-                if attr::contains_name(&it.attrs, "no_mangle") &&
-                       !cx.access_levels.is_reachable(it.id) {
-                    let msg = format!("function {} is marked #[no_mangle], but not exported",
-                                      it.name);
-                    cx.span_lint(PRIVATE_NO_MANGLE_FNS, it.span, &msg);
+            hir::ItemFn(_, _, _, _, ref generics, _) => {
+                if attr::contains_name(&it.attrs, "no_mangle") {
+                    if !cx.access_levels.is_reachable(it.id) {
+                        let msg = format!("function {} is marked #[no_mangle], but not exported",
+                                          it.name);
+                        cx.span_lint(PRIVATE_NO_MANGLE_FNS, it.span, &msg);
+                    }
+                    if generics.is_parameterized() {
+                        cx.span_lint(NO_MANGLE_GENERIC_ITEMS,
+                                     it.span,
+                                     "generic functions must be mangled");
+                    }
                 }
             },
             hir::ItemStatic(..) => {
index 226084361530a98756da8303a5368931a1009e33..78252c491ecfe72818d60ce33be5c97a04c38a38 100644 (file)
 //!
 //! This API is completely unstable and subject to change.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_lint"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -33,7 +30,6 @@
 #![cfg_attr(test, feature(test))]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
-#![feature(num_bits_bytes)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(rustc_private)]
@@ -41,6 +37,7 @@
 #![feature(staged_api)]
 #![feature(str_char)]
 
+#[macro_use]
 extern crate syntax;
 #[macro_use]
 extern crate rustc;
@@ -56,6 +53,7 @@ pub use rustc::util as util;
 
 use session::Session;
 use lint::LintId;
+use lint::FutureIncompatibleInfo;
 
 mod bad_style;
 mod builtin;
@@ -124,7 +122,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
                  UnusedAllocation,
                  MissingCopyImplementations,
                  UnstableFeatures,
-                 Stability,
+                 Deprecated,
                  UnconditionalRecursion,
                  InvalidNoMangleItems,
                  PluginAsLibrary,
@@ -146,8 +144,29 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
                     UNUSED_MUT, UNREACHABLE_CODE, UNUSED_MUST_USE,
                     UNUSED_UNSAFE, PATH_STATEMENTS, UNUSED_ATTRIBUTES);
 
-    add_lint_group!(sess, FUTURE_INCOMPATIBLE,
-                    MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT);
+    // Guidelines for creating a future incompatibility lint:
+    //
+    // - Create a lint defaulting to warn as normal, with ideally the same error
+    //   message you would normally give
+    // - Add a suitable reference, typically an RFC or tracking issue. Go ahead
+    //   and include the full URL.
+    // - Later, change lint to error
+    // - Eventually, remove lint
+    store.register_future_incompatible(sess, vec![
+        FutureIncompatibleInfo {
+            id: LintId::of(PRIVATE_IN_PUBLIC),
+            reference: "the explanation for E0446 (`--explain E0446`)",
+        },
+        FutureIncompatibleInfo {
+            id: LintId::of(INVALID_TYPE_PARAM_DEFAULT),
+            reference: "PR 30742 <https://github.com/rust-lang/rust/pull/30724>",
+        },
+        FutureIncompatibleInfo {
+            id: LintId::of(MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT),
+            reference: "RFC 218 <https://github.com/rust-lang/rfcs/blob/\
+                        master/text/0218-empty-struct-with-braces.md>",
+        },
+        ]);
 
     // We have one lint pass defined specially
     store.register_late_pass(sess, false, box lint::GatherNodeLevels);
@@ -155,6 +174,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
     // Register renamed and removed lints
     store.register_renamed("unknown_features", "unused_features");
     store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate");
+    store.register_removed("negate_unsigned", "cast a signed value instead");
     store.register_removed("raw_pointer_derive", "using derive with raw pointers is ok");
     // This was renamed to raw_pointer_derive, which was then removed,
     // so it is also considered removed
index a1d029025b2fa8c09680dc4ca93f8e7c7462ff77..c3dfca44349eb6f9b6a84538cdd662abc82845b6 100644 (file)
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(non_snake_case)]
+
 use middle::{infer};
 use middle::def_id::DefId;
 use middle::subst::Substs;
@@ -24,13 +26,39 @@ use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
 use syntax::{abi, ast};
 use syntax::attr::{self, AttrMetaMethods};
 use syntax::codemap::{self, Span};
-use syntax::feature_gate::{emit_feature_err, GateIssue};
 use syntax::ast::{TyIs, TyUs, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64};
 
 use rustc_front::hir;
 use rustc_front::intravisit::{self, Visitor};
 use rustc_front::util::is_shift_binop;
 
+register_long_diagnostics! {
+E0519: r##"
+It is not allowed to negate an unsigned integer.
+You can negate a signed integer and cast it to an
+unsigned integer or use the `!` operator.
+
+```
+let x: usize = -1isize as usize;
+let y: usize = !0;
+assert_eq!(x, y);
+```
+
+Alternatively you can use the `Wrapping` newtype
+or the `wrapping_neg` operation that all
+integral types support:
+
+```
+use std::num::Wrapping;
+let x: Wrapping<usize> = -Wrapping(1);
+let Wrapping(x) = x;
+let y: usize = 1.wrapping_neg();
+assert_eq!(x, y);
+```
+
+"##
+}
+
 declare_lint! {
     UNUSED_COMPARISONS,
     Warn,
@@ -73,30 +101,24 @@ impl LateLintPass for TypeLimits {
     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
         match e.node {
             hir::ExprUnary(hir::UnNeg, ref expr) => {
-                match expr.node  {
-                    hir::ExprLit(ref lit) => {
-                        match lit.node {
-                            ast::LitInt(_, ast::UnsignedIntLit(_)) => {
-                                check_unsigned_negation_feature(cx, e.span);
-                            },
-                            ast::LitInt(_, ast::UnsuffixedIntLit(_)) => {
-                                if let ty::TyUint(_) = cx.tcx.node_id_to_type(e.id).sty {
-                                    check_unsigned_negation_feature(cx, e.span);
-                                }
-                            },
-                            _ => ()
-                        }
-                    },
-                    _ => {
-                        let t = cx.tcx.node_id_to_type(expr.id);
-                        match t.sty {
-                            ty::TyUint(_) => {
-                                check_unsigned_negation_feature(cx, e.span);
-                            },
-                            _ => ()
-                        }
+                if let hir::ExprLit(ref lit) = expr.node {
+                    match lit.node {
+                        ast::LitInt(_, ast::UnsignedIntLit(_)) => {
+                            forbid_unsigned_negation(cx, e.span);
+                        },
+                        ast::LitInt(_, ast::UnsuffixedIntLit(_)) => {
+                            if let ty::TyUint(_) = cx.tcx.node_id_to_type(e.id).sty {
+                                forbid_unsigned_negation(cx, e.span);
+                            }
+                        },
+                        _ => ()
                     }
-                };
+                } else {
+                    let t = cx.tcx.node_id_to_type(expr.id);
+                    if let ty::TyUint(_) = t.sty {
+                        forbid_unsigned_negation(cx, e.span);
+                    }
+                }
                 // propagate negation, if the negation itself isn't negated
                 if self.negated_expr_id != e.id {
                     self.negated_expr_id = expr.id;
@@ -253,20 +275,20 @@ impl LateLintPass for TypeLimits {
         fn int_ty_bits(int_ty: ast::IntTy, target_int_ty: ast::IntTy) -> u64 {
             match int_ty {
                 ast::TyIs => int_ty_bits(target_int_ty, target_int_ty),
-                ast::TyI8 =>    i8::BITS  as u64,
-                ast::TyI16 =>   i16::BITS as u64,
-                ast::TyI32 =>   i32::BITS as u64,
-                ast::TyI64 =>   i64::BITS as u64
+                ast::TyI8 => 8,
+                ast::TyI16 => 16 as u64,
+                ast::TyI32 => 32,
+                ast::TyI64 => 64,
             }
         }
 
         fn uint_ty_bits(uint_ty: ast::UintTy, target_uint_ty: ast::UintTy) -> u64 {
             match uint_ty {
                 ast::TyUs => uint_ty_bits(target_uint_ty, target_uint_ty),
-                ast::TyU8 =>    u8::BITS  as u64,
-                ast::TyU16 =>   u16::BITS as u64,
-                ast::TyU32 =>   u32::BITS as u64,
-                ast::TyU64 =>   u64::BITS as u64
+                ast::TyU8 => 8,
+                ast::TyU16 => 16,
+                ast::TyU32 => 32,
+                ast::TyU64 => 64,
             }
         }
 
@@ -322,15 +344,11 @@ impl LateLintPass for TypeLimits {
             }
         }
 
-        fn check_unsigned_negation_feature(cx: &LateContext, span: Span) {
-            if !cx.sess().features.borrow().negate_unsigned {
-                emit_feature_err(
-                    &cx.sess().parse_sess.span_diagnostic,
-                    "negate_unsigned",
-                    span,
-                    GateIssue::Language,
-                    "unary negation of unsigned integers may be removed in the future");
-            }
+        fn forbid_unsigned_negation(cx: &LateContext, span: Span) {
+            cx.sess()
+              .struct_span_err_with_code(span, "unary negation of unsigned integer", "E0519")
+              .span_help(span, "use a cast or the `!` operator")
+              .emit();
         }
     }
 }
index b8750cccb4b729d7c4f78bb21a4c121fdbaddc25..18a3a96069e19d8fcf7bd08bf45399ab239dabbb 100644 (file)
@@ -319,6 +319,7 @@ impl UnusedParens {
                 }
                 ast::ExprUnary(_, ref x) |
                 ast::ExprCast(ref x, _) |
+                ast::ExprType(ref x, _) |
                 ast::ExprField(ref x, _) |
                 ast::ExprTupField(ref x, _) |
                 ast::ExprIndex(ref x, _) => {
index 2b9adf5490a5551b711d3388de40f334dd2ca395..fc7fa299fb8fae9546f9d8b499cb0417fa23ba4c 100644 (file)
@@ -8,19 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![allow(non_upper_case_globals)]
 #![allow(non_camel_case_types)]
 #![allow(non_snake_case)]
 #![allow(dead_code)]
 #![allow(trivial_casts)]
 
-#![cfg_attr(stage0, allow(improper_ctypes))]
-
 #![crate_name = "rustc_llvm"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -33,6 +28,7 @@
 #![feature(link_args)]
 #![feature(staged_api)]
 #![feature(linked_from)]
+#![feature(concat_idents)]
 
 extern crate libc;
 #[macro_use] #[no_link] extern crate rustc_bitflags;
@@ -89,6 +85,7 @@ pub enum CallConv {
     X86StdcallCallConv = 64,
     X86FastcallCallConv = 65,
     X86_64_Win64 = 79,
+    X86_VectorCall = 80
 }
 
 #[derive(Copy, Clone)]
@@ -615,6 +612,7 @@ extern {
                                              C: ContextRef)
                                              -> ModuleRef;
     pub fn LLVMGetModuleContext(M: ModuleRef) -> ContextRef;
+    pub fn LLVMCloneModule(M: ModuleRef) -> ModuleRef;
     pub fn LLVMDisposeModule(M: ModuleRef);
 
     /// Data layout. See Module::getDataLayout.
@@ -2010,32 +2008,6 @@ extern {
     pub fn LLVMIsAAllocaInst(value_ref: ValueRef) -> ValueRef;
     pub fn LLVMIsAConstantInt(value_ref: ValueRef) -> ValueRef;
 
-    pub fn LLVMInitializeX86TargetInfo();
-    pub fn LLVMInitializeX86Target();
-    pub fn LLVMInitializeX86TargetMC();
-    pub fn LLVMInitializeX86AsmPrinter();
-    pub fn LLVMInitializeX86AsmParser();
-    pub fn LLVMInitializeARMTargetInfo();
-    pub fn LLVMInitializeARMTarget();
-    pub fn LLVMInitializeARMTargetMC();
-    pub fn LLVMInitializeARMAsmPrinter();
-    pub fn LLVMInitializeARMAsmParser();
-    pub fn LLVMInitializeAArch64TargetInfo();
-    pub fn LLVMInitializeAArch64Target();
-    pub fn LLVMInitializeAArch64TargetMC();
-    pub fn LLVMInitializeAArch64AsmPrinter();
-    pub fn LLVMInitializeAArch64AsmParser();
-    pub fn LLVMInitializeMipsTargetInfo();
-    pub fn LLVMInitializeMipsTarget();
-    pub fn LLVMInitializeMipsTargetMC();
-    pub fn LLVMInitializeMipsAsmPrinter();
-    pub fn LLVMInitializeMipsAsmParser();
-    pub fn LLVMInitializePowerPCTargetInfo();
-    pub fn LLVMInitializePowerPCTarget();
-    pub fn LLVMInitializePowerPCTargetMC();
-    pub fn LLVMInitializePowerPCAsmPrinter();
-    pub fn LLVMInitializePowerPCAsmParser();
-
     pub fn LLVMRustAddPass(PM: PassManagerRef, Pass: *const c_char) -> bool;
     pub fn LLVMRustCreateTargetMachine(Triple: *const c_char,
                                        CPU: *const c_char,
@@ -2143,6 +2115,53 @@ extern {
     pub fn LLVMRustGetModuleDataLayout(M: ModuleRef) -> TargetDataRef;
 }
 
+#[cfg(have_component_x86)]
+extern {
+    pub fn LLVMInitializeX86TargetInfo();
+    pub fn LLVMInitializeX86Target();
+    pub fn LLVMInitializeX86TargetMC();
+    pub fn LLVMInitializeX86AsmPrinter();
+    pub fn LLVMInitializeX86AsmParser();
+}
+#[cfg(have_component_arm)]
+extern {
+    pub fn LLVMInitializeARMTargetInfo();
+    pub fn LLVMInitializeARMTarget();
+    pub fn LLVMInitializeARMTargetMC();
+    pub fn LLVMInitializeARMAsmPrinter();
+    pub fn LLVMInitializeARMAsmParser();
+}
+#[cfg(have_component_aarch64)]
+extern {
+    pub fn LLVMInitializeAArch64TargetInfo();
+    pub fn LLVMInitializeAArch64Target();
+    pub fn LLVMInitializeAArch64TargetMC();
+    pub fn LLVMInitializeAArch64AsmPrinter();
+    pub fn LLVMInitializeAArch64AsmParser();
+}
+#[cfg(have_component_mips)]
+extern {
+    pub fn LLVMInitializeMipsTargetInfo();
+    pub fn LLVMInitializeMipsTarget();
+    pub fn LLVMInitializeMipsTargetMC();
+    pub fn LLVMInitializeMipsAsmPrinter();
+    pub fn LLVMInitializeMipsAsmParser();
+}
+#[cfg(have_component_powerpc)]
+extern {
+    pub fn LLVMInitializePowerPCTargetInfo();
+    pub fn LLVMInitializePowerPCTarget();
+    pub fn LLVMInitializePowerPCTargetMC();
+    pub fn LLVMInitializePowerPCAsmPrinter();
+    pub fn LLVMInitializePowerPCAsmParser();
+}
+#[cfg(have_component_pnacl)]
+extern {
+    pub fn LLVMInitializePNaClTargetInfo();
+    pub fn LLVMInitializePNaClTarget();
+    pub fn LLVMInitializePNaClTargetMC();
+}
+
 // LLVM requires symbols from this library, but apparently they're not printed
 // during llvm-config?
 #[cfg(windows)]
@@ -2327,6 +2346,51 @@ pub unsafe fn debug_loc_to_string(c: ContextRef, tr: DebugLocRef) -> String {
         .expect("got a non-UTF8 DebugLoc from LLVM")
 }
 
+pub fn initialize_available_targets() {
+    macro_rules! init_target(
+        ($cfg:ident $arch:ident) => { {
+            #[cfg($cfg)]
+            fn init() {
+                unsafe {
+                    let f = concat_idents!(LLVMInitialize, $arch, TargetInfo);
+                    f();
+                    let f = concat_idents!(LLVMInitialize, $arch, Target);
+                    f();
+                    let f = concat_idents!(LLVMInitialize, $arch, TargetMC);
+                    f();
+                    let f = concat_idents!(LLVMInitialize, $arch, AsmPrinter);
+                    f();
+                    let f = concat_idents!(LLVMInitialize, $arch, AsmParser);
+                    f();
+                }
+            }
+            #[cfg(not($cfg))]
+            fn init() { }
+            init();
+        } }
+    );
+
+    init_target!(have_component_powerpc PowerPC);
+    init_target!(have_component_mips Mips);
+    init_target!(have_component_aarch64 AArch64);
+    init_target!(have_component_arm ARM);
+    init_target!(have_component_x86 X86);
+
+    // PNaCl doesn't provide some of the optional target components, so we
+    // manually initialize it here.
+    #[cfg(have_component_pnacl)]
+    fn init_pnacl() {
+        unsafe {
+            LLVMInitializePNaClTargetInfo();
+            LLVMInitializePNaClTarget();
+            LLVMInitializePNaClTargetMC();
+        }
+    }
+    #[cfg(not(have_component_pnacl))]
+    fn init_pnacl() { }
+    init_pnacl();
+}
+
 // The module containing the native LLVM dependencies, generated by the build system
 // Note that this must come after the rustllvm extern declaration so that
 // parts of LLVM that rustllvm depends on aren't thrown away by the linker.
index 9de7b3b0cc52511e13453d683b7bca6f8d21e8a5..8c3bd3c4f8a5352851b919077ad90224bc70c425 100644 (file)
@@ -39,7 +39,6 @@ use middle::ty::{self, Ty};
 
 use syntax::{ast, ast_util, codemap};
 use syntax::ast::NodeIdAssigner;
-use syntax::codemap::Span;
 use syntax::ptr::P;
 
 use std::cell::Cell;
@@ -116,7 +115,7 @@ 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: Span) -> Span {
+    fn new_span(&self, span: codemap::Span) -> codemap::Span {
         self.tr_span(span)
     }
 }
@@ -125,20 +124,20 @@ impl<'a, 'b, 'c, 'tcx> ast_map::FoldOps for &'a DecodeContext<'b, 'c, 'tcx> {
 /// ast-map.
 pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata,
                                  tcx: &ty::ctxt<'tcx>,
-                                 path: Vec<ast_map::PathElem>,
-                                 def_path: ast_map::DefPath,
+                                 parent_path: Vec<ast_map::PathElem>,
+                                 parent_def_path: ast_map::DefPath,
                                  par_doc: rbml::Doc,
                                  orig_did: DefId)
                                  -> Result<&'tcx InlinedItem, (Vec<ast_map::PathElem>,
                                                                ast_map::DefPath)> {
     match par_doc.opt_child(c::tag_ast) {
-      None => Err((path, def_path)),
+      None => Err((parent_path, parent_def_path)),
       Some(ast_doc) => {
         let mut path_as_str = None;
         debug!("> Decoding inlined fn: {:?}::?",
         {
             // Do an Option dance to use the path after it is moved below.
-            let s = ast_map::path_to_string(path.iter().cloned());
+            let s = ast_map::path_to_string(parent_path.iter().cloned());
             path_as_str = Some(s);
             path_as_str.as_ref().map(|x| &x[..])
         });
@@ -153,8 +152,11 @@ pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata,
             last_filemap_index: Cell::new(0)
         };
         let raw_ii = decode_ast(ast_doc);
-        let ii = ast_map::map_decoded_item(&dcx.tcx.map, path, def_path, raw_ii, dcx);
-
+        let ii = ast_map::map_decoded_item(&dcx.tcx.map,
+                                           parent_path,
+                                           parent_def_path,
+                                           raw_ii,
+                                           dcx);
         let name = match *ii {
             InlinedItem::Item(ref i) => i.name,
             InlinedItem::Foreign(ref i) => i.name,
@@ -219,60 +221,12 @@ 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. `creader::import_codemap()` will
-    /// already have allocated any additionally needed FileMaps in the local
-    /// codemap as a side-effect of creating the crate_metadata's
-    /// `codemap_import_info`.
-    pub fn tr_span(&self, span: Span) -> 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
-            // translate these values, let's transform them into something we
-            // can handle (and which will produce useful debug locations at
-            // 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)
-        } else {
-            span
-        };
-
-        let imported_filemaps = self.cdata.imported_filemaps(self.tcx.sess.codemap());
-        let filemap = {
-            // Optimize for the case that most spans within a translated item
-            // originate from the same filemap.
-            let last_filemap_index = self.last_filemap_index.get();
-            let last_filemap = &imported_filemaps[last_filemap_index];
-
-            if span.lo >= last_filemap.original_start_pos &&
-               span.lo <= last_filemap.original_end_pos &&
-               span.hi >= last_filemap.original_start_pos &&
-               span.hi <= last_filemap.original_end_pos {
-                last_filemap
-            } else {
-                let mut a = 0;
-                let mut b = imported_filemaps.len();
-
-                while b - a > 1 {
-                    let m = (a + b) / 2;
-                    if imported_filemaps[m].original_start_pos > span.lo {
-                        b = m;
-                    } else {
-                        a = m;
-                    }
-                }
-
-                self.last_filemap_index.set(a);
-                &imported_filemaps[a]
-            }
-        };
-
-        let lo = (span.lo - filemap.original_start_pos) +
-                  filemap.translated_filemap.start_pos;
-        let hi = (span.hi - filemap.original_start_pos) +
-                  filemap.translated_filemap.start_pos;
-
-        codemap::mk_sp(lo, hi)
+    /// within the local crate's codemap.
+    pub fn tr_span(&self, span: codemap::Span) -> codemap::Span {
+        decoder::translate_span(self.cdata,
+                                self.tcx.sess.codemap(),
+                                &self.last_filemap_index,
+                                span)
     }
 }
 
@@ -288,8 +242,8 @@ impl tr for Option<DefId> {
     }
 }
 
-impl tr for Span {
-    fn tr(&self, dcx: &DecodeContext) -> Span {
+impl tr for codemap::Span {
+    fn tr(&self, dcx: &DecodeContext) -> codemap::Span {
         dcx.tr_span(*self)
     }
 }
@@ -338,7 +292,7 @@ impl<D:serialize::Decoder> def_id_decoder_helpers for D
 
 fn encode_ast(rbml_w: &mut Encoder, item: &InlinedItem) {
     rbml_w.start_tag(c::tag_tree as usize);
-    item.encode(rbml_w);
+    rbml_w.emit_opaque(|this| item.encode(this));
     rbml_w.end_tag();
 }
 
@@ -407,8 +361,8 @@ fn simplify_ast(ii: InlinedItemRef) -> InlinedItem {
 
 fn decode_ast(par_doc: rbml::Doc) -> InlinedItem {
     let chi_doc = par_doc.get(c::tag_tree as usize);
-    let mut d = reader::Decoder::new(chi_doc);
-    Decodable::decode(&mut d).unwrap()
+    let mut rbml_r = reader::Decoder::new(chi_doc);
+    rbml_r.read_opaque(|decoder, _| Decodable::decode(decoder)).unwrap()
 }
 
 // ______________________________________________________________________
@@ -448,7 +402,6 @@ impl tr for def::Def {
               def::DefAssociatedTy(trait_did.tr(dcx), did.tr(dcx)),
           def::DefPrimTy(p) => def::DefPrimTy(p),
           def::DefTyParam(s, index, def_id, n) => def::DefTyParam(s, index, def_id.tr(dcx), n),
-          def::DefUse(did) => def::DefUse(did.tr(dcx)),
           def::DefUpvar(_, nid1, index, nid2) => {
               let nid1 = dcx.tr_id(nid1);
               let nid2 = dcx.tr_id(nid2);
@@ -456,7 +409,8 @@ impl tr for def::Def {
               def::DefUpvar(did1, nid1, index, nid2)
           }
           def::DefStruct(did) => def::DefStruct(did.tr(dcx)),
-          def::DefLabel(nid) => def::DefLabel(dcx.tr_id(nid))
+          def::DefLabel(nid) => def::DefLabel(dcx.tr_id(nid)),
+          def::DefErr => def::DefErr,
         }
     }
 }
@@ -555,21 +509,6 @@ pub fn encode_cast_kind(ebml_w: &mut Encoder, kind: cast::CastKind) {
 // ______________________________________________________________________
 // Encoding and decoding the side tables
 
-trait get_ty_str_ctxt<'tcx> {
-    fn ty_str_ctxt<'a>(&'a self) -> tyencode::ctxt<'a, 'tcx>;
-}
-
-impl<'a, 'tcx> get_ty_str_ctxt<'tcx> for e::EncodeContext<'a, 'tcx> {
-    fn ty_str_ctxt<'b>(&'b self) -> tyencode::ctxt<'b, 'tcx> {
-        tyencode::ctxt {
-            diag: self.tcx.sess.diagnostic(),
-            ds: e::def_to_string,
-            tcx: self.tcx,
-            abbrevs: &self.type_abbrevs
-        }
-    }
-}
-
 trait rbml_writer_helpers<'tcx> {
     fn emit_region(&mut self, ecx: &e::EncodeContext, r: ty::Region);
     fn emit_ty<'a>(&mut self, ecx: &e::EncodeContext<'a, 'tcx>, ty: Ty<'tcx>);
@@ -594,11 +533,15 @@ trait rbml_writer_helpers<'tcx> {
 
 impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
     fn emit_region(&mut self, ecx: &e::EncodeContext, r: ty::Region) {
-        self.emit_opaque(|this| Ok(e::write_region(ecx, this, r)));
+        self.emit_opaque(|this| Ok(tyencode::enc_region(&mut this.cursor,
+                                                        &ecx.ty_str_ctxt(),
+                                                        r)));
     }
 
     fn emit_ty<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>, ty: Ty<'tcx>) {
-        self.emit_opaque(|this| Ok(e::write_type(ecx, this, ty)));
+        self.emit_opaque(|this| Ok(tyencode::enc_ty(&mut this.cursor,
+                                                    &ecx.ty_str_ctxt(),
+                                                    ty)));
     }
 
     fn emit_tys<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>, tys: &[Ty<'tcx>]) {
@@ -607,13 +550,15 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
 
     fn emit_trait_ref<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>,
                           trait_ref: &ty::TraitRef<'tcx>) {
-        self.emit_opaque(|this| Ok(e::write_trait_ref(ecx, this, trait_ref)));
+        self.emit_opaque(|this| Ok(tyencode::enc_trait_ref(&mut this.cursor,
+                                                           &ecx.ty_str_ctxt(),
+                                                           *trait_ref)));
     }
 
     fn emit_predicate<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>,
                           predicate: &ty::Predicate<'tcx>) {
         self.emit_opaque(|this| {
-            Ok(tyencode::enc_predicate(this,
+            Ok(tyencode::enc_predicate(&mut this.cursor,
                                        &ecx.ty_str_ctxt(),
                                        predicate))
         });
@@ -621,13 +566,13 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
 
     fn emit_existential_bounds<'b>(&mut self, ecx: &e::EncodeContext<'b,'tcx>,
                                    bounds: &ty::ExistentialBounds<'tcx>) {
-        self.emit_opaque(|this| Ok(tyencode::enc_existential_bounds(this,
+        self.emit_opaque(|this| Ok(tyencode::enc_existential_bounds(&mut this.cursor,
                                                                     &ecx.ty_str_ctxt(),
                                                                     bounds)));
     }
 
     fn emit_builtin_bounds(&mut self, ecx: &e::EncodeContext, bounds: &ty::BuiltinBounds) {
-        self.emit_opaque(|this| Ok(tyencode::enc_builtin_bounds(this,
+        self.emit_opaque(|this| Ok(tyencode::enc_builtin_bounds(&mut this.cursor,
                                                                 &ecx.ty_str_ctxt(),
                                                                 bounds)));
     }
@@ -654,9 +599,9 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
 
     fn emit_substs<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>,
                        substs: &subst::Substs<'tcx>) {
-        self.emit_opaque(|this| Ok(tyencode::enc_substs(this,
-                                                           &ecx.ty_str_ctxt(),
-                                                           substs)));
+        self.emit_opaque(|this| Ok(tyencode::enc_substs(&mut this.cursor,
+                                                        &ecx.ty_str_ctxt(),
+                                                        substs)));
     }
 
     fn emit_auto_adjustment<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>,
@@ -924,10 +869,6 @@ trait rbml_decoder_decoder_helpers<'tcx> {
                                    -> adjustment::AutoDerefRef<'tcx>;
     fn read_autoref<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
                             -> adjustment::AutoRef<'tcx>;
-    fn convert_def_id(&mut self,
-                      dcx: &DecodeContext,
-                      did: DefId)
-                      -> DefId;
 
     // Versions of the type reading functions that don't need the full
     // DecodeContext.
@@ -979,12 +920,12 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
     fn read_ty_encoded<'b, 'c, F, R>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>, op: F) -> R
         where F: for<'x> FnOnce(&mut tydecode::TyDecoder<'x,'tcx>) -> R
     {
-        return self.read_opaque(|this, doc| {
+        return self.read_opaque(|_, doc| {
             debug!("read_ty_encoded({})", type_string(doc));
             Ok(op(
                 &mut tydecode::TyDecoder::with_doc(
                     dcx.tcx, dcx.cdata.cnum, doc,
-                    &mut |a| this.convert_def_id(dcx, a))))
+                    &mut |d| convert_def_id(dcx, d))))
         }).unwrap();
 
         fn type_string(doc: rbml::Doc) -> String {
@@ -1035,9 +976,9 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
 
     fn read_substs<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
                            -> subst::Substs<'tcx> {
-        self.read_opaque(|this, doc| {
+        self.read_opaque(|_, doc| {
             Ok(tydecode::TyDecoder::with_doc(dcx.tcx, dcx.cdata.cnum, doc,
-                                             &mut |a| this.convert_def_id(dcx, a))
+                                             &mut |d| convert_def_id(dcx, d))
                .parse_substs())
         }).unwrap()
     }
@@ -1143,47 +1084,46 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
     {
         Decodable::decode(self).unwrap()
     }
+}
 
-    /// Converts a def-id that appears in a type.  The correct
-    /// translation will depend on what kind of def-id this is.
-    /// This is a subtle point: type definitions are not
-    /// inlined into the current crate, so if the def-id names
-    /// a nominal type or type alias, then it should be
-    /// translated to refer to the source crate.
-    ///
-    /// However, *type parameters* are cloned along with the function
-    /// they are attached to.  So we should translate those def-ids
-    /// to refer to the new, cloned copy of the type parameter.
-    /// We only see references to free type parameters in the body of
-    /// an inlined function. In such cases, we need the def-id to
-    /// be a local id so that the TypeContents code is able to lookup
-    /// the relevant info in the ty_param_defs table.
-    ///
-    /// *Region parameters*, unfortunately, are another kettle of fish.
-    /// In such cases, def_id's can appear in types to distinguish
-    /// shadowed bound regions and so forth. It doesn't actually
-    /// matter so much what we do to these, since regions are erased
-    /// at trans time, but it's good to keep them consistent just in
-    /// case. We translate them with `tr_def_id()` which will map
-    /// the crate numbers back to the original source crate.
-    ///
-    /// Scopes will end up as being totally bogus. This can actually
-    /// be fixed though.
-    ///
-    /// Unboxed closures are cloned along with the function being
-    /// inlined, and all side tables use interned node IDs, so we
-    /// translate their def IDs accordingly.
-    ///
-    /// It'd be really nice to refactor the type repr to not include
-    /// def-ids so that all these distinctions were unnecessary.
-    fn convert_def_id(&mut self,
-                      dcx: &DecodeContext,
-                      did: DefId)
-                      -> DefId {
-        let r = dcx.tr_def_id(did);
-        debug!("convert_def_id(did={:?})={:?}", did, r);
-        return r;
-    }
+// Converts a def-id that appears in a type.  The correct
+// translation will depend on what kind of def-id this is.
+// This is a subtle point: type definitions are not
+// inlined into the current crate, so if the def-id names
+// a nominal type or type alias, then it should be
+// translated to refer to the source crate.
+//
+// However, *type parameters* are cloned along with the function
+// they are attached to.  So we should translate those def-ids
+// to refer to the new, cloned copy of the type parameter.
+// We only see references to free type parameters in the body of
+// an inlined function. In such cases, we need the def-id to
+// be a local id so that the TypeContents code is able to lookup
+// the relevant info in the ty_param_defs table.
+//
+// *Region parameters*, unfortunately, are another kettle of fish.
+// In such cases, def_id's can appear in types to distinguish
+// shadowed bound regions and so forth. It doesn't actually
+// matter so much what we do to these, since regions are erased
+// at trans time, but it's good to keep them consistent just in
+// case. We translate them with `tr_def_id()` which will map
+// the crate numbers back to the original source crate.
+//
+// Scopes will end up as being totally bogus. This can actually
+// be fixed though.
+//
+// Unboxed closures are cloned along with the function being
+// inlined, and all side tables use interned node IDs, so we
+// translate their def IDs accordingly.
+//
+// It'd be really nice to refactor the type repr to not include
+// def-ids so that all these distinctions were unnecessary.
+fn convert_def_id(dcx: &DecodeContext,
+                  did: DefId)
+                  -> DefId {
+    let r = dcx.tr_def_id(did);
+    debug!("convert_def_id(did={:?})={:?}", did, r);
+    return r;
 }
 
 fn decode_side_tables(dcx: &DecodeContext,
index b6454a4c81a2f8ce4aeee63dc3481ba7da670cdf..479ab7592784795a765e46f7ad4e32ccf4efc880 100644 (file)
@@ -120,7 +120,8 @@ enum_from_u32! {
 
         tag_tree = 0x51,
 
-        // GAP 0x52
+        tag_mir = 0x52,
+
         tag_table = 0x53,
         // GAP 0x54, 0x55
         tag_table_def = 0x56,
@@ -236,6 +237,8 @@ pub const tag_impl_coerce_unsized_kind: usize = 0xa5;
 
 pub const tag_items_data_item_constness: usize = 0xa6;
 
+pub const tag_items_data_item_deprecation: usize = 0xa7;
+
 pub const tag_rustc_version: usize = 0x10f;
 pub fn rustc_version() -> String {
     format!(
index 4420da5f9b87dfd3f7d77fcb18f73d24381e1b27..9122148a8cc05ff9dc37f7927feb4fb742e3bc16 100644 (file)
@@ -35,6 +35,7 @@ use syntax::codemap::{self, Span, mk_sp, Pos};
 use syntax::parse;
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
+use syntax::errors::FatalError;
 use syntax::parse::token::InternedString;
 use syntax::util::small_vector::SmallVector;
 use rustc_front::intravisit::Visitor;
@@ -504,7 +505,10 @@ impl<'a> CrateReader<'a> {
                 let lo = p.span.lo;
                 let body = match p.parse_all_token_trees() {
                     Ok(body) => body,
-                    Err(err) => panic!(err),
+                    Err(mut err) => {
+                        err.emit();
+                        panic!(FatalError);
+                    }
                 };
                 let span = mk_sp(lo, p.last_span.hi);
                 p.abort_if_errors();
index aae3a762c197ac37ceef3544a860e26ba7c76aa9..ecbc84023309105f9953e0a7babf1a36c43e79d5 100644 (file)
@@ -22,6 +22,7 @@ use middle::ty::{self, Ty};
 use middle::def_id::{DefId, DefIndex};
 
 use rustc::front::map as hir_map;
+use rustc::mir::repr::Mir;
 use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet};
 
 use std::cell::RefCell;
@@ -41,6 +42,12 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         decoder::get_stability(&*cdata, def.index)
     }
 
+    fn deprecation(&self, def: DefId) -> Option<attr::Deprecation>
+    {
+        let cdata = self.get_crate_data(def.krate);
+        decoder::get_deprecation(&*cdata, def.index)
+    }
+
     fn closure_kind(&self, _tcx: &ty::ctxt<'tcx>, def_id: DefId) -> ty::ClosureKind
     {
         assert!(!def_id.is_local());
@@ -421,6 +428,12 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         decoder::maybe_get_item_ast(&*cdata, tcx, def.index, decode_inlined_item)
     }
 
+    fn maybe_get_item_mir(&self, tcx: &ty::ctxt<'tcx>, def: DefId)
+                          -> Option<Mir<'tcx>> {
+        let cdata = self.get_crate_data(def.krate);
+        decoder::maybe_get_item_mir(&*cdata, tcx, def.index)
+    }
+
     fn crates(&self) -> Vec<ast::CrateNum>
     {
         let mut result = vec![];
@@ -473,6 +486,7 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
                        item_symbols: &RefCell<NodeMap<String>>,
                        link_meta: &LinkMeta,
                        reachable: &NodeSet,
+                       mir_map: &NodeMap<Mir<'tcx>>,
                        krate: &hir::Crate) -> Vec<u8>
     {
         let encode_inlined_item: encoder::EncodeInlinedItem =
@@ -486,7 +500,8 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
             link_meta: link_meta,
             cstore: self,
             encode_inlined_item: encode_inlined_item,
-            reachable: reachable
+            reachable: reachable,
+            mir_map: mir_map,
         };
         encoder::encode_metadata(encode_params, krate)
 
index 092f7849115c3eed714dc13438f7462cf6c1319e..def5897e92d9d8aa8d1161ad84577c95315382c0 100644 (file)
@@ -18,6 +18,7 @@ use cstore::{self, crate_metadata};
 use common::*;
 use encoder::def_to_u64;
 use index;
+use tls_context;
 use tydecode::TyDecoder;
 
 use rustc::back::svh::Svh;
@@ -26,15 +27,18 @@ use rustc::util::nodemap::FnvHashMap;
 use rustc_front::hir;
 
 use middle::cstore::{LOCAL_CRATE, FoundAst, InlinedItem, LinkagePreference};
-use middle::cstore::{DefLike, DlDef, DlField, DlImpl};
+use middle::cstore::{DefLike, DlDef, DlField, DlImpl, tls};
 use middle::def;
 use middle::def_id::{DefId, DefIndex};
 use middle::lang_items;
 use middle::subst;
 use middle::ty::{ImplContainer, TraitContainer};
-use middle::ty::{self, RegionEscape, Ty};
+use middle::ty::{self, Ty, TypeFoldable};
 
-use std::cell::{Cell, RefCell};
+use rustc::mir;
+use rustc::mir::visit::MutVisitor;
+
+use std::cell::Cell;
 use std::io::prelude::*;
 use std::io;
 use std::rc::Rc;
@@ -48,7 +52,7 @@ use syntax::parse::token::{IdentInterner, special_idents};
 use syntax::parse::token;
 use syntax::ast;
 use syntax::abi;
-use syntax::codemap;
+use syntax::codemap::{self, Span};
 use syntax::print::pprust;
 use syntax::ptr::P;
 
@@ -97,12 +101,15 @@ enum Family {
     Mod,                   // m
     ForeignMod,            // n
     Enum,                  // t
-    TupleVariant,          // v
     StructVariant,         // V
+    TupleVariant,          // v
+    UnitVariant,           // w
     Impl,                  // i
-    DefaultImpl,              // d
+    DefaultImpl,           // d
     Trait,                 // I
     Struct,                // S
+    TupleStruct,           // s
+    UnitStruct,            // u
     PublicField,           // g
     InheritedField,        // N
     Constant,              // C
@@ -122,12 +129,15 @@ fn item_family(item: rbml::Doc) -> Family {
       'm' => Mod,
       'n' => ForeignMod,
       't' => Enum,
-      'v' => TupleVariant,
       'V' => StructVariant,
+      'v' => TupleVariant,
+      'w' => UnitVariant,
       'i' => Impl,
       'd' => DefaultImpl,
       'I' => Trait,
       'S' => Struct,
+      's' => TupleStruct,
+      'u' => UnitStruct,
       'g' => PublicField,
       'N' => InheritedField,
        c => panic!("unexpected family char: {}", c)
@@ -278,7 +288,7 @@ fn item_to_def_like(cdata: Cmd, item: rbml::Doc, did: DefId) -> DefLike {
         }
         ImmStatic => DlDef(def::DefStatic(did, false)),
         MutStatic => DlDef(def::DefStatic(did, true)),
-        Struct    => DlDef(def::DefStruct(did)),
+        Struct | TupleStruct | UnitStruct => DlDef(def::DefStruct(did)),
         Fn        => DlDef(def::DefFn(did, false)),
         CtorFn    => DlDef(def::DefFn(did, true)),
         Method | StaticMethod => {
@@ -298,7 +308,7 @@ fn item_to_def_like(cdata: Cmd, item: rbml::Doc, did: DefId) -> DefLike {
             let enum_did = item_require_parent_item(cdata, item);
             DlDef(def::DefVariant(enum_did, did, true))
         }
-        TupleVariant => {
+        TupleVariant | UnitVariant => {
             let enum_did = item_require_parent_item(cdata, item);
             DlDef(def::DefVariant(enum_did, did, false))
         }
@@ -349,16 +359,11 @@ pub fn get_trait_def<'tcx>(cdata: Cmd,
     let associated_type_names = parse_associated_type_names(item_doc);
     let paren_sugar = parse_paren_sugar(item_doc);
 
-    ty::TraitDef {
-        paren_sugar: paren_sugar,
-        unsafety: unsafety,
-        generics: generics,
-        trait_ref: item_trait_ref(item_doc, tcx, cdata),
-        associated_type_names: associated_type_names,
-        nonblanket_impls: RefCell::new(FnvHashMap()),
-        blanket_impls: RefCell::new(vec![]),
-        flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS)
-    }
+    ty::TraitDef::new(unsafety,
+                      paren_sugar,
+                      generics,
+                      item_trait_ref(item_doc, tcx, cdata),
+                      associated_type_names)
 }
 
 pub fn get_adt_def<'tcx>(intr: &IdentInterner,
@@ -366,6 +371,14 @@ pub fn get_adt_def<'tcx>(intr: &IdentInterner,
                          item_id: DefIndex,
                          tcx: &ty::ctxt<'tcx>) -> ty::AdtDefMaster<'tcx>
 {
+    fn family_to_variant_kind<'tcx>(family: Family, tcx: &ty::ctxt<'tcx>) -> ty::VariantKind {
+        match family {
+            Struct | StructVariant => ty::VariantKind::Struct,
+            TupleStruct | TupleVariant => ty::VariantKind::Tuple,
+            UnitStruct | UnitVariant => ty::VariantKind::Unit,
+            _ => tcx.sess.bug(&format!("unexpected family: {:?}", family)),
+        }
+    }
     fn get_enum_variants<'tcx>(intr: &IdentInterner,
                                cdata: Cmd,
                                doc: rbml::Doc,
@@ -385,7 +398,8 @@ pub fn get_adt_def<'tcx>(intr: &IdentInterner,
                 did: did,
                 name: item_name(intr, item),
                 fields: get_variant_fields(intr, cdata, item, tcx),
-                disr_val: disr
+                disr_val: disr,
+                kind: family_to_variant_kind(item_family(item), tcx),
             }
         }).collect()
     }
@@ -418,7 +432,8 @@ pub fn get_adt_def<'tcx>(intr: &IdentInterner,
             did: did,
             name: item_name(intr, doc),
             fields: get_variant_fields(intr, cdata, doc, tcx),
-            disr_val: 0
+            disr_val: 0,
+            kind: family_to_variant_kind(item_family(doc), tcx),
         }
     }
 
@@ -429,7 +444,7 @@ pub fn get_adt_def<'tcx>(intr: &IdentInterner,
             (ty::AdtKind::Enum,
              get_enum_variants(intr, cdata, doc, tcx))
         }
-        Struct => {
+        Struct | TupleStruct | UnitStruct => {
             let ctor_did =
                 reader::maybe_get_doc(doc, tag_items_data_item_struct_ctor).
                 map_or(did, |ctor_doc| translated_def_id(cdata, ctor_doc));
@@ -522,6 +537,14 @@ pub fn get_stability(cdata: Cmd, id: DefIndex) -> Option<attr::Stability> {
     })
 }
 
+pub fn get_deprecation(cdata: Cmd, id: DefIndex) -> Option<attr::Deprecation> {
+    let item = cdata.lookup_item(id);
+    reader::maybe_get_doc(item, tag_items_data_item_deprecation).map(|doc| {
+        let mut decoder = reader::Decoder::new(doc);
+        Decodable::decode(&mut decoder).unwrap()
+    })
+}
+
 pub fn get_repr_attrs(cdata: Cmd, id: DefIndex) -> Vec<attr::ReprAttr> {
     let item = cdata.lookup_item(id);
     match reader::maybe_get_doc(item, tag_items_data_item_repr).map(|doc| {
@@ -751,29 +774,54 @@ pub fn get_item_name(intr: &IdentInterner, cdata: Cmd, id: DefIndex) -> ast::Nam
 pub type DecodeInlinedItem<'a> =
     Box<for<'tcx> FnMut(Cmd,
                         &ty::ctxt<'tcx>,
-                        Vec<hir_map::PathElem>,
-                        hir_map::DefPath,
+                        Vec<hir_map::PathElem>, // parent_path
+                        hir_map::DefPath,       // parent_def_path
                         rbml::Doc,
                         DefId)
                         -> Result<&'tcx InlinedItem, (Vec<hir_map::PathElem>,
                                                       hir_map::DefPath)> + 'a>;
 
-pub fn maybe_get_item_ast<'tcx>(cdata: Cmd, tcx: &ty::ctxt<'tcx>, id: DefIndex,
+pub fn maybe_get_item_ast<'tcx>(cdata: Cmd,
+                                tcx: &ty::ctxt<'tcx>,
+                                id: DefIndex,
                                 mut decode_inlined_item: DecodeInlinedItem)
                                 -> FoundAst<'tcx> {
     debug!("Looking up item: {:?}", id);
     let item_doc = cdata.lookup_item(id);
     let item_did = item_def_id(item_doc, cdata);
-    let path = item_path(item_doc).split_last().unwrap().1.to_vec();
-    let def_path = def_path(cdata, id);
-    match decode_inlined_item(cdata, tcx, path, def_path, item_doc, item_did) {
+    let parent_path = {
+        let mut path = item_path(item_doc);
+        path.pop();
+        path
+    };
+    let parent_def_path = {
+        let mut def_path = def_path(cdata, id);
+        def_path.pop();
+        def_path
+    };
+    match decode_inlined_item(cdata,
+                              tcx,
+                              parent_path,
+                              parent_def_path,
+                              item_doc,
+                              item_did) {
         Ok(ii) => FoundAst::Found(ii),
-        Err((path, def_path)) => {
+        Err((mut parent_path, mut parent_def_path)) => {
             match item_parent_item(cdata, item_doc) {
-                Some(did) => {
-                    let parent_item = cdata.lookup_item(did.index);
-                    match decode_inlined_item(cdata, tcx, path, def_path, parent_item, did) {
-                        Ok(ii) => FoundAst::FoundParent(did, ii),
+                Some(parent_did) => {
+                    // Remove the last element from the paths, since we are now
+                    // trying to inline the parent.
+                    parent_path.pop();
+                    parent_def_path.pop();
+
+                    let parent_item = cdata.lookup_item(parent_did.index);
+                    match decode_inlined_item(cdata,
+                                              tcx,
+                                              parent_path,
+                                              parent_def_path,
+                                              parent_item,
+                                              parent_did) {
+                        Ok(ii) => FoundAst::FoundParent(parent_did, ii),
                         Err(_) => FoundAst::NotFound
                     }
                 }
@@ -783,6 +831,58 @@ pub fn maybe_get_item_ast<'tcx>(cdata: Cmd, tcx: &ty::ctxt<'tcx>, id: DefIndex,
     }
 }
 
+pub fn maybe_get_item_mir<'tcx>(cdata: Cmd,
+                                tcx: &ty::ctxt<'tcx>,
+                                id: DefIndex)
+                                -> Option<mir::repr::Mir<'tcx>> {
+    let item_doc = cdata.lookup_item(id);
+
+    return reader::maybe_get_doc(item_doc, tag_mir as usize).map(|mir_doc| {
+        let dcx = tls_context::DecodingContext {
+            crate_metadata: cdata,
+            tcx: tcx,
+        };
+        let mut decoder = reader::Decoder::new(mir_doc);
+
+        let mut mir = decoder.read_opaque(|opaque_decoder, _| {
+            tls::enter_decoding_context(&dcx, opaque_decoder, |_, opaque_decoder| {
+                Decodable::decode(opaque_decoder)
+            })
+        }).unwrap();
+
+        let mut def_id_and_span_translator = MirDefIdAndSpanTranslator {
+            crate_metadata: cdata,
+            codemap: tcx.sess.codemap(),
+            last_filemap_index_hint: Cell::new(0),
+        };
+
+        def_id_and_span_translator.visit_mir(&mut mir);
+
+        mir
+    });
+
+    struct MirDefIdAndSpanTranslator<'cdata, 'codemap> {
+        crate_metadata: Cmd<'cdata>,
+        codemap: &'codemap codemap::CodeMap,
+        last_filemap_index_hint: Cell<usize>
+    }
+
+    impl<'v, 'cdata, 'codemap> mir::visit::MutVisitor<'v>
+        for MirDefIdAndSpanTranslator<'cdata, 'codemap>
+    {
+        fn visit_def_id(&mut self, def_id: &mut DefId) {
+            *def_id = translate_def_id(self.crate_metadata, *def_id);
+        }
+
+        fn visit_span(&mut self, span: &mut Span) {
+            *span = translate_span(self.crate_metadata,
+                                   self.codemap,
+                                   &self.last_filemap_index_hint,
+                                   *span);
+        }
+    }
+}
+
 fn get_explicit_self(item: rbml::Doc) -> ty::ExplicitSelfCategory {
     fn get_mutability(ch: u8) -> hir::Mutability {
         match ch as char {
@@ -797,12 +897,12 @@ fn get_explicit_self(item: rbml::Doc) -> ty::ExplicitSelfCategory {
 
     let explicit_self_kind = string.as_bytes()[0];
     match explicit_self_kind as char {
-        's' => ty::StaticExplicitSelfCategory,
-        'v' => ty::ByValueExplicitSelfCategory,
-        '~' => ty::ByBoxExplicitSelfCategory,
+        's' => ty::ExplicitSelfCategory::Static,
+        'v' => ty::ExplicitSelfCategory::ByValue,
+        '~' => ty::ExplicitSelfCategory::ByBox,
         // FIXME(#4846) expl. region
         '&' => {
-            ty::ByReferenceExplicitSelfCategory(
+            ty::ExplicitSelfCategory::ByReference(
                 ty::ReEmpty,
                 get_mutability(string.as_bytes()[1]))
         }
@@ -836,7 +936,7 @@ pub fn is_static_method(cdata: Cmd, id: DefIndex) -> bool {
     let doc = cdata.lookup_item(id);
     match item_sort(doc) {
         Some('r') | Some('p') => {
-            get_explicit_self(doc) == ty::StaticExplicitSelfCategory
+            get_explicit_self(doc) == ty::ExplicitSelfCategory::Static
         }
         _ => false
     }
@@ -1216,6 +1316,64 @@ fn reverse_translate_def_id(cdata: Cmd, did: DefId) -> Option<DefId> {
     None
 }
 
+/// Translates a `Span` from an extern crate to the corresponding `Span`
+/// within the local crate's codemap.
+pub fn translate_span(cdata: Cmd,
+                      codemap: &codemap::CodeMap,
+                      last_filemap_index_hint: &Cell<usize>,
+                      span: codemap::Span)
+                      -> codemap::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
+        // translate these values, let's transform them into something we
+        // can handle (and which will produce useful debug locations at
+        // 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)
+    } else {
+        span
+    };
+
+    let imported_filemaps = cdata.imported_filemaps(&codemap);
+    let filemap = {
+        // Optimize for the case that most spans within a translated item
+        // originate from the same filemap.
+        let last_filemap_index = last_filemap_index_hint.get();
+        let last_filemap = &imported_filemaps[last_filemap_index];
+
+        if span.lo >= last_filemap.original_start_pos &&
+           span.lo <= last_filemap.original_end_pos &&
+           span.hi >= last_filemap.original_start_pos &&
+           span.hi <= last_filemap.original_end_pos {
+            last_filemap
+        } else {
+            let mut a = 0;
+            let mut b = imported_filemaps.len();
+
+            while b - a > 1 {
+                let m = (a + b) / 2;
+                if imported_filemaps[m].original_start_pos > span.lo {
+                    b = m;
+                } else {
+                    a = m;
+                }
+            }
+
+            last_filemap_index_hint.set(a);
+            &imported_filemaps[a]
+        }
+    };
+
+    let lo = (span.lo - filemap.original_start_pos) +
+              filemap.translated_filemap.start_pos;
+    let hi = (span.hi - filemap.original_start_pos) +
+              filemap.translated_filemap.start_pos;
+
+    codemap::mk_sp(lo, hi)
+}
+
 pub fn each_inherent_implementation_for_type<F>(cdata: Cmd,
                                                 id: DefIndex,
                                                 mut callback: F)
@@ -1498,7 +1656,9 @@ pub fn get_imported_filemaps(metadata: &[u8]) -> Vec<codemap::FileMap> {
 
     reader::tagged_docs(cm_doc, tag_codemap_filemap).map(|filemap_doc| {
         let mut decoder = reader::Decoder::new(filemap_doc);
-        Decodable::decode(&mut decoder).unwrap()
+        decoder.read_opaque(|opaque_decoder, _| {
+            Decodable::decode(opaque_decoder)
+        }).unwrap()
     }).collect()
 }
 
index 2340efd2cae7ae8d6e84d8102fb1093ad1bf6930..50b9ea575508677520068c8957ec3ada6452d390 100644 (file)
@@ -56,6 +56,20 @@ you want. Example:
 ```
 "##,
 
+E0463: r##"
+A plugin/crate was declared but cannot be found. Erroneous code example:
+
+```
+#![feature(plugin)]
+#![plugin(cookie_monster)] // error: can't find crate for `cookie_monster`
+extern crate cake_is_a_lie; // error: can't find crate for `cake_is_a_lie`
+```
+
+You need to link your code to the relevant crate in order to be able to use it
+(through Cargo or the `-L` option of rustc example). Plugins are crates as
+well, and you link to them the same way.
+"##,
+
 }
 
 register_diagnostics! {
@@ -66,7 +80,6 @@ register_diagnostics! {
     E0460, // found possibly newer version of crate `..`
     E0461, // couldn't find crate `..` with expected target triple ..
     E0462, // found staticlib `..` instead of rlib or dylib
-    E0463, // can't find crate for `..`
     E0464, // multiple matching crates for `..`
     E0465, // multiple .. candidates for `..` found
     E0466, // bad macro import
index 86ab7edf8dda6049f635ca51eba102466c5edf66..ec70a610e0b3afecc7c03e690228f0b5ebad496d 100644 (file)
@@ -19,7 +19,7 @@ use decoder;
 use tyencode;
 use index::{self, IndexData};
 
-use middle::cstore::{LOCAL_CRATE, CrateStore, InlinedItemRef, LinkMeta};
+use middle::cstore::{LOCAL_CRATE, CrateStore, InlinedItemRef, LinkMeta, tls};
 use middle::def;
 use middle::def_id::{CRATE_DEF_INDEX, DefId};
 use middle::dependency_format::Linkage;
@@ -30,6 +30,7 @@ use middle::ty::{self, Ty};
 use rustc::back::svh::Svh;
 use rustc::front::map::{LinkedPath, PathElem, PathElems};
 use rustc::front::map as ast_map;
+use rustc::mir::repr::Mir;
 use rustc::session::config;
 use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet};
 
@@ -43,7 +44,7 @@ use syntax::abi;
 use syntax::ast::{self, NodeId, Name, CRATE_NODE_ID, CrateNum};
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
-use syntax::diagnostic::SpanHandler;
+use syntax::errors::Handler;
 use syntax::parse::token::special_idents;
 use syntax;
 use rbml::writer::Encoder;
@@ -56,7 +57,7 @@ pub type EncodeInlinedItem<'a> =
     Box<FnMut(&EncodeContext, &mut Encoder, InlinedItemRef) + 'a>;
 
 pub struct EncodeParams<'a, 'tcx: 'a> {
-    pub diag: &'a SpanHandler,
+    pub diag: &'a Handler,
     pub tcx: &'a ty::ctxt<'tcx>,
     pub reexports: &'a def::ExportMap,
     pub item_symbols: &'a RefCell<NodeMap<String>>,
@@ -64,10 +65,11 @@ pub struct EncodeParams<'a, 'tcx: 'a> {
     pub cstore: &'a cstore::CStore,
     pub encode_inlined_item: EncodeInlinedItem<'a>,
     pub reachable: &'a NodeSet,
+    pub mir_map: &'a NodeMap<Mir<'tcx>>,
 }
 
 pub struct EncodeContext<'a, 'tcx: 'a> {
-    pub diag: &'a SpanHandler,
+    pub diag: &'a Handler,
     pub tcx: &'a ty::ctxt<'tcx>,
     pub reexports: &'a def::ExportMap,
     pub item_symbols: &'a RefCell<NodeMap<String>>,
@@ -76,6 +78,7 @@ pub struct EncodeContext<'a, 'tcx: 'a> {
     pub encode_inlined_item: RefCell<EncodeInlinedItem<'a>>,
     pub type_abbrevs: tyencode::abbrev_map<'tcx>,
     pub reachable: &'a NodeSet,
+    pub mir_map: &'a NodeMap<Mir<'tcx>>,
 }
 
 impl<'a, 'tcx> EncodeContext<'a,'tcx> {
@@ -137,15 +140,9 @@ fn encode_trait_ref<'a, 'tcx>(rbml_w: &mut Encoder,
                               ecx: &EncodeContext<'a, 'tcx>,
                               trait_ref: ty::TraitRef<'tcx>,
                               tag: usize) {
-    let ty_str_ctxt = &tyencode::ctxt {
-        diag: ecx.diag,
-        ds: def_to_string,
-        tcx: ecx.tcx,
-        abbrevs: &ecx.type_abbrevs
-    };
-
     rbml_w.start_tag(tag);
-    tyencode::enc_trait_ref(rbml_w, ty_str_ctxt, trait_ref);
+    tyencode::enc_trait_ref(rbml_w.writer, &ecx.ty_str_ctxt(), trait_ref);
+    rbml_w.mark_stable_position();
     rbml_w.end_tag();
 }
 
@@ -199,59 +196,19 @@ fn encode_variant_id(rbml_w: &mut Encoder, vid: DefId) {
     rbml_w.wr_tagged_u64(tag_mod_child, id);
 }
 
-pub fn write_closure_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
-                                    rbml_w: &mut Encoder,
-                                    closure_type: &ty::ClosureTy<'tcx>) {
-    let ty_str_ctxt = &tyencode::ctxt {
-        diag: ecx.diag,
-        ds: def_to_string,
-        tcx: ecx.tcx,
-        abbrevs: &ecx.type_abbrevs
-    };
-    tyencode::enc_closure_ty(rbml_w, ty_str_ctxt, closure_type);
-}
-
-pub fn write_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
+fn write_closure_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
                             rbml_w: &mut Encoder,
-                            typ: Ty<'tcx>) {
-    let ty_str_ctxt = &tyencode::ctxt {
-        diag: ecx.diag,
-        ds: def_to_string,
-        tcx: ecx.tcx,
-        abbrevs: &ecx.type_abbrevs
-    };
-    tyencode::enc_ty(rbml_w, ty_str_ctxt, typ);
-}
-
-pub fn write_trait_ref<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
-                                 rbml_w: &mut Encoder,
-                                trait_ref: &ty::TraitRef<'tcx>) {
-    let ty_str_ctxt = &tyencode::ctxt {
-        diag: ecx.diag,
-        ds: def_to_string,
-        tcx: ecx.tcx,
-        abbrevs: &ecx.type_abbrevs
-    };
-    tyencode::enc_trait_ref(rbml_w, ty_str_ctxt, *trait_ref);
-}
-
-pub fn write_region(ecx: &EncodeContext,
-                    rbml_w: &mut Encoder,
-                    r: ty::Region) {
-    let ty_str_ctxt = &tyencode::ctxt {
-        diag: ecx.diag,
-        ds: def_to_string,
-        tcx: ecx.tcx,
-        abbrevs: &ecx.type_abbrevs
-    };
-    tyencode::enc_region(rbml_w, ty_str_ctxt, r);
+                            closure_type: &ty::ClosureTy<'tcx>) {
+    tyencode::enc_closure_ty(rbml_w.writer, &ecx.ty_str_ctxt(), closure_type);
+    rbml_w.mark_stable_position();
 }
 
 fn encode_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
                          rbml_w: &mut Encoder,
                          typ: Ty<'tcx>) {
     rbml_w.start_tag(tag_items_data_item_type);
-    write_type(ecx, rbml_w, typ);
+    tyencode::enc_ty(rbml_w.writer, &ecx.ty_str_ctxt(), typ);
+    rbml_w.mark_stable_position();
     rbml_w.end_tag();
 }
 
@@ -259,7 +216,8 @@ fn encode_region(ecx: &EncodeContext,
                  rbml_w: &mut Encoder,
                  r: ty::Region) {
     rbml_w.start_tag(tag_items_data_region);
-    write_region(ecx, rbml_w, r);
+    tyencode::enc_region(rbml_w.writer, &ecx.ty_str_ctxt(), r);
+    rbml_w.mark_stable_position();
     rbml_w.end_tag();
 }
 
@@ -272,8 +230,7 @@ fn encode_symbol(ecx: &EncodeContext,
             rbml_w.wr_tagged_str(tag_items_data_item_symbol, x);
         }
         None => {
-            ecx.diag.handler().bug(
-                &format!("encode_symbol: id not found {}", id));
+            ecx.diag.bug(&format!("encode_symbol: id not found {}", id));
         }
     }
 }
@@ -328,8 +285,9 @@ fn encode_enum_variant_info<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         rbml_w.start_tag(tag_items_data_item);
         encode_def_id_and_key(ecx, rbml_w, vid);
         encode_family(rbml_w, match variant.kind() {
-            ty::VariantKind::Unit | ty::VariantKind::Tuple => 'v',
-            ty::VariantKind::Struct => 'V'
+            ty::VariantKind::Struct => 'V',
+            ty::VariantKind::Tuple => 'v',
+            ty::VariantKind::Unit => 'w',
         });
         encode_name(rbml_w, variant.name);
         encode_parent_item(rbml_w, ecx.tcx.map.local_def_id(id));
@@ -339,8 +297,10 @@ fn encode_enum_variant_info<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_attributes(rbml_w, &attrs);
         encode_repr_attrs(rbml_w, ecx, &attrs);
 
-        let stab = stability::lookup(ecx.tcx, vid);
+        let stab = stability::lookup_stability(ecx.tcx, vid);
+        let depr = stability::lookup_deprecation(ecx.tcx, vid);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
 
         encode_struct_fields(rbml_w, variant);
 
@@ -447,8 +407,10 @@ fn encode_info_for_mod(ecx: &EncodeContext,
     encode_path(rbml_w, path.clone());
     encode_visibility(rbml_w, vis);
 
-    let stab = stability::lookup(ecx.tcx, ecx.tcx.map.local_def_id(id));
+    let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(id));
+    let depr = stability::lookup_deprecation(ecx.tcx, ecx.tcx.map.local_def_id(id));
     encode_stability(rbml_w, stab);
+    encode_deprecation(rbml_w, depr);
 
     // Encode the reexports of this module, if this module is public.
     if vis == hir::Public {
@@ -492,16 +454,16 @@ fn encode_explicit_self(rbml_w: &mut Encoder,
 
     // Encode the base self type.
     match *explicit_self {
-        ty::StaticExplicitSelfCategory => {
+        ty::ExplicitSelfCategory::Static => {
             rbml_w.wr_tagged_bytes(tag, &['s' as u8]);
         }
-        ty::ByValueExplicitSelfCategory => {
+        ty::ExplicitSelfCategory::ByValue => {
             rbml_w.wr_tagged_bytes(tag, &['v' as u8]);
         }
-        ty::ByBoxExplicitSelfCategory => {
+        ty::ExplicitSelfCategory::ByBox => {
             rbml_w.wr_tagged_bytes(tag, &['~' as u8]);
         }
-        ty::ByReferenceExplicitSelfCategory(_, m) => {
+        ty::ExplicitSelfCategory::ByReference(_, m) => {
             // FIXME(#4846) encode custom lifetime
             let ch = encode_mutability(m);
             rbml_w.wr_tagged_bytes(tag, &['&' as u8, ch]);
@@ -535,8 +497,10 @@ fn encode_field<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
     encode_bounds_and_type_for_item(rbml_w, ecx, index, id);
     encode_def_id_and_key(ecx, rbml_w, field.did);
 
-    let stab = stability::lookup(ecx.tcx, field.did);
+    let stab = stability::lookup_stability(ecx.tcx, field.did);
+    let depr = stability::lookup_deprecation(ecx.tcx, field.did);
     encode_stability(rbml_w, stab);
+    encode_deprecation(rbml_w, depr);
 
     rbml_w.end_tag();
 }
@@ -562,8 +526,10 @@ fn encode_info_for_struct_ctor<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_symbol(ecx, rbml_w, ctor_id);
     }
 
-    let stab = stability::lookup(ecx.tcx, ecx.tcx.map.local_def_id(ctor_id));
+    let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(ctor_id));
+    let depr= stability::lookup_deprecation(ecx.tcx, ecx.tcx.map.local_def_id(ctor_id));
     encode_stability(rbml_w, stab);
+    encode_deprecation(rbml_w, depr);
 
     // indicate that this is a tuple struct ctor, because downstream users will normally want
     // the tuple struct definition, but without this there is no way for them to tell that
@@ -582,17 +548,10 @@ fn encode_generics<'a, 'tcx>(rbml_w: &mut Encoder,
 {
     rbml_w.start_tag(tag);
 
-    // Type parameters
-    let ty_str_ctxt = &tyencode::ctxt {
-        diag: ecx.diag,
-        ds: def_to_string,
-        tcx: ecx.tcx,
-        abbrevs: &ecx.type_abbrevs
-    };
-
     for param in &generics.types {
         rbml_w.start_tag(tag_type_param_def);
-        tyencode::enc_type_param_def(rbml_w, ty_str_ctxt, param);
+        tyencode::enc_type_param_def(rbml_w.writer, &ecx.ty_str_ctxt(), param);
+        rbml_w.mark_stable_position();
         rbml_w.end_tag();
     }
 
@@ -665,7 +624,7 @@ fn encode_method_ty_fields<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
     encode_visibility(rbml_w, method_ty.vis);
     encode_explicit_self(rbml_w, &method_ty.explicit_self);
     match method_ty.explicit_self {
-        ty::StaticExplicitSelfCategory => {
+        ty::ExplicitSelfCategory::Static => {
             encode_family(rbml_w, STATIC_METHOD_FAMILY);
         }
         _ => encode_family(rbml_w, METHOD_FAMILY)
@@ -697,8 +656,10 @@ fn encode_info_for_associated_const<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
     encode_bounds_and_type_for_item(rbml_w, ecx, index,
                                     ecx.local_id(associated_const.def_id));
 
-    let stab = stability::lookup(ecx.tcx, associated_const.def_id);
+    let stab = stability::lookup_stability(ecx.tcx, associated_const.def_id);
+    let depr = stability::lookup_deprecation(ecx.tcx, associated_const.def_id);
     encode_stability(rbml_w, stab);
+    encode_deprecation(rbml_w, depr);
 
     let elem = ast_map::PathName(associated_const.name);
     encode_path(rbml_w, impl_path.chain(Some(elem)));
@@ -732,8 +693,10 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
     encode_parent_item(rbml_w, ecx.tcx.map.local_def_id(parent_id));
     encode_item_sort(rbml_w, 'r');
 
-    let stab = stability::lookup(ecx.tcx, m.def_id);
+    let stab = stability::lookup_stability(ecx.tcx, m.def_id);
+    let depr = stability::lookup_deprecation(ecx.tcx, m.def_id);
     encode_stability(rbml_w, stab);
+    encode_deprecation(rbml_w, depr);
 
     let m_node_id = ecx.local_id(m.def_id);
     encode_bounds_and_type_for_item(rbml_w, ecx, index, m_node_id);
@@ -786,8 +749,10 @@ fn encode_info_for_associated_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
     encode_parent_item(rbml_w, ecx.tcx.map.local_def_id(parent_id));
     encode_item_sort(rbml_w, 't');
 
-    let stab = stability::lookup(ecx.tcx, associated_type.def_id);
+    let stab = stability::lookup_stability(ecx.tcx, associated_type.def_id);
+    let depr = stability::lookup_deprecation(ecx.tcx, associated_type.def_id);
     encode_stability(rbml_w, stab);
+    encode_deprecation(rbml_w, depr);
 
     let elem = ast_map::PathName(associated_type.name);
     encode_path(rbml_w, impl_path.chain(Some(elem)));
@@ -840,7 +805,28 @@ fn encode_inlined_item(ecx: &EncodeContext,
                        ii: InlinedItemRef) {
     let mut eii = ecx.encode_inlined_item.borrow_mut();
     let eii: &mut EncodeInlinedItem = &mut *eii;
-    eii(ecx, rbml_w, ii)
+    eii(ecx, rbml_w, ii);
+
+    let node_id = match ii {
+        InlinedItemRef::Item(item) => item.id,
+        InlinedItemRef::TraitItem(_, trait_item) => trait_item.id,
+        InlinedItemRef::ImplItem(_, impl_item) => impl_item.id,
+        InlinedItemRef::Foreign(foreign_item) => foreign_item.id
+    };
+
+    encode_mir(ecx, rbml_w, node_id);
+}
+
+fn encode_mir(ecx: &EncodeContext, rbml_w: &mut Encoder, node_id: NodeId) {
+    if let Some(mir) = ecx.mir_map.get(&node_id) {
+        rbml_w.start_tag(tag_mir as usize);
+        rbml_w.emit_opaque(|opaque_encoder| {
+            tls::enter_encoding_context(ecx, opaque_encoder, |_, opaque_encoder| {
+                Encodable::encode(mir, opaque_encoder)
+            })
+        }).unwrap();
+        rbml_w.end_tag();
+    }
 }
 
 const FN_FAMILY: char = 'f';
@@ -871,27 +857,29 @@ fn encode_stability(rbml_w: &mut Encoder, stab_opt: Option<&attr::Stability>) {
     });
 }
 
+fn encode_deprecation(rbml_w: &mut Encoder, depr_opt: Option<attr::Deprecation>) {
+    depr_opt.map(|depr| {
+        rbml_w.start_tag(tag_items_data_item_deprecation);
+        depr.encode(rbml_w).unwrap();
+        rbml_w.end_tag();
+    });
+}
+
 fn encode_xrefs<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
                           rbml_w: &mut Encoder,
                           xrefs: FnvHashMap<XRef<'tcx>, u32>)
 {
-    let ty_str_ctxt = &tyencode::ctxt {
-        diag: ecx.diag,
-        ds: def_to_string,
-        tcx: ecx.tcx,
-        abbrevs: &ecx.type_abbrevs
-    };
-
     let mut xref_positions = vec![0; xrefs.len()];
     rbml_w.start_tag(tag_xref_data);
     for (xref, id) in xrefs.into_iter() {
         xref_positions[id as usize] = rbml_w.mark_stable_position() as u32;
         match xref {
             XRef::Predicate(p) => {
-                tyencode::enc_predicate(rbml_w, ty_str_ctxt, &p)
+                tyencode::enc_predicate(rbml_w.writer, &ecx.ty_str_ctxt(), &p)
             }
         }
     }
+    rbml_w.mark_stable_position();
     rbml_w.end_tag();
 
     rbml_w.start_tag(tag_xref_index);
@@ -911,7 +899,8 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
            tcx.sess.codemap().span_to_string(item.span));
 
     let def_id = ecx.tcx.map.local_def_id(item.id);
-    let stab = stability::lookup(tcx, ecx.tcx.map.local_def_id(item.id));
+    let stab = stability::lookup_stability(tcx, ecx.tcx.map.local_def_id(item.id));
+    let depr = stability::lookup_deprecation(tcx, ecx.tcx.map.local_def_id(item.id));
 
     match item.node {
       hir::ItemStatic(_, m, _) => {
@@ -929,6 +918,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_path(rbml_w, path);
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         encode_attributes(rbml_w, &item.attrs);
         rbml_w.end_tag();
       }
@@ -944,6 +934,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_inlined_item(ecx, rbml_w, InlinedItemRef::Item(item));
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         rbml_w.end_tag();
       }
       hir::ItemFn(ref decl, _, constness, _, ref generics, _) => {
@@ -966,6 +957,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_constness(rbml_w, constness);
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         encode_method_argument_names(rbml_w, &**decl);
         rbml_w.end_tag();
       }
@@ -995,6 +987,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         }
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         rbml_w.end_tag();
       }
       hir::ItemTy(..) => {
@@ -1007,6 +1000,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_path(rbml_w, path);
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         rbml_w.end_tag();
       }
       hir::ItemEnum(ref enum_definition, _) => {
@@ -1031,6 +1025,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
 
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         rbml_w.end_tag();
 
         encode_enum_variant_info(ecx,
@@ -1049,7 +1044,11 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         /* Now, make an item for the class itself */
         rbml_w.start_tag(tag_items_data_item);
         encode_def_id_and_key(ecx, rbml_w, def_id);
-        encode_family(rbml_w, 'S');
+        encode_family(rbml_w, match *struct_def {
+            hir::VariantData::Struct(..) => 'S',
+            hir::VariantData::Tuple(..) => 's',
+            hir::VariantData::Unit(..) => 'u',
+        });
         encode_bounds_and_type_for_item(rbml_w, ecx, index, item.id);
 
         encode_item_variances(rbml_w, ecx, item.id);
@@ -1057,6 +1056,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_attributes(rbml_w, &item.attrs);
         encode_path(rbml_w, path.clone());
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         encode_visibility(rbml_w, vis);
         encode_repr_attrs(rbml_w, ecx, &item.attrs);
 
@@ -1147,6 +1147,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         }
         encode_path(rbml_w, path.clone());
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         rbml_w.end_tag();
 
         // Iterate down the trait items, emitting them. We rely on the
@@ -1216,6 +1217,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_attributes(rbml_w, &item.attrs);
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         for &method_def_id in tcx.trait_item_def_ids(def_id).iter() {
             rbml_w.start_tag(tag_item_trait_item);
             match method_def_id {
@@ -1254,8 +1256,10 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
 
             encode_parent_item(rbml_w, def_id);
 
-            let stab = stability::lookup(tcx, item_def_id.def_id());
+            let stab = stability::lookup_stability(tcx, item_def_id.def_id());
+            let depr = stability::lookup_deprecation(tcx, item_def_id.def_id());
             encode_stability(rbml_w, stab);
+            encode_deprecation(rbml_w, depr);
 
             let trait_item_type =
                 tcx.impl_or_trait_item(item_def_id.def_id());
@@ -1287,7 +1291,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
                                 path.clone().chain(Some(elem)));
 
                     match method_ty.explicit_self {
-                        ty::StaticExplicitSelfCategory => {
+                        ty::ExplicitSelfCategory::Static => {
                             encode_family(rbml_w,
                                           STATIC_METHOD_FAMILY);
                         }
@@ -1300,7 +1304,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
                                                     ecx.local_id(method_def_id));
 
                     is_nonstatic_method = method_ty.explicit_self !=
-                        ty::StaticExplicitSelfCategory;
+                        ty::ExplicitSelfCategory::Static;
                 }
                 ty::TypeTraitItem(associated_type) => {
                     encode_name(rbml_w, associated_type.name);
@@ -1387,8 +1391,10 @@ fn encode_info_for_foreign_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
             encode_inlined_item(ecx, rbml_w, InlinedItemRef::Foreign(nitem));
         }
         encode_attributes(rbml_w, &*nitem.attrs);
-        let stab = stability::lookup(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id));
+        let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id));
+        let depr = stability::lookup_deprecation(ecx.tcx, 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_method_argument_names(rbml_w, &*fndecl);
       }
@@ -1400,8 +1406,10 @@ fn encode_info_for_foreign_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         }
         encode_bounds_and_type_for_item(rbml_w, ecx, index, nitem.id);
         encode_attributes(rbml_w, &*nitem.attrs);
-        let stab = stability::lookup(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id));
+        let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id));
+        let depr = stability::lookup_deprecation(ecx.tcx, 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);
       }
@@ -1433,6 +1441,9 @@ fn my_visit_expr(expr: &hir::Expr,
 
             ecx.tcx.map.with_path(expr.id, |path| encode_path(rbml_w, path));
 
+            assert!(ecx.mir_map.contains_key(&expr.id));
+            encode_mir(ecx, rbml_w, expr.id);
+
             rbml_w.end_tag();
         }
         _ => { }
@@ -1693,7 +1704,9 @@ fn encode_codemap(ecx: &EncodeContext, rbml_w: &mut Encoder) {
         }
 
         rbml_w.start_tag(tag_codemap_filemap);
-        filemap.encode(rbml_w);
+        rbml_w.emit_opaque(|opaque_encoder| {
+            filemap.encode(opaque_encoder)
+        }).unwrap();
         rbml_w.end_tag();
     }
 
@@ -1875,8 +1888,37 @@ fn encode_dylib_dependency_formats(rbml_w: &mut Encoder, ecx: &EncodeContext) {
 pub const metadata_encoding_version : &'static [u8] = &[b'r', b'u', b's', b't', 0, 0, 0, 2 ];
 
 pub fn encode_metadata(parms: EncodeParams, krate: &hir::Crate) -> Vec<u8> {
+    let EncodeParams {
+        item_symbols,
+        diag,
+        tcx,
+        reexports,
+        cstore,
+        encode_inlined_item,
+        link_meta,
+        reachable,
+        mir_map,
+        ..
+    } = parms;
+    let ecx = EncodeContext {
+        diag: diag,
+        tcx: tcx,
+        reexports: reexports,
+        item_symbols: item_symbols,
+        link_meta: link_meta,
+        cstore: cstore,
+        encode_inlined_item: RefCell::new(encode_inlined_item),
+        type_abbrevs: RefCell::new(FnvHashMap()),
+        reachable: reachable,
+        mir_map: mir_map,
+    };
+
     let mut wr = Cursor::new(Vec::new());
-    encode_metadata_inner(&mut wr, parms, krate);
+
+    {
+        let mut rbml_w = Encoder::new(&mut wr);
+        encode_metadata_inner(&mut rbml_w, &ecx, krate)
+    }
 
     // RBML compacts the encoded bytes whenever appropriate,
     // so there are some garbages left after the end of the data.
@@ -1911,8 +1953,8 @@ pub fn encode_metadata(parms: EncodeParams, krate: &hir::Crate) -> Vec<u8> {
     return v;
 }
 
-fn encode_metadata_inner(wr: &mut Cursor<Vec<u8>>,
-                         parms: EncodeParams,
+fn encode_metadata_inner(rbml_w: &mut Encoder,
+                         ecx: &EncodeContext,
                          krate: &hir::Crate) {
     struct Stats {
         attr_bytes: u64,
@@ -1946,101 +1988,77 @@ fn encode_metadata_inner(wr: &mut Cursor<Vec<u8>>,
         zero_bytes: 0,
         total_bytes: 0,
     };
-    let EncodeParams {
-        item_symbols,
-        diag,
-        tcx,
-        reexports,
-        cstore,
-        encode_inlined_item,
-        link_meta,
-        reachable,
-        ..
-    } = parms;
-    let ecx = EncodeContext {
-        diag: diag,
-        tcx: tcx,
-        reexports: reexports,
-        item_symbols: item_symbols,
-        link_meta: link_meta,
-        cstore: cstore,
-        encode_inlined_item: RefCell::new(encode_inlined_item),
-        type_abbrevs: RefCell::new(FnvHashMap()),
-        reachable: reachable,
-     };
-
-    let mut rbml_w = Encoder::new(wr);
 
-    encode_rustc_version(&mut rbml_w);
-    encode_crate_name(&mut rbml_w, &ecx.link_meta.crate_name);
-    encode_crate_triple(&mut rbml_w, &tcx.sess.opts.target_triple);
-    encode_hash(&mut rbml_w, &ecx.link_meta.crate_hash);
-    encode_dylib_dependency_formats(&mut rbml_w, &ecx);
+    encode_rustc_version(rbml_w);
+    encode_crate_name(rbml_w, &ecx.link_meta.crate_name);
+    encode_crate_triple(rbml_w, &ecx.tcx.sess.opts.target_triple);
+    encode_hash(rbml_w, &ecx.link_meta.crate_hash);
+    encode_dylib_dependency_formats(rbml_w, &ecx);
 
     let mut i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
-    encode_attributes(&mut rbml_w, &krate.attrs);
+    encode_attributes(rbml_w, &krate.attrs);
     stats.attr_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
 
     i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
-    encode_crate_deps(&mut rbml_w, ecx.cstore);
+    encode_crate_deps(rbml_w, ecx.cstore);
     stats.dep_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
 
     // Encode the language items.
     i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
-    encode_lang_items(&ecx, &mut rbml_w);
+    encode_lang_items(&ecx, rbml_w);
     stats.lang_item_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
 
     // Encode the native libraries used
     i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
-    encode_native_libraries(&ecx, &mut rbml_w);
+    encode_native_libraries(&ecx, rbml_w);
     stats.native_lib_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
 
     // Encode the plugin registrar function
     i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
-    encode_plugin_registrar_fn(&ecx, &mut rbml_w);
+    encode_plugin_registrar_fn(&ecx, rbml_w);
     stats.plugin_registrar_fn_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
 
     // Encode codemap
     i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
-    encode_codemap(&ecx, &mut rbml_w);
+    encode_codemap(&ecx, rbml_w);
     stats.codemap_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
 
     // Encode macro definitions
     i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
-    encode_macro_defs(&mut rbml_w, krate);
+    encode_macro_defs(rbml_w, krate);
     stats.macro_defs_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
 
     // Encode the def IDs of impls, for coherence checking.
     i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
-    encode_impls(&ecx, krate, &mut rbml_w);
+    encode_impls(&ecx, krate, rbml_w);
     stats.impl_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
 
     // Encode miscellaneous info.
     i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
-    encode_misc_info(&ecx, krate, &mut rbml_w);
-    encode_reachable(&ecx, &mut rbml_w);
+    encode_misc_info(&ecx, krate, rbml_w);
+    encode_reachable(&ecx, rbml_w);
     stats.misc_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
 
     // Encode and index the items.
     rbml_w.start_tag(tag_items);
     i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
-    let index = encode_info_for_items(&ecx, &mut rbml_w);
+    let index = encode_info_for_items(&ecx, rbml_w);
     stats.item_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
     rbml_w.end_tag();
 
     i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
-    encode_item_index(&mut rbml_w, index.items);
+    encode_item_index(rbml_w, index.items);
     stats.index_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
 
     i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
-    encode_xrefs(&ecx, &mut rbml_w, index.xrefs);
+    encode_xrefs(&ecx, rbml_w, index.xrefs);
     stats.xref_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
 
-    encode_struct_field_attrs(&ecx, &mut rbml_w, krate);
+    encode_struct_field_attrs(&ecx, rbml_w, krate);
 
     stats.total_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
 
-    if tcx.sess.meta_stats() {
+    if ecx.tcx.sess.meta_stats() {
         for e in rbml_w.writer.get_ref() {
             if *e == 0 {
                 stats.zero_bytes += 1;
@@ -2068,7 +2086,7 @@ fn encode_metadata_inner(wr: &mut Cursor<Vec<u8>>,
 // Get the encoded string for a type
 pub fn encoded_ty<'tcx>(tcx: &ty::ctxt<'tcx>, t: Ty<'tcx>) -> Vec<u8> {
     let mut wr = Cursor::new(Vec::new());
-    tyencode::enc_ty(&mut Encoder::new(&mut wr), &tyencode::ctxt {
+    tyencode::enc_ty(&mut wr, &tyencode::ctxt {
         diag: tcx.sess.diagnostic(),
         ds: def_to_string,
         tcx: tcx,
index d2a6b0eaedf53253cebf7d2a27e1b52599665e89..42332c46969790a4d8974e7aaeabce004b0e27d3 100644 (file)
@@ -8,10 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_metadata"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -58,3 +56,4 @@ pub mod cstore;
 pub mod index;
 pub mod loader;
 pub mod macro_import;
+pub mod tls_context;
index bd9f9c36f631b8ec4aaf467c414377f76b298144..40665beaa5ac2698b08c92e6107ad139cef5ff17 100644 (file)
@@ -226,7 +226,7 @@ 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::diagnostic::SpanHandler;
+use syntax::errors::DiagnosticBuilder;
 use rustc_back::target::Target;
 
 use std::cmp;
@@ -315,38 +315,38 @@ impl<'a> Context<'a> {
             &Some(ref r) => format!(" which `{}` depends on",
                                     r.ident)
         };
-        if !self.rejected_via_hash.is_empty() {
-            span_err!(self.sess, self.span, E0460,
-                      "found possibly newer version of crate `{}`{}",
-                      self.ident, add);
+        let mut err = if !self.rejected_via_hash.is_empty() {
+            struct_span_err!(self.sess, self.span, E0460,
+                             "found possibly newer version of crate `{}`{}",
+                             self.ident, add)
         } else if !self.rejected_via_triple.is_empty() {
-            span_err!(self.sess, self.span, E0461,
-                      "couldn't find crate `{}` with expected target triple {}{}",
-                      self.ident, self.triple, add);
+            struct_span_err!(self.sess, self.span, E0461,
+                             "couldn't find crate `{}` with expected target triple {}{}",
+                             self.ident, self.triple, add)
         } else if !self.rejected_via_kind.is_empty() {
-            span_err!(self.sess, self.span, E0462,
-                      "found staticlib `{}` instead of rlib or dylib{}",
-                      self.ident, add);
+            struct_span_err!(self.sess, self.span, E0462,
+                             "found staticlib `{}` instead of rlib or dylib{}",
+                             self.ident, add)
         } else {
-            span_err!(self.sess, self.span, E0463,
-                      "can't find crate for `{}`{}",
-                      self.ident, add);
-        }
+            struct_span_err!(self.sess, self.span, E0463,
+                             "can't find crate for `{}`{}",
+                             self.ident, add)
+        };
 
         if !self.rejected_via_triple.is_empty() {
             let mismatches = self.rejected_via_triple.iter();
             for (i, &CrateMismatch{ ref path, ref got }) in mismatches.enumerate() {
-                self.sess.fileline_note(self.span,
+                err.fileline_note(self.span,
                     &format!("crate `{}`, path #{}, triple {}: {}",
                             self.ident, i+1, got, path.display()));
             }
         }
         if !self.rejected_via_hash.is_empty() {
-            self.sess.span_note(self.span, "perhaps this crate needs \
+            err.span_note(self.span, "perhaps this crate needs \
                                             to be recompiled?");
             let mismatches = self.rejected_via_hash.iter();
             for (i, &CrateMismatch{ ref path, .. }) in mismatches.enumerate() {
-                self.sess.fileline_note(self.span,
+                err.fileline_note(self.span,
                     &format!("crate `{}` path #{}: {}",
                             self.ident, i+1, path.display()));
             }
@@ -354,7 +354,7 @@ impl<'a> Context<'a> {
                 &None => {}
                 &Some(ref r) => {
                     for (i, path) in r.paths().iter().enumerate() {
-                        self.sess.fileline_note(self.span,
+                        err.fileline_note(self.span,
                             &format!("crate `{}` path #{}: {}",
                                     r.ident, i+1, path.display()));
                     }
@@ -362,15 +362,17 @@ impl<'a> Context<'a> {
             }
         }
         if !self.rejected_via_kind.is_empty() {
-            self.sess.fileline_help(self.span, "please recompile this crate using \
-                                            --crate-type lib");
+            err.fileline_help(self.span, "please recompile this crate using \
+                                          --crate-type lib");
             let mismatches = self.rejected_via_kind.iter();
             for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() {
-                self.sess.fileline_note(self.span,
-                                        &format!("crate `{}` path #{}: {}",
-                                                 self.ident, i+1, path.display()));
+                err.fileline_note(self.span,
+                                  &format!("crate `{}` path #{}: {}",
+                                           self.ident, i+1, path.display()));
             }
         }
+
+        err.emit();
         self.sess.abort_if_errors();
     }
 
@@ -480,29 +482,30 @@ impl<'a> Context<'a> {
             0 => None,
             1 => Some(libraries.into_iter().next().unwrap()),
             _ => {
-                span_err!(self.sess, self.span, E0464,
-                          "multiple matching crates for `{}`",
-                          self.crate_name);
-                self.sess.note("candidates:");
+                let mut err = struct_span_err!(self.sess, self.span, E0464,
+                                               "multiple matching crates for `{}`",
+                                               self.crate_name);
+                err.note("candidates:");
                 for lib in &libraries {
                     match lib.dylib {
                         Some((ref p, _)) => {
-                            self.sess.note(&format!("path: {}",
-                                                   p.display()));
+                            err.note(&format!("path: {}",
+                                              p.display()));
                         }
                         None => {}
                     }
                     match lib.rlib {
                         Some((ref p, _)) => {
-                            self.sess.note(&format!("path: {}",
-                                                    p.display()));
+                            err.note(&format!("path: {}",
+                                              p.display()));
                         }
                         None => {}
                     }
                     let data = lib.metadata.as_slice();
                     let name = decoder::get_crate_name(data);
-                    note_crate_name(self.sess.diagnostic(), &name);
+                    note_crate_name(&mut err, &name);
                 }
+                err.emit();
                 None
             }
         }
@@ -533,6 +536,7 @@ impl<'a> Context<'a> {
             }
         }
 
+        let mut err: Option<DiagnosticBuilder> = None;
         for (lib, kind) in m {
             info!("{} reading metadata from: {}", flavor, lib.display());
             let metadata = match get_metadata_section(self.target, &lib) {
@@ -555,27 +559,37 @@ impl<'a> Context<'a> {
             // candidates have the same hash, so they're not actually
             // duplicates that we should warn about.
             if ret.is_some() && self.hash.is_none() {
-                span_err!(self.sess, self.span, E0465,
-                          "multiple {} candidates for `{}` found",
-                          flavor, self.crate_name);
-                self.sess.span_note(self.span,
-                                    &format!(r"candidate #1: {}",
-                                            ret.as_ref().unwrap().0
-                                               .display()));
+                let mut e = struct_span_err!(self.sess, self.span, E0465,
+                                             "multiple {} candidates for `{}` found",
+                                             flavor, self.crate_name);
+                e.span_note(self.span,
+                            &format!(r"candidate #1: {}",
+                                     ret.as_ref().unwrap().0
+                                        .display()));
+                if let Some(ref mut e) = err {
+                    e.emit();
+                }
+                err = Some(e);
                 error = 1;
                 ret = None;
             }
             if error > 0 {
                 error += 1;
-                self.sess.span_note(self.span,
-                                    &format!(r"candidate #{}: {}", error,
-                                            lib.display()));
+                err.as_mut().unwrap().span_note(self.span,
+                                                &format!(r"candidate #{}: {}", error,
+                                                         lib.display()));
                 continue
             }
             *slot = Some(metadata);
             ret = Some((lib, kind));
         }
-        return if error > 0 {None} else {ret}
+
+        if error > 0 {
+            err.unwrap().emit();
+            None
+        } else {
+            ret
+        }
     }
 
     fn crate_matches(&mut self, crate_data: &[u8], libpath: &Path) -> bool {
@@ -662,8 +676,11 @@ impl<'a> Context<'a> {
                         return true
                     }
                 }
-                sess.err(&format!("extern location for {} is of an unknown type: {}",
-                                 self.crate_name, loc.display()));
+                sess.struct_err(&format!("extern location for {} is of an unknown type: {}",
+                                         self.crate_name, loc.display()))
+                    .help(&format!("file name should be lib*.rlib or {}*.{}",
+                                   dylibname.0, dylibname.1))
+                    .emit();
                 false
             });
 
@@ -697,8 +714,8 @@ impl<'a> Context<'a> {
     }
 }
 
-pub fn note_crate_name(diag: &SpanHandler, name: &str) {
-    diag.handler().note(&format!("crate name: {}", name));
+pub fn note_crate_name(err: &mut DiagnosticBuilder, name: &str) {
+    err.note(&format!("crate name: {}", name));
 }
 
 impl ArchiveMetadata {
diff --git a/src/librustc_metadata/tls_context.rs b/src/librustc_metadata/tls_context.rs
new file mode 100644 (file)
index 0000000..37e661c
--- /dev/null
@@ -0,0 +1,102 @@
+// 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.
+
+// This module provides implementations for the thread-local encoding and
+// decoding context traits in rustc::middle::cstore::tls.
+
+use rbml::opaque::Encoder as OpaqueEncoder;
+use rbml::opaque::Decoder as OpaqueDecoder;
+use rustc::middle::cstore::tls;
+use rustc::middle::def_id::DefId;
+use rustc::middle::subst::Substs;
+use rustc::middle::ty;
+
+use decoder::{self, Cmd};
+use encoder;
+use tydecode::TyDecoder;
+use tyencode;
+
+impl<'a, 'tcx: 'a> tls::EncodingContext<'tcx> for encoder::EncodeContext<'a, 'tcx> {
+
+    fn tcx<'s>(&'s self) -> &'s ty::ctxt<'tcx> {
+        &self.tcx
+    }
+
+    fn encode_ty(&self, encoder: &mut OpaqueEncoder, t: ty::Ty<'tcx>) {
+        tyencode::enc_ty(encoder.cursor, &self.ty_str_ctxt(), t);
+    }
+
+    fn encode_substs(&self, encoder: &mut OpaqueEncoder, substs: &Substs<'tcx>) {
+        tyencode::enc_substs(encoder.cursor, &self.ty_str_ctxt(), substs);
+    }
+}
+
+pub struct DecodingContext<'a, 'tcx: 'a> {
+    pub crate_metadata: Cmd<'a>,
+    pub tcx: &'a ty::ctxt<'tcx>,
+}
+
+impl<'a, 'tcx: 'a> tls::DecodingContext<'tcx> for DecodingContext<'a, 'tcx> {
+
+    fn tcx<'s>(&'s self) -> &'s ty::ctxt<'tcx> {
+        &self.tcx
+    }
+
+    fn decode_ty(&self, decoder: &mut OpaqueDecoder) -> ty::Ty<'tcx> {
+        let def_id_convert = &mut |did| {
+            decoder::translate_def_id(self.crate_metadata, did)
+        };
+
+        let starting_position = decoder.position();
+
+        let mut ty_decoder = TyDecoder::new(
+            self.crate_metadata.data.as_slice(),
+            self.crate_metadata.cnum,
+            starting_position,
+            self.tcx,
+            def_id_convert);
+
+        let ty = ty_decoder.parse_ty();
+
+        let end_position = ty_decoder.position();
+
+        // We can just reuse the tydecode implementation for parsing types, but
+        // we have to make sure to leave the rbml reader at the position just
+        // after the type.
+        decoder.advance(end_position - starting_position);
+        ty
+    }
+
+    fn decode_substs(&self, decoder: &mut OpaqueDecoder) -> Substs<'tcx> {
+        let def_id_convert = &mut |did| {
+            decoder::translate_def_id(self.crate_metadata, did)
+        };
+
+        let starting_position = decoder.position();
+
+        let mut ty_decoder = TyDecoder::new(
+            self.crate_metadata.data.as_slice(),
+            self.crate_metadata.cnum,
+            starting_position,
+            self.tcx,
+            def_id_convert);
+
+        let substs = ty_decoder.parse_substs();
+
+        let end_position = ty_decoder.position();
+
+        decoder.advance(end_position - starting_position);
+        substs
+    }
+
+    fn translate_def_id(&self, def_id: DefId) -> DefId {
+        decoder::translate_def_id(self.crate_metadata, def_id)
+    }
+}
index 34f289456bbdea7bf01e0597900be72961388146..5a48d6019d69937b455ea860d760ed43e10e048f 100644 (file)
@@ -22,9 +22,10 @@ use middle::def_id::{DefId, DefIndex};
 use middle::region;
 use middle::subst;
 use middle::subst::VecPerParamSpace;
-use middle::ty::{self, ToPredicate, Ty, HasTypeFlags};
+use middle::ty::{self, ToPredicate, Ty, TypeFoldable};
 
 use rbml;
+use rbml::leb128;
 use std::str;
 use syntax::abi;
 use syntax::ast;
@@ -68,6 +69,10 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
         }
     }
 
+    pub fn position(&self) -> usize {
+        self.pos
+    }
+
     fn peek(&self) -> char {
         self.data[self.pos] as char
     }
@@ -99,9 +104,10 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
     }
 
     fn parse_vuint(&mut self) -> usize {
-        let res = rbml::reader::vuint_at(self.data, self.pos).unwrap();
-        self.pos = res.next;
-        res.val
+        let (value, bytes_read) = leb128::read_unsigned_leb128(self.data,
+                                                               self.pos);
+        self.pos += bytes_read;
+        value as usize
     }
 
     fn parse_name(&mut self, last: char) -> ast::Name {
@@ -188,14 +194,12 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
             }
             'B' => {
                 assert_eq!(self.next(), '[');
-                let def_id = self.parse_def();
                 let space = self.parse_param_space();
                 assert_eq!(self.next(), '|');
                 let index = self.parse_u32();
                 assert_eq!(self.next(), '|');
                 let name = token::intern(&self.parse_str(']'));
                 ty::ReEarlyBound(ty::EarlyBoundRegion {
-                    def_id: def_id,
                     space: space,
                     index: index,
                     name: name
@@ -233,6 +237,17 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
             // doesn't care about regions.
             //
             // May still be worth fixing though.
+            'C' => {
+                assert_eq!(self.next(), '[');
+                let fn_id = self.parse_uint() as ast::NodeId;
+                assert_eq!(self.next(), '|');
+                let body_id = self.parse_uint() as ast::NodeId;
+                assert_eq!(self.next(), ']');
+                region::CodeExtentData::CallSiteScope {
+                    fn_id: fn_id, body_id: body_id
+                }
+            }
+            // This creates scopes with the wrong NodeId. (See note above.)
             'P' => {
                 assert_eq!(self.next(), '[');
                 let fn_id = self.parse_uint() as ast::NodeId;
index bc1edd5c7671815d8fbb0ab473106ab95e6ee57a..f03c25d698feb927020b084d3af9f2a737b9198a 100644 (file)
@@ -29,14 +29,13 @@ use rustc_front::hir;
 
 use syntax::abi::Abi;
 use syntax::ast;
-use syntax::diagnostic::SpanHandler;
+use syntax::errors::Handler;
 
-use rbml::writer::{self, Encoder};
-
-macro_rules! mywrite { ($w:expr, $($arg:tt)*) => ({ write!($w.writer, $($arg)*); }) }
+use rbml::leb128;
+use encoder;
 
 pub struct ctxt<'a, 'tcx: 'a> {
-    pub diag: &'a SpanHandler,
+    pub diag: &'a Handler,
     // Def -> str Callback:
     pub ds: fn(DefId) -> String,
     // The type context.
@@ -44,6 +43,17 @@ pub struct ctxt<'a, 'tcx: 'a> {
     pub abbrevs: &'a abbrev_map<'tcx>
 }
 
+impl<'a, 'tcx> encoder::EncodeContext<'a, 'tcx> {
+    pub fn ty_str_ctxt<'b>(&'b self) -> ctxt<'b, 'tcx> {
+        ctxt {
+            diag: self.tcx.sess.diagnostic(),
+            ds: encoder::def_to_string,
+            tcx: self.tcx,
+            abbrevs: &self.type_abbrevs
+        }
+    }
+}
+
 // Compact string representation for Ty values. API TyStr & parse_from_str.
 // Extra parameters are for converting to/from def_ids in the string rep.
 // Whatever format you choose should not contain pipe characters.
@@ -53,125 +63,129 @@ pub struct ty_abbrev {
 
 pub type abbrev_map<'tcx> = RefCell<FnvHashMap<Ty<'tcx>, ty_abbrev>>;
 
-pub fn enc_ty<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx>) {
+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.writer.write_all(&a.s); return; }
+        Some(a) => { w.write_all(&a.s); return; }
         None => {}
     }
 
-    // type abbreviations needs a stable position
-    let pos = w.mark_stable_position();
+    let pos = w.position();
 
     match t.sty {
-        ty::TyBool => mywrite!(w, "b"),
-        ty::TyChar => mywrite!(w, "c"),
+        ty::TyBool => { write!(w, "b"); }
+        ty::TyChar => { write!(w, "c"); }
         ty::TyInt(t) => {
             match t {
-                ast::TyIs => mywrite!(w, "is"),
-                ast::TyI8 => mywrite!(w, "MB"),
-                ast::TyI16 => mywrite!(w, "MW"),
-                ast::TyI32 => mywrite!(w, "ML"),
-                ast::TyI64 => mywrite!(w, "MD")
-            }
+                ast::TyIs => write!(w, "is"),
+                ast::TyI8 => write!(w, "MB"),
+                ast::TyI16 => write!(w, "MW"),
+                ast::TyI32 => write!(w, "ML"),
+                ast::TyI64 => write!(w, "MD")
+            };
         }
         ty::TyUint(t) => {
             match t {
-                ast::TyUs => mywrite!(w, "us"),
-                ast::TyU8 => mywrite!(w, "Mb"),
-                ast::TyU16 => mywrite!(w, "Mw"),
-                ast::TyU32 => mywrite!(w, "Ml"),
-                ast::TyU64 => mywrite!(w, "Md")
-            }
+                ast::TyUs => write!(w, "us"),
+                ast::TyU8 => write!(w, "Mb"),
+                ast::TyU16 => write!(w, "Mw"),
+                ast::TyU32 => write!(w, "Ml"),
+                ast::TyU64 => write!(w, "Md")
+            };
         }
         ty::TyFloat(t) => {
             match t {
-                ast::TyF32 => mywrite!(w, "Mf"),
-                ast::TyF64 => mywrite!(w, "MF"),
-            }
+                ast::TyF32 => write!(w, "Mf"),
+                ast::TyF64 => write!(w, "MF"),
+            };
         }
         ty::TyEnum(def, substs) => {
-            mywrite!(w, "t[{}|", (cx.ds)(def.did));
+            write!(w, "t[{}|", (cx.ds)(def.did));
             enc_substs(w, cx, substs);
-            mywrite!(w, "]");
+            write!(w, "]");
         }
         ty::TyTrait(box ty::TraitTy { ref principal,
                                        ref bounds }) => {
-            mywrite!(w, "x[");
+            write!(w, "x[");
             enc_trait_ref(w, cx, principal.0);
             enc_existential_bounds(w, cx, bounds);
-            mywrite!(w, "]");
+            write!(w, "]");
         }
         ty::TyTuple(ref ts) => {
-            mywrite!(w, "T[");
+            write!(w, "T[");
             for t in ts { enc_ty(w, cx, *t); }
-            mywrite!(w, "]");
+            write!(w, "]");
         }
-        ty::TyBox(typ) => { mywrite!(w, "~"); enc_ty(w, cx, typ); }
-        ty::TyRawPtr(mt) => { mywrite!(w, "*"); enc_mt(w, cx, mt); }
+        ty::TyBox(typ) => { write!(w, "~"); enc_ty(w, cx, typ); }
+        ty::TyRawPtr(mt) => { write!(w, "*"); enc_mt(w, cx, mt); }
         ty::TyRef(r, mt) => {
-            mywrite!(w, "&");
+            write!(w, "&");
             enc_region(w, cx, *r);
             enc_mt(w, cx, mt);
         }
         ty::TyArray(t, sz) => {
-            mywrite!(w, "V");
+            write!(w, "V");
             enc_ty(w, cx, t);
-            mywrite!(w, "/{}|", sz);
+            write!(w, "/{}|", sz);
         }
         ty::TySlice(t) => {
-            mywrite!(w, "V");
+            write!(w, "V");
             enc_ty(w, cx, t);
-            mywrite!(w, "/|");
+            write!(w, "/|");
         }
         ty::TyStr => {
-            mywrite!(w, "v");
+            write!(w, "v");
         }
         ty::TyBareFn(Some(def_id), f) => {
-            mywrite!(w, "F");
-            mywrite!(w, "{}|", (cx.ds)(def_id));
+            write!(w, "F");
+            write!(w, "{}|", (cx.ds)(def_id));
             enc_bare_fn_ty(w, cx, f);
         }
         ty::TyBareFn(None, f) => {
-            mywrite!(w, "G");
+            write!(w, "G");
             enc_bare_fn_ty(w, cx, f);
         }
         ty::TyInfer(_) => {
-            cx.diag.handler().bug("cannot encode inference variable types");
+            cx.diag.bug("cannot encode inference variable types");
         }
         ty::TyParam(ParamTy {space, idx, name}) => {
-            mywrite!(w, "p[{}|{}|{}]", idx, space.to_uint(), name)
+            write!(w, "p[{}|{}|{}]", idx, space.to_uint(), name);
         }
         ty::TyStruct(def, substs) => {
-            mywrite!(w, "a[{}|", (cx.ds)(def.did));
+            write!(w, "a[{}|", (cx.ds)(def.did));
             enc_substs(w, cx, substs);
-            mywrite!(w, "]");
+            write!(w, "]");
         }
         ty::TyClosure(def, ref substs) => {
-            mywrite!(w, "k[{}|", (cx.ds)(def));
+            write!(w, "k[{}|", (cx.ds)(def));
             enc_substs(w, cx, &substs.func_substs);
             for ty in &substs.upvar_tys {
                 enc_ty(w, cx, ty);
             }
-            mywrite!(w, ".");
-            mywrite!(w, "]");
+            write!(w, ".");
+            write!(w, "]");
         }
         ty::TyProjection(ref data) => {
-            mywrite!(w, "P[");
+            write!(w, "P[");
             enc_trait_ref(w, cx, data.trait_ref);
-            mywrite!(w, "{}]", data.item_name);
+            write!(w, "{}]", data.item_name);
         }
         ty::TyError => {
-            mywrite!(w, "e");
+            write!(w, "e");
         }
     }
 
-    let end = w.mark_stable_position();
+    let end = w.position();
     let len = end - pos;
 
-    let buf: &mut [u8] = &mut [0; 16]; // vuint < 15 bytes
-    let mut abbrev = Cursor::new(buf);
+    let mut abbrev = Cursor::new(Vec::with_capacity(16));
     abbrev.write_all(b"#");
-    writer::write_vuint(&mut abbrev, pos as usize);
+    {
+        let start_position = abbrev.position() as usize;
+        let bytes_written = leb128::write_unsigned_leb128(abbrev.get_mut(),
+                                                          start_position,
+                                                          pos);
+        abbrev.set_position((start_position + bytes_written) as u64);
+    }
 
     cx.abbrevs.borrow_mut().insert(t, ty_abbrev {
         s: if abbrev.position() < len {
@@ -180,209 +194,214 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx>) {
             // if the abbreviation is longer than the real type,
             // don't use #-notation. However, insert it here so
             // other won't have to `mark_stable_position`
-            w.writer.get_ref()[pos as usize..end as usize].to_owned()
+            w.get_ref()[pos as usize .. end as usize].to_owned()
         }
     });
 }
 
-fn enc_mutability(w: &mut Encoder, mt: hir::Mutability) {
+fn enc_mutability(w: &mut Cursor<Vec<u8>>, mt: hir::Mutability) {
     match mt {
         hir::MutImmutable => (),
-        hir::MutMutable => mywrite!(w, "m"),
-    }
+        hir::MutMutable => {
+            write!(w, "m");
+        }
+    };
 }
 
-fn enc_mt<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>,
+fn enc_mt<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>,
                     mt: ty::TypeAndMut<'tcx>) {
     enc_mutability(w, mt.mutbl);
     enc_ty(w, cx, mt.ty);
 }
 
-fn enc_opt<T, F>(w: &mut Encoder, t: Option<T>, enc_f: F) where
-    F: FnOnce(&mut Encoder, T),
+fn enc_opt<T, F>(w: &mut Cursor<Vec<u8>>, t: Option<T>, enc_f: F) where
+    F: FnOnce(&mut Cursor<Vec<u8>>, T),
 {
     match t {
-        None => mywrite!(w, "n"),
+        None => {
+            write!(w, "n");
+        }
         Some(v) => {
-            mywrite!(w, "s");
+            write!(w, "s");
             enc_f(w, v);
         }
     }
 }
 
-fn enc_vec_per_param_space<'a, 'tcx, T, F>(w: &mut Encoder,
+fn enc_vec_per_param_space<'a, 'tcx, T, F>(w: &mut Cursor<Vec<u8>>,
                                            cx: &ctxt<'a, 'tcx>,
                                            v: &VecPerParamSpace<T>,
                                            mut op: F) where
-    F: FnMut(&mut Encoder, &ctxt<'a, 'tcx>, &T),
+    F: FnMut(&mut Cursor<Vec<u8>>, &ctxt<'a, 'tcx>, &T),
 {
     for &space in &subst::ParamSpace::all() {
-        mywrite!(w, "[");
+        write!(w, "[");
         for t in v.get_slice(space) {
             op(w, cx, t);
         }
-        mywrite!(w, "]");
+        write!(w, "]");
     }
 }
 
-pub fn enc_substs<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>,
+pub fn enc_substs<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>,
                             substs: &subst::Substs<'tcx>) {
     enc_region_substs(w, cx, &substs.regions);
     enc_vec_per_param_space(w, cx, &substs.types,
                             |w, cx, &ty| enc_ty(w, cx, ty));
 }
 
-fn enc_region_substs(w: &mut Encoder, cx: &ctxt, substs: &subst::RegionSubsts) {
+fn enc_region_substs(w: &mut Cursor<Vec<u8>>, cx: &ctxt, substs: &subst::RegionSubsts) {
     match *substs {
         subst::ErasedRegions => {
-            mywrite!(w, "e");
+            write!(w, "e");
         }
         subst::NonerasedRegions(ref regions) => {
-            mywrite!(w, "n");
+            write!(w, "n");
             enc_vec_per_param_space(w, cx, regions,
                                     |w, cx, &r| enc_region(w, cx, r));
         }
     }
 }
 
-pub fn enc_region(w: &mut Encoder, cx: &ctxt, r: ty::Region) {
+pub fn enc_region(w: &mut Cursor<Vec<u8>>, cx: &ctxt, r: ty::Region) {
     match r {
         ty::ReLateBound(id, br) => {
-            mywrite!(w, "b[{}|", id.depth);
+            write!(w, "b[{}|", id.depth);
             enc_bound_region(w, cx, br);
-            mywrite!(w, "]");
+            write!(w, "]");
         }
         ty::ReEarlyBound(ref data) => {
-            mywrite!(w, "B[{}|{}|{}|{}]",
-                     (cx.ds)(data.def_id),
-                     data.space.to_uint(),
-                     data.index,
-                     data.name);
+            write!(w, "B[{}|{}|{}]",
+                   data.space.to_uint(),
+                   data.index,
+                   data.name);
         }
         ty::ReFree(ref fr) => {
-            mywrite!(w, "f[");
+            write!(w, "f[");
             enc_scope(w, cx, fr.scope);
-            mywrite!(w, "|");
+            write!(w, "|");
             enc_bound_region(w, cx, fr.bound_region);
-            mywrite!(w, "]");
+            write!(w, "]");
         }
         ty::ReScope(scope) => {
-            mywrite!(w, "s");
+            write!(w, "s");
             enc_scope(w, cx, scope);
-            mywrite!(w, "|");
+            write!(w, "|");
         }
         ty::ReStatic => {
-            mywrite!(w, "t");
+            write!(w, "t");
         }
         ty::ReEmpty => {
-            mywrite!(w, "e");
+            write!(w, "e");
         }
         ty::ReVar(_) | ty::ReSkolemized(..) => {
             // these should not crop up after typeck
-            cx.diag.handler().bug("cannot encode region variables");
+            cx.diag.bug("cannot encode region variables");
         }
     }
 }
 
-fn enc_scope(w: &mut Encoder, cx: &ctxt, scope: region::CodeExtent) {
+fn enc_scope(w: &mut Cursor<Vec<u8>>, cx: &ctxt, scope: region::CodeExtent) {
     match cx.tcx.region_maps.code_extent_data(scope) {
+        region::CodeExtentData::CallSiteScope {
+            fn_id, body_id } => write!(w, "C[{}|{}]", fn_id, body_id),
         region::CodeExtentData::ParameterScope {
-            fn_id, body_id } => mywrite!(w, "P[{}|{}]", fn_id, body_id),
-        region::CodeExtentData::Misc(node_id) => mywrite!(w, "M{}", node_id),
+            fn_id, body_id } => write!(w, "P[{}|{}]", fn_id, body_id),
+        region::CodeExtentData::Misc(node_id) => write!(w, "M{}", node_id),
         region::CodeExtentData::Remainder(region::BlockRemainder {
-            block: b, first_statement_index: i }) => mywrite!(w, "B[{}|{}]", b, i),
-        region::CodeExtentData::DestructionScope(node_id) => mywrite!(w, "D{}", node_id),
-    }
+            block: b, first_statement_index: i }) => write!(w, "B[{}|{}]", b, i),
+        region::CodeExtentData::DestructionScope(node_id) => write!(w, "D{}", node_id),
+    };
 }
 
-fn enc_bound_region(w: &mut Encoder, cx: &ctxt, br: ty::BoundRegion) {
+fn enc_bound_region(w: &mut Cursor<Vec<u8>>, cx: &ctxt, br: ty::BoundRegion) {
     match br {
         ty::BrAnon(idx) => {
-            mywrite!(w, "a{}|", idx);
+            write!(w, "a{}|", idx);
         }
         ty::BrNamed(d, name) => {
-            mywrite!(w, "[{}|{}]",
+            write!(w, "[{}|{}]",
                      (cx.ds)(d),
                      name);
         }
         ty::BrFresh(id) => {
-            mywrite!(w, "f{}|", id);
+            write!(w, "f{}|", id);
         }
         ty::BrEnv => {
-            mywrite!(w, "e|");
+            write!(w, "e|");
         }
     }
 }
 
-pub fn enc_trait_ref<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>,
+pub fn enc_trait_ref<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>,
                                s: ty::TraitRef<'tcx>) {
-    mywrite!(w, "{}|", (cx.ds)(s.def_id));
+    write!(w, "{}|", (cx.ds)(s.def_id));
     enc_substs(w, cx, s.substs);
 }
 
-fn enc_unsafety(w: &mut Encoder, p: hir::Unsafety) {
+fn enc_unsafety(w: &mut Cursor<Vec<u8>>, p: hir::Unsafety) {
     match p {
-        hir::Unsafety::Normal => mywrite!(w, "n"),
-        hir::Unsafety::Unsafe => mywrite!(w, "u"),
-    }
+        hir::Unsafety::Normal => write!(w, "n"),
+        hir::Unsafety::Unsafe => write!(w, "u"),
+    };
 }
 
-fn enc_abi(w: &mut Encoder, abi: Abi) {
-    mywrite!(w, "[");
-    mywrite!(w, "{}", abi.name());
-    mywrite!(w, "]")
+fn enc_abi(w: &mut Cursor<Vec<u8>>, abi: Abi) {
+    write!(w, "[");
+    write!(w, "{}", abi.name());
+    write!(w, "]");
 }
 
-pub fn enc_bare_fn_ty<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>,
+pub fn enc_bare_fn_ty<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>,
                                 ft: &ty::BareFnTy<'tcx>) {
     enc_unsafety(w, ft.unsafety);
     enc_abi(w, ft.abi);
     enc_fn_sig(w, cx, &ft.sig);
 }
 
-pub fn enc_closure_ty<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>,
+pub fn enc_closure_ty<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>,
                                 ft: &ty::ClosureTy<'tcx>) {
     enc_unsafety(w, ft.unsafety);
     enc_fn_sig(w, cx, &ft.sig);
     enc_abi(w, ft.abi);
 }
 
-fn enc_fn_sig<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>,
+fn enc_fn_sig<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>,
                         fsig: &ty::PolyFnSig<'tcx>) {
-    mywrite!(w, "[");
+    write!(w, "[");
     for ty in &fsig.0.inputs {
         enc_ty(w, cx, *ty);
     }
-    mywrite!(w, "]");
+    write!(w, "]");
     if fsig.0.variadic {
-        mywrite!(w, "V");
+        write!(w, "V");
     } else {
-        mywrite!(w, "N");
+        write!(w, "N");
     }
     match fsig.0.output {
         ty::FnConverging(result_type) => {
             enc_ty(w, cx, result_type);
         }
         ty::FnDiverging => {
-            mywrite!(w, "z");
+            write!(w, "z");
         }
     }
 }
 
-pub fn enc_builtin_bounds(w: &mut Encoder, _cx: &ctxt, bs: &ty::BuiltinBounds) {
+pub fn enc_builtin_bounds(w: &mut Cursor<Vec<u8>>, _cx: &ctxt, bs: &ty::BuiltinBounds) {
     for bound in bs {
         match bound {
-            ty::BoundSend => mywrite!(w, "S"),
-            ty::BoundSized => mywrite!(w, "Z"),
-            ty::BoundCopy => mywrite!(w, "P"),
-            ty::BoundSync => mywrite!(w, "T"),
-        }
+            ty::BoundSend => write!(w, "S"),
+            ty::BoundSized => write!(w, "Z"),
+            ty::BoundCopy => write!(w, "P"),
+            ty::BoundSync => write!(w, "T"),
+        };
     }
 
-    mywrite!(w, ".");
+    write!(w, ".");
 }
 
-pub fn enc_existential_bounds<'a,'tcx>(w: &mut Encoder,
+pub fn enc_existential_bounds<'a,'tcx>(w: &mut Cursor<Vec<u8>>,
                                        cx: &ctxt<'a,'tcx>,
                                        bs: &ty::ExistentialBounds<'tcx>) {
     enc_builtin_bounds(w, cx, &bs.builtin_bounds);
@@ -390,90 +409,94 @@ pub fn enc_existential_bounds<'a,'tcx>(w: &mut Encoder,
     enc_region(w, cx, bs.region_bound);
 
     for tp in &bs.projection_bounds {
-        mywrite!(w, "P");
+        write!(w, "P");
         enc_projection_predicate(w, cx, &tp.0);
     }
 
-    mywrite!(w, ".");
+    write!(w, ".");
 }
 
-pub fn enc_type_param_def<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>,
+pub fn enc_type_param_def<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>,
                                     v: &ty::TypeParameterDef<'tcx>) {
-    mywrite!(w, "{}:{}|{}|{}|{}|",
+    write!(w, "{}:{}|{}|{}|{}|",
              v.name, (cx.ds)(v.def_id),
              v.space.to_uint(), v.index, (cx.ds)(v.default_def_id));
     enc_opt(w, v.default, |w, t| enc_ty(w, cx, t));
     enc_object_lifetime_default(w, cx, v.object_lifetime_default);
 }
 
-pub fn enc_region_param_def(w: &mut Encoder, cx: &ctxt,
+pub fn enc_region_param_def(w: &mut Cursor<Vec<u8>>, cx: &ctxt,
                             v: &ty::RegionParameterDef) {
-    mywrite!(w, "{}:{}|{}|{}|",
+    write!(w, "{}:{}|{}|{}|",
              v.name, (cx.ds)(v.def_id),
              v.space.to_uint(), v.index);
     for &r in &v.bounds {
-        mywrite!(w, "R");
+        write!(w, "R");
         enc_region(w, cx, r);
     }
-    mywrite!(w, ".");
+    write!(w, ".");
 }
 
-fn enc_object_lifetime_default<'a, 'tcx>(w: &mut Encoder,
+fn enc_object_lifetime_default<'a, 'tcx>(w: &mut Cursor<Vec<u8>>,
                                          cx: &ctxt<'a, 'tcx>,
                                          default: ty::ObjectLifetimeDefault)
 {
     match default {
-        ty::ObjectLifetimeDefault::Ambiguous => mywrite!(w, "a"),
-        ty::ObjectLifetimeDefault::BaseDefault => mywrite!(w, "b"),
+        ty::ObjectLifetimeDefault::Ambiguous => {
+            write!(w, "a");
+        }
+        ty::ObjectLifetimeDefault::BaseDefault => {
+            write!(w, "b");
+        }
         ty::ObjectLifetimeDefault::Specific(r) => {
-            mywrite!(w, "s");
+            write!(w, "s");
             enc_region(w, cx, r);
         }
     }
 }
 
-pub fn enc_predicate<'a, 'tcx>(w: &mut Encoder,
+pub fn enc_predicate<'a, 'tcx>(w: &mut Cursor<Vec<u8>>,
                                cx: &ctxt<'a, 'tcx>,
                                p: &ty::Predicate<'tcx>)
 {
     match *p {
         ty::Predicate::Trait(ref trait_ref) => {
-            mywrite!(w, "t");
+            write!(w, "t");
             enc_trait_ref(w, cx, trait_ref.0.trait_ref);
         }
         ty::Predicate::Equate(ty::Binder(ty::EquatePredicate(a, b))) => {
-            mywrite!(w, "e");
+            write!(w, "e");
             enc_ty(w, cx, a);
             enc_ty(w, cx, b);
         }
         ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(a, b))) => {
-            mywrite!(w, "r");
+            write!(w, "r");
             enc_region(w, cx, a);
             enc_region(w, cx, b);
         }
         ty::Predicate::TypeOutlives(ty::Binder(ty::OutlivesPredicate(a, b))) => {
-            mywrite!(w, "o");
+            write!(w, "o");
             enc_ty(w, cx, a);
             enc_region(w, cx, b);
         }
         ty::Predicate::Projection(ty::Binder(ref data)) => {
-            mywrite!(w, "p");
-            enc_projection_predicate(w, cx, data)
+            write!(w, "p");
+            enc_projection_predicate(w, cx, data);
         }
         ty::Predicate::WellFormed(data) => {
-            mywrite!(w, "w");
+            write!(w, "w");
             enc_ty(w, cx, data);
         }
         ty::Predicate::ObjectSafe(trait_def_id) => {
-            mywrite!(w, "O{}|", (cx.ds)(trait_def_id));
+            write!(w, "O{}|", (cx.ds)(trait_def_id));
         }
     }
 }
 
-fn enc_projection_predicate<'a, 'tcx>(w: &mut Encoder,
+fn enc_projection_predicate<'a, 'tcx>(w: &mut Cursor<Vec<u8>>,
                                       cx: &ctxt<'a, 'tcx>,
                                       data: &ty::ProjectionPredicate<'tcx>) {
     enc_trait_ref(w, cx, data.projection_ty.trait_ref);
-    mywrite!(w, "{}|", data.projection_ty.item_name);
+    write!(w, "{}|", data.projection_ty.item_name);
     enc_ty(w, cx, data.ty);
 }
index 5f7f87cb86234ecbe7eb73c5dda309b693d716ec..12b9130b48c612c9081914e2af66f6597a819951 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use build::{BlockAnd, Builder};
+use build::{BlockAnd, BlockAndExtension, Builder};
 use hair::*;
 use rustc::mir::repr::*;
 use rustc_front::hir;
@@ -19,11 +19,16 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                      mut block: BasicBlock,
                      ast_block: &'tcx hir::Block)
                      -> BlockAnd<()> {
-        let this = self;
-        let Block { extent, span: _, stmts, expr } = this.hir.mirror(ast_block);
-        this.in_scope(extent, block, |this| {
+        let Block { extent, span, stmts, expr } = self.hir.mirror(ast_block);
+        self.in_scope(extent, block, move |this| {
             unpack!(block = this.stmts(block, stmts));
-            this.into(destination, block, expr)
+            match expr {
+                Some(expr) => this.into(destination, block, expr),
+                None => {
+                    this.cfg.push_assign_unit(block, span, destination);
+                    block.unit()
+                }
+            }
         })
     }
 }
index d28724c30aaa0b79730f8dbc7ef375bb11cc3760..523ac85cdc5090414e3d93f812ec1669380d6fd3 100644 (file)
@@ -28,7 +28,7 @@ impl<'tcx> CFG<'tcx> {
 
     pub fn start_new_block(&mut self) -> BasicBlock {
         let node_index = self.basic_blocks.len();
-        self.basic_blocks.push(BasicBlockData::new(Terminator::Diverge));
+        self.basic_blocks.push(BasicBlockData::new(None));
         BasicBlock::new(node_index)
     }
 
@@ -37,14 +37,6 @@ impl<'tcx> CFG<'tcx> {
         self.block_data_mut(block).statements.push(statement);
     }
 
-    pub fn push_assign_constant(&mut self,
-                                block: BasicBlock,
-                                span: Span,
-                                temp: &Lvalue<'tcx>,
-                                constant: Constant<'tcx>) {
-        self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant)));
-    }
-
     pub fn push_drop(&mut self, block: BasicBlock, span: Span,
                      kind: DropKind, lvalue: &Lvalue<'tcx>) {
         self.push(block, Statement {
@@ -64,18 +56,29 @@ impl<'tcx> CFG<'tcx> {
         });
     }
 
+    pub fn push_assign_constant(&mut self,
+                                block: BasicBlock,
+                                span: Span,
+                                temp: &Lvalue<'tcx>,
+                                constant: Constant<'tcx>) {
+        self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant)));
+    }
+
+    pub fn push_assign_unit(&mut self,
+                            block: BasicBlock,
+                            span: Span,
+                            lvalue: &Lvalue<'tcx>) {
+        self.push_assign(block, span, lvalue, Rvalue::Aggregate(
+            AggregateKind::Tuple, vec![]
+        ));
+    }
+
     pub fn terminate(&mut self,
                      block: BasicBlock,
                      terminator: Terminator<'tcx>) {
-        // Check whether this block has already been terminated. For
-        // this, we rely on the fact that the initial state is to have
-        // a Diverge terminator and an empty list of targets (which
-        // is not a valid state).
-        debug_assert!(match self.block_data(block).terminator { Terminator::Diverge => true,
-                                                                _ => false },
+        debug_assert!(self.block_data(block).terminator.is_none(),
                       "terminate: block {:?} already has a terminator set", block);
-
-        self.block_data_mut(block).terminator = terminator;
+        self.block_data_mut(block).terminator = Some(terminator);
     }
 }
 
index 697799efd143bee9343acd9738413045bb7470f9..4e03ed489eb9fcf93bcf9ca1828c521b3a42ac3d 100644 (file)
@@ -63,15 +63,15 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 this.cfg.push_assign(block, expr_span, // lt = idx < len
                                      &lt, Rvalue::BinaryOp(BinOp::Lt,
                                                            idx.clone(),
-                                                           Operand::Consume(len)));
+                                                           Operand::Consume(len.clone())));
 
                 let (success, failure) = (this.cfg.start_new_block(), this.cfg.start_new_block());
                 this.cfg.terminate(block,
                                    Terminator::If {
                                        cond: Operand::Consume(lt),
-                                       targets: [success, failure],
+                                       targets: (success, failure),
                                    });
-                this.panic(failure);
+                this.panic_bounds_check(failure, idx.clone(), Operand::Consume(len), expr_span);
                 success.and(slice.index(idx))
             }
             ExprKind::SelfRef => {
index 7f69b9a521f11205a9532f529f780940f9d78f12..2f57dd22454cb50c3841216fe1512b27e86fd6df 100644 (file)
@@ -40,7 +40,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 this.in_scope(extent, block, |this| this.as_rvalue(block, value))
             }
             ExprKind::InlineAsm { asm } => {
-                block.and(Rvalue::InlineAsm(asm))
+                block.and(Rvalue::InlineAsm(asm.clone()))
             }
             ExprKind::Repeat { value, count } => {
                 let value_operand = unpack!(block = this.as_operand(block, value));
index ac3e87e6b629a340bffa2f21eaec4117464f5300..63eb760720479a750d90779fa96a21c135332975 100644 (file)
@@ -15,6 +15,7 @@ use build::expr::category::{Category, RvalueFunc};
 use build::scope::LoopScope;
 use hair::*;
 use rustc::middle::region::CodeExtent;
+use rustc::middle::ty;
 use rustc::mir::repr::*;
 use syntax::codemap::Span;
 
@@ -53,11 +54,18 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 let mut else_block = this.cfg.start_new_block();
                 this.cfg.terminate(block, Terminator::If {
                     cond: operand,
-                    targets: [then_block, else_block]
+                    targets: (then_block, else_block)
                 });
 
                 unpack!(then_block = this.into(destination, then_block, then_expr));
-                unpack!(else_block = this.into(destination, else_block, else_expr));
+                else_block = if let Some(else_expr) = else_expr {
+                    unpack!(this.into(destination, else_block, else_expr))
+                } else {
+                    // Body of the `if` expression without an `else` clause must return `()`, thus
+                    // we implicitly generate a `else {}` if it is not specified.
+                    this.cfg.push_assign_unit(else_block, expr_span, &Lvalue::ReturnPointer);
+                    else_block
+                };
 
                 let join_block = this.cfg.start_new_block();
                 this.cfg.terminate(then_block, Terminator::Goto { target: join_block });
@@ -84,15 +92,15 @@ impl<'a,'tcx> Builder<'a,'tcx> {
 
                 let lhs = unpack!(block = this.as_operand(block, lhs));
                 let blocks = match op {
-                    LogicalOp::And => [else_block, false_block],
-                    LogicalOp::Or => [true_block, else_block],
+                    LogicalOp::And => (else_block, false_block),
+                    LogicalOp::Or => (true_block, else_block),
                 };
                 this.cfg.terminate(block, Terminator::If { cond: lhs, targets: blocks });
 
                 let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
                 this.cfg.terminate(else_block, Terminator::If {
                     cond: rhs,
-                    targets: [true_block, false_block]
+                    targets: (true_block, false_block)
                 });
 
                 this.cfg.push_assign_constant(
@@ -149,18 +157,25 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                         this.cfg.terminate(loop_block_end,
                                            Terminator::If {
                                                cond: cond,
-                                               targets: [body_block, exit_block]
+                                               targets: (body_block, exit_block)
                                            });
                     } else {
                         body_block = loop_block;
                     }
 
                     // execute the body, branching back to the test
-                    let unit_temp = this.unit_temp.clone();
-                    let body_block_end = unpack!(this.into(&unit_temp, body_block, body));
+                    // We write body’s “return value” into the destination of loop. This is fine,
+                    // because:
+                    //
+                    // * In Rust both loop expression and its body are required to have `()`
+                    //   as the “return value”;
+                    // * The destination will be considered uninitialised (given it was
+                    //   uninitialised before the loop) during the first iteration, thus
+                    //   disallowing its use inside the body. Alternatively, if it was already
+                    //   initialised, the `destination` can only possibly have a value of `()`,
+                    //   therefore, “mutating” the destination during iteration is fine.
+                    let body_block_end = unpack!(this.into(destination, body_block, body));
                     this.cfg.terminate(body_block_end, Terminator::Goto { target: loop_block });
-
-                    // final point is exit_block
                     exit_block.unit()
                 })
             }
@@ -205,28 +220,46 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 this.break_or_continue(expr_span, label, block, |loop_scope| loop_scope.break_block)
             }
             ExprKind::Return { value } => {
-                unpack!(block = this.into(&Lvalue::ReturnPointer, block, value));
+                block = match value {
+                    Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
+                    None => {
+                        this.cfg.push_assign_unit(block, expr_span, &Lvalue::ReturnPointer);
+                        block
+                    }
+                };
                 let extent = this.extent_of_outermost_scope();
                 this.exit_scope(expr_span, extent, block, END_BLOCK);
                 this.cfg.start_new_block().unit()
             }
-            ExprKind::Call { fun, args } => {
+            ExprKind::Call { ty, fun, args } => {
+                let diverges = match ty.sty {
+                    ty::TyBareFn(_, ref f) => f.sig.0.output.diverges(),
+                    _ => false
+                };
                 let fun = unpack!(block = this.as_operand(block, fun));
                 let args: Vec<_> =
                     args.into_iter()
                         .map(|arg| unpack!(block = this.as_operand(block, arg)))
                         .collect();
+
                 let success = this.cfg.start_new_block();
-                let panic = this.diverge_cleanup();
-                this.cfg.terminate(block,
-                                   Terminator::Call {
-                                       data: CallData {
-                                           destination: destination.clone(),
-                                           func: fun,
-                                           args: args,
-                                       },
-                                       targets: [success, panic],
-                                   });
+                let cleanup = this.diverge_cleanup();
+                this.cfg.terminate(block, Terminator::Call {
+                    func: fun,
+                    args: args,
+                    kind: match (cleanup, diverges) {
+                        (None, true) => CallKind::Diverging,
+                        (Some(c), true) => CallKind::DivergingCleanup(c),
+                        (None, false) => CallKind::Converging {
+                            destination: destination.clone(),
+                            target: success
+                        },
+                        (Some(c), false) => CallKind::ConvergingCleanup {
+                            destination: destination.clone(),
+                            targets: (success, c)
+                        }
+                    }
+                });
                 success.unit()
             }
 
index 66d6c49ef12aaf3b49b0eb5d49ae9a1152fb029b..77d9d926328fc5535bf26967ad79797c2d7a07b9 100644 (file)
@@ -14,7 +14,7 @@
 //! wrapped up as expressions (e.g. blocks). To make this ergonomic, we use this
 //! latter `EvalInto` trait.
 
-use build::{BlockAnd, BlockAndExtension, Builder};
+use build::{BlockAnd, Builder};
 use hair::*;
 use rustc::mir::repr::*;
 
@@ -58,16 +58,3 @@ impl<'tcx> EvalInto<'tcx> for Expr<'tcx> {
         builder.into_expr(destination, block, self)
     }
 }
-
-impl<'tcx> EvalInto<'tcx> for Option<ExprRef<'tcx>> {
-    fn eval_into<'a>(self,
-                     builder: &mut Builder<'a, 'tcx>,
-                     destination: &Lvalue<'tcx>,
-                     block: BasicBlock)
-                     -> BlockAnd<()> {
-        match self {
-            Some(expr) => builder.into(destination, block, expr),
-            None => block.unit(),
-        }
-    }
-}
index 0248f2fc49adbf3a3211443a3fc6bef2e6199164..c2c87fcbd20dafd14ccc63d50856b4bcf0ae9f04 100644 (file)
@@ -61,16 +61,13 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         // assemble a list of candidates: there is one candidate per
         // pattern, which means there may be more than one candidate
         // *per arm*. These candidates are kept sorted such that the
-        // highest priority candidate comes last in the list. This the
-        // reverse of the order in which candidates are written in the
-        // source.
+        // highest priority candidate comes first in the list.
+        // (i.e. same order as in source)
         let candidates: Vec<_> =
             arms.iter()
                 .enumerate()
-                .rev() // highest priority comes last
                 .flat_map(|(arm_index, arm)| {
                     arm.patterns.iter()
-                                .rev()
                                 .map(move |pat| (arm_index, pat, arm.guard.clone()))
                 })
                 .map(|(arm_index, pattern, guard)| {
@@ -92,7 +89,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         // not entirely precise
         if !otherwise.is_empty() {
             let join_block = self.join_otherwise_blocks(otherwise);
-            self.panic(join_block);
+            self.panic(join_block, "something about matches algorithm not being precise", span);
         }
 
         // all the arm blocks will rejoin here
@@ -209,7 +206,7 @@ struct ArmBlocks {
 }
 
 #[derive(Clone, Debug)]
-struct Candidate<'pat, 'tcx:'pat> {
+pub struct Candidate<'pat, 'tcx:'pat> {
     // all of these must be satisfied...
     match_pairs: Vec<MatchPair<'pat, 'tcx>>,
 
@@ -235,7 +232,7 @@ struct Binding<'tcx> {
 }
 
 #[derive(Clone, Debug)]
-struct MatchPair<'pat, 'tcx:'pat> {
+pub struct MatchPair<'pat, 'tcx:'pat> {
     // this lvalue...
     lvalue: Lvalue<'tcx>,
 
@@ -259,7 +256,7 @@ enum TestKind<'tcx> {
 
     // test for equality
     Eq {
-        value: Literal<'tcx>,
+        value: ConstVal,
         ty: Ty<'tcx>,
     },
 
@@ -278,7 +275,7 @@ enum TestKind<'tcx> {
 }
 
 #[derive(Debug)]
-struct Test<'tcx> {
+pub struct Test<'tcx> {
     span: Span,
     kind: TestKind<'tcx>,
 }
@@ -290,9 +287,9 @@ impl<'a,'tcx> Builder<'a,'tcx> {
     /// The main match algorithm. It begins with a set of candidates
     /// `candidates` and has the job of generating code to determine
     /// which of these candidates, if any, is the correct one. The
-    /// candidates are sorted in inverse priority -- so the last item
-    /// in the list has highest priority. When a candidate is found to
-    /// match the value, we will generate a branch to the appropriate
+    /// candidates are sorted such that the first item in the list
+    /// has the highest priority. When a candidate is found to match
+    /// the value, we will generate a branch to the appropriate
     /// block found in `arm_blocks`.
     ///
     /// The return value is a list of "otherwise" blocks. These are
@@ -324,17 +321,17 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             unpack!(block = self.simplify_candidate(block, candidate));
         }
 
-        // The candidates are inversely sorted by priority. Check to
-        // see whether the candidates in the front of the queue (and
-        // hence back of the vec) have satisfied all their match
+        // The candidates are sorted by priority. Check to see
+        // whether the higher priority candidates (and hence at
+        // the front of the vec) have satisfied all their match
         // pairs.
         let fully_matched =
-            candidates.iter().rev().take_while(|c| c.match_pairs.is_empty()).count();
+            candidates.iter().take_while(|c| c.match_pairs.is_empty()).count();
         debug!("match_candidates: {:?} candidates fully matched", fully_matched);
-        for _ in 0..fully_matched {
+        let mut unmatched_candidates = candidates.split_off(fully_matched);
+        for candidate in candidates {
             // If so, apply any bindings, test the guard (if any), and
             // branch to the arm.
-            let candidate = candidates.pop().unwrap();
             if let Some(b) = self.bind_and_guard_matched_candidate(block, arm_blocks, candidate) {
                 block = b;
             } else {
@@ -346,13 +343,13 @@ impl<'a,'tcx> Builder<'a,'tcx> {
 
         // If there are no candidates that still need testing, we're done.
         // Since all matches are exhaustive, execution should never reach this point.
-        if candidates.is_empty() {
+        if unmatched_candidates.is_empty() {
             return vec![block];
         }
 
         // Test candidates where possible.
         let (otherwise, tested_candidates) =
-            self.test_candidates(span, arm_blocks, &candidates, block);
+            self.test_candidates(span, arm_blocks, &unmatched_candidates, block);
 
         // If the target candidates were exhaustive, then we are done.
         if otherwise.is_empty() {
@@ -361,15 +358,14 @@ impl<'a,'tcx> Builder<'a,'tcx> {
 
         // If all candidates were sorted into `target_candidates` somewhere, then
         // the initial set was inexhaustive.
-        let untested_candidates = candidates.len() - tested_candidates;
-        if untested_candidates == 0 {
+        let untested_candidates = unmatched_candidates.split_off(tested_candidates);
+        if untested_candidates.len() == 0 {
             return otherwise;
         }
 
         // Otherwise, let's process those remaining candidates.
         let join_block = self.join_otherwise_blocks(otherwise);
-        candidates.truncate(untested_candidates);
-        self.match_candidates(span, arm_blocks, candidates, join_block)
+        self.match_candidates(span, arm_blocks, untested_candidates, join_block)
     }
 
     fn join_otherwise_blocks(&mut self,
@@ -461,7 +457,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                              -> (Vec<BasicBlock>, usize)
     {
         // extract the match-pair from the highest priority candidate
-        let match_pair = &candidates.last().unwrap().match_pairs[0];
+        let match_pair = &candidates.first().unwrap().match_pairs[0];
         let mut test = self.test(match_pair);
 
         // most of the time, the test to perform is simply a function
@@ -470,7 +466,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         // available
         match test.kind {
             TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => {
-                for candidate in candidates.iter().rev() {
+                for candidate in candidates.iter() {
                     if !self.add_cases_to_switch(&match_pair.lvalue,
                                                  candidate,
                                                  switch_ty,
@@ -497,7 +493,6 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         // that point, we stop sorting.
         let tested_candidates =
             candidates.iter()
-                      .rev()
                       .take_while(|c| self.sort_candidate(&match_pair.lvalue,
                                                           &test,
                                                           c,
@@ -555,7 +550,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             let cond = unpack!(block = self.as_operand(block, guard));
             let otherwise = self.cfg.start_new_block();
             self.cfg.terminate(block, Terminator::If { cond: cond,
-                                                       targets: [arm_block, otherwise]});
+                                                       targets: (arm_block, otherwise)});
             Some(otherwise)
         } else {
             self.cfg.terminate(block, Terminator::Goto { target: arm_block });
index 968514cd05c13dc99f23742f6dbde2bc3f627700..ec67429379f952e8e3a402728d5203782060184d 100644 (file)
@@ -37,7 +37,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                 }
             }
 
-            PatternKind::Constant { value: Literal::Value { .. } }
+            PatternKind::Constant { .. }
             if is_switch_ty(match_pair.pattern.ty) => {
                 // for integers, we use a SwitchInt match, which allows
                 // us to handle more cases
@@ -55,12 +55,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             }
 
             PatternKind::Constant { ref value } => {
-                // for other types, we use an equality comparison
                 Test {
                     span: match_pair.pattern.span,
                     kind: TestKind::Eq {
                         value: value.clone(),
-                        ty: match_pair.pattern.ty.clone(),
+                        ty: match_pair.pattern.ty.clone()
                     }
                 }
             }
@@ -113,7 +112,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         };
 
         match *match_pair.pattern.kind {
-            PatternKind::Constant { value: Literal::Value { ref value } } => {
+            PatternKind::Constant { ref value } => {
                 // if the lvalues match, the type should match
                 assert_eq!(match_pair.pattern.ty, switch_ty);
 
@@ -126,7 +125,6 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             }
 
             PatternKind::Range { .. } |
-            PatternKind::Constant { .. } |
             PatternKind::Variant { .. } |
             PatternKind::Slice { .. } |
             PatternKind::Array { .. } |
@@ -177,36 +175,26 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             }
 
             TestKind::Eq { ref value, ty } => {
-                // call PartialEq::eq(discrim, constant)
-                let constant = self.literal_operand(test.span, ty.clone(), value.clone());
-                let item_ref = self.hir.partial_eq(ty);
-                self.call_comparison_fn(block, test.span, item_ref,
-                                        Operand::Consume(lvalue.clone()), constant)
+                let expect = self.literal_operand(test.span, ty.clone(), Literal::Value {
+                    value: value.clone()
+                });
+                let val = Operand::Consume(lvalue.clone());
+                let fail = self.cfg.start_new_block();
+                let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val.clone());
+                vec![block, fail]
             }
 
             TestKind::Range { ref lo, ref hi, ty } => {
-                // Test `v` by computing `PartialOrd::le(lo, v) && PartialOrd::le(v, hi)`.
+                // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
                 let lo = self.literal_operand(test.span, ty.clone(), lo.clone());
                 let hi = self.literal_operand(test.span, ty.clone(), hi.clone());
-                let item_ref = self.hir.partial_le(ty);
-
-                let lo_blocks = self.call_comparison_fn(block,
-                                                        test.span,
-                                                        item_ref.clone(),
-                                                        lo,
-                                                        Operand::Consume(lvalue.clone()));
-
-                let hi_blocks = self.call_comparison_fn(lo_blocks[0],
-                                                        test.span,
-                                                        item_ref,
-                                                        Operand::Consume(lvalue.clone()),
-                                                        hi);
+                let val = Operand::Consume(lvalue.clone());
 
-                let failure = self.cfg.start_new_block();
-                self.cfg.terminate(lo_blocks[1], Terminator::Goto { target: failure });
-                self.cfg.terminate(hi_blocks[1], Terminator::Goto { target: failure });
+                let fail = self.cfg.start_new_block();
+                let block = self.compare(block, fail, test.span, BinOp::Le, lo, val.clone());
+                let block = self.compare(block, fail, test.span, BinOp::Le, val, hi);
 
-                vec![hi_blocks[0], failure]
+                vec![block, fail]
             }
 
             TestKind::Len { len, op } => {
@@ -232,7 +220,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                                                  self.cfg.start_new_block()];
                 self.cfg.terminate(block, Terminator::If {
                     cond: Operand::Consume(result),
-                    targets: [target_blocks[0], target_blocks[1]]
+                    targets: (target_blocks[0], target_blocks[1])
                 });
 
                 target_blocks
@@ -240,37 +228,27 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         }
     }
 
-    fn call_comparison_fn(&mut self,
-                          block: BasicBlock,
-                          span: Span,
-                          item_ref: ItemRef<'tcx>,
-                          lvalue1: Operand<'tcx>,
-                          lvalue2: Operand<'tcx>)
-                          -> Vec<BasicBlock> {
-        let target_blocks = vec![self.cfg.start_new_block(), self.cfg.start_new_block()];
-
+    fn compare(&mut self,
+               block: BasicBlock,
+               fail_block: BasicBlock,
+               span: Span,
+               op: BinOp,
+               left: Operand<'tcx>,
+               right: Operand<'tcx>) -> BasicBlock {
         let bool_ty = self.hir.bool_ty();
-        let eq_result = self.temp(bool_ty);
-        let func = self.item_ref_operand(span, item_ref);
-        let call_blocks = [self.cfg.start_new_block(), self.diverge_cleanup()];
-        self.cfg.terminate(block,
-                           Terminator::Call {
-                               data: CallData {
-                                   destination: eq_result.clone(),
-                                   func: func,
-                                   args: vec![lvalue1, lvalue2],
-                               },
-                               targets: call_blocks,
-                           });
-
-        // check the result
-        self.cfg.terminate(call_blocks[0],
-                           Terminator::If {
-                               cond: Operand::Consume(eq_result),
-                               targets: [target_blocks[0], target_blocks[1]],
-                           });
-
-        target_blocks
+        let result = self.temp(bool_ty);
+
+        // result = op(left, right)
+        self.cfg.push_assign(block, span, &result, Rvalue::BinaryOp(op, left, right));
+
+        // branch based on result
+        let target_block = self.cfg.start_new_block();
+        self.cfg.terminate(block, Terminator::If {
+            cond: Operand::Consume(result),
+            targets: (target_block, fail_block)
+        });
+
+        target_block
     }
 
     /// Given that we are performing `test` against `test_lvalue`,
@@ -357,7 +335,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
             // things out here, in some cases.
             TestKind::SwitchInt { switch_ty: _, options: _, ref indices } => {
                 match *match_pair.pattern.kind {
-                    PatternKind::Constant { value: Literal::Value { ref value } }
+                    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,
index bdcb183c0acfb223e041431aba9a4d9b82fe6a29..5d040bcb40ad8e72c2a1e111e47923df09e05d99 100644 (file)
@@ -66,6 +66,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                             -> Operand<'tcx> {
         let literal = Literal::Item {
             def_id: item_ref.def_id,
+            kind: item_ref.kind,
             substs: item_ref.substs,
         };
         self.literal_operand(span, item_ref.ty, literal)
index 45368b5a68d22f84bea638ec8cf65c6ce34d3a14..d217eb066479364de370bdc536bb7d96c3d091f0 100644 (file)
@@ -18,12 +18,11 @@ use rustc_front::hir;
 use syntax::ast;
 use syntax::codemap::Span;
 
-struct Builder<'a, 'tcx: 'a> {
+pub struct Builder<'a, 'tcx: 'a> {
     hir: Cx<'a, 'tcx>,
     cfg: CFG<'tcx>,
     scopes: Vec<scope::Scope<'tcx>>,
     loop_scopes: Vec<scope::LoopScope>,
-    unit_temp: Lvalue<'tcx>,
     var_decls: Vec<VarDecl<'tcx>>,
     var_indices: FnvHashMap<ast::NodeId, u32>,
     temp_decls: Vec<TempDecl<'tcx>>,
@@ -40,7 +39,7 @@ struct CFG<'tcx> {
 // convenient.
 
 #[must_use] // if you don't use one of these results, you're leaving a dangling edge
-struct BlockAnd<T>(BasicBlock, T);
+pub struct BlockAnd<T>(BasicBlock, T);
 
 trait BlockAndExtension {
     fn and<T>(self, v: T) -> BlockAnd<T>;
@@ -79,7 +78,7 @@ macro_rules! unpack {
 ///////////////////////////////////////////////////////////////////////////
 // construct() -- the main entry point for building MIR for a function
 
-pub fn construct<'a,'tcx>(mut hir: Cx<'a,'tcx>,
+pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
                           _span: Span,
                           implicit_arguments: Vec<Ty<'tcx>>,
                           explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
@@ -89,25 +88,18 @@ pub fn construct<'a,'tcx>(mut hir: Cx<'a,'tcx>,
                           -> Mir<'tcx> {
     let cfg = CFG { basic_blocks: vec![] };
 
-    // it's handy to have a temporary of type `()` sometimes, so make
-    // one from the start and keep it available
-    let temp_decls = vec![TempDecl::<'tcx> { ty: hir.unit_ty() }];
-    let unit_temp = Lvalue::Temp(0);
-
     let mut builder = Builder {
         hir: hir,
         cfg: cfg,
         scopes: vec![],
         loop_scopes: vec![],
-        temp_decls: temp_decls,
+        temp_decls: vec![],
         var_decls: vec![],
         var_indices: FnvHashMap(),
-        unit_temp: unit_temp,
     };
 
     assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
     assert_eq!(builder.cfg.start_new_block(), END_BLOCK);
-    assert_eq!(builder.cfg.start_new_block(), DIVERGE_BLOCK);
 
     let mut block = START_BLOCK;
     let arg_decls = unpack!(block = builder.args_and_body(block,
@@ -138,28 +130,25 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                      -> BlockAnd<Vec<ArgDecl<'tcx>>>
     {
         self.in_scope(argument_extent, block, |this| {
-            let arg_decls = {
-                let implicit_arg_decls = implicit_arguments.into_iter()
-                                                           .map(|ty| ArgDecl { ty: ty });
-
-                // to start, translate the argument patterns and collect the
-                // argument types.
-                let explicit_arg_decls =
-                    explicit_arguments
-                    .into_iter()
-                    .enumerate()
-                    .map(|(index, (ty, pattern))| {
+            // to start, translate the argument patterns and collect the argument types.
+            let implicits = implicit_arguments.into_iter().map(|ty| (ty, None));
+            let explicits = explicit_arguments.into_iter().map(|(ty, pat)| (ty, Some(pat)));
+            let arg_decls =
+                implicits
+                .chain(explicits)
+                .enumerate()
+                .map(|(index, (ty, pattern))| {
+                    if let Some(pattern) = pattern {
                         let lvalue = Lvalue::Arg(index as u32);
                         let pattern = this.hir.irrefutable_pat(pattern);
                         unpack!(block = this.lvalue_into_pattern(block,
                                                                  argument_extent,
                                                                  pattern,
                                                                  &lvalue));
-                        ArgDecl { ty: ty }
-                    });
-
-                implicit_arg_decls.chain(explicit_arg_decls).collect()
-            };
+                    }
+                    ArgDecl { ty: ty }
+                })
+                .collect();
 
             // start the first basic block and translate the body
             unpack!(block = this.ast_block(&Lvalue::ReturnPointer, block, ast_block));
index 4d136d265e5f4e1b5277634e369427973fdc8c7a..90d6a90682f6eb1680cd467d1730a56fa6e0ed47 100644 (file)
@@ -34,7 +34,7 @@ you but duty compels me to mention. In the course of translating
 matches, it sometimes happen that certain code (namely guards) gets
 executed multiple times. This means that the scope lexical scope may
 in fact correspond to multiple, disjoint SEME regions. So in fact our
-mapping os from one scope to a vector of SEME regions.
+mapping is from one scope to a vector of SEME regions.
 
 ### Drops
 
@@ -86,11 +86,14 @@ should go to.
 
 */
 
-use build::{BlockAnd, BlockAndExtension, Builder, CFG};
+use build::{BlockAnd, BlockAndExtension, Builder};
 use rustc::middle::region::CodeExtent;
-use rustc::middle::ty::Ty;
+use rustc::middle::lang_items;
+use rustc::middle::subst::Substs;
+use rustc::middle::ty::{self, Ty};
 use rustc::mir::repr::*;
-use syntax::codemap::Span;
+use syntax::codemap::{Span, DUMMY_SP};
+use syntax::parse::token::intern_and_get_ident;
 
 pub struct Scope<'tcx> {
     extent: CodeExtent,
@@ -227,17 +230,39 @@ impl<'a,'tcx> Builder<'a,'tcx> {
         self.cfg.terminate(block, Terminator::Goto { target: target });
     }
 
-    /// Creates a path that performs all required cleanup for
-    /// unwinding. This path terminates in DIVERGE. Returns the start
-    /// of the path. See module comment for more details.
-    pub fn diverge_cleanup(&mut self) -> BasicBlock {
-        diverge_cleanup_helper(&mut self.cfg, &mut self.scopes)
-    }
+    /// Creates a path that performs all required cleanup for unwinding.
+    ///
+    /// This path terminates in Resume. Returns the start of the path.
+    /// See module comment for more details. None indicates there’s no
+    /// cleanup to do at this point.
+    pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
+        if self.scopes.is_empty() {
+            return None;
+        }
 
-    /// Create diverge cleanup and branch to it from `block`.
-    pub fn panic(&mut self, block: BasicBlock) {
-        let cleanup = self.diverge_cleanup();
-        self.cfg.terminate(block, Terminator::Panic { target: cleanup });
+        let mut terminator = Terminator::Resume;
+        // Given an array of scopes, we generate these from the outermost scope to the innermost
+        // one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will
+        // generate B0 <- B1 <- B2 in left-to-right order. The outermost scope (B0) will always
+        // terminate with a Resume terminator.
+        for scope in self.scopes.iter_mut().filter(|s| !s.drops.is_empty()) {
+            if let Some(b) = scope.cached_block {
+                terminator = Terminator::Goto { target: b };
+                continue;
+            } else {
+                let new_block = self.cfg.start_new_block();
+                self.cfg.block_data_mut(new_block).is_cleanup = true;
+                self.cfg.terminate(new_block, terminator);
+                terminator = Terminator::Goto { target: new_block };
+                for &(kind, span, ref lvalue) in scope.drops.iter().rev() {
+                    self.cfg.push_drop(new_block, span, kind, lvalue);
+                }
+                scope.cached_block = Some(new_block);
+            }
+        }
+        // Return the innermost cached block, most likely the one we just generated.
+        // Note that if there are no cleanups in scope we return None.
+        self.scopes.iter().rev().flat_map(|b| b.cached_block).next()
     }
 
     /// Indicates that `lvalue` should be dropped on exit from
@@ -248,15 +273,19 @@ impl<'a,'tcx> Builder<'a,'tcx> {
                          kind: DropKind,
                          lvalue: &Lvalue<'tcx>,
                          lvalue_ty: Ty<'tcx>) {
-        if self.hir.needs_drop(lvalue_ty, span) {
-            match self.scopes.iter_mut().rev().find(|s| s.extent == extent) {
-                Some(scope) => {
+        if self.hir.needs_drop(lvalue_ty) {
+            for scope in self.scopes.iter_mut().rev() {
+                // We must invalidate all the cached_blocks leading up to the scope we’re looking
+                // for, because otherwise some/most of the blocks in the chain might become
+                // incorrect (i.e. they still are pointing at old cached_block).
+                scope.cached_block = None;
+                if scope.extent == extent {
                     scope.drops.push((kind, span, lvalue.clone()));
-                    scope.cached_block = None;
+                    return;
                 }
-                None => self.hir.span_bug(span, &format!("extent {:?} not in scope to drop {:?}",
-                                                         extent, lvalue)),
             }
+            self.hir.span_bug(span,
+                              &format!("extent {:?} not in scope to drop {:?}", extent, lvalue));
         }
     }
 
@@ -267,29 +296,111 @@ impl<'a,'tcx> Builder<'a,'tcx> {
     pub fn extent_of_outermost_scope(&self) -> CodeExtent {
         self.scopes.first().map(|scope| scope.extent).unwrap()
     }
-}
-
-fn diverge_cleanup_helper<'tcx>(cfg: &mut CFG<'tcx>, scopes: &mut [Scope<'tcx>]) -> BasicBlock {
-    let len = scopes.len();
 
-    if len == 0 {
-        return DIVERGE_BLOCK;
+    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 func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem);
+        let args = func.ty.fn_args();
+        let ref_ty = args.skip_binder()[0];
+        let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty {
+            (region, tyandmut.ty)
+        } else {
+            self.hir.span_bug(span, &format!("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)];
+        // 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, DUMMY_SP, &tuple, // tuple = (file_arg, line_arg);
+                             Rvalue::Aggregate(AggregateKind::Tuple, elems));
+        // FIXME: is this region really correct here?
+        self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple;
+                             Rvalue::Ref(*region, BorrowKind::Unique, tuple));
+        let cleanup = self.diverge_cleanup();
+        self.cfg.terminate(block, Terminator::Call {
+            func: Operand::Constant(func),
+            args: vec![Operand::Consume(tuple_ref), index, len],
+            kind: match cleanup {
+                None => CallKind::Diverging,
+                Some(c) => CallKind::DivergingCleanup(c)
+            }
+        });
     }
 
-    let (remaining, scope) = scopes.split_at_mut(len - 1);
-    let scope = &mut scope[0];
-
-    if let Some(b) = scope.cached_block {
-        return b;
+    /// 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 func = self.lang_function(lang_items::PanicFnLangItem);
+        let args = func.ty.fn_args();
+        let ref_ty = args.skip_binder()[0];
+        let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty {
+            (region, tyandmut.ty)
+        } else {
+            self.hir.span_bug(span, &format!("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: DUMMY_SP,
+            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)];
+        // 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, DUMMY_SP, &tuple, // tuple = (message_arg, file_arg, line_arg);
+                             Rvalue::Aggregate(AggregateKind::Tuple, elems));
+        // FIXME: is this region really correct here?
+        self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple;
+                             Rvalue::Ref(*region, BorrowKind::Unique, tuple));
+        let cleanup = self.diverge_cleanup();
+        self.cfg.terminate(block, Terminator::Call {
+            func: Operand::Constant(func),
+            args: vec![Operand::Consume(tuple_ref)],
+            kind: match cleanup {
+                None => CallKind::Diverging,
+                Some(c) => CallKind::DivergingCleanup(c)
+            }
+        });
     }
 
-    let block = cfg.start_new_block();
-    for &(kind, span, ref lvalue) in &scope.drops {
-        cfg.push_drop(block, span, kind, lvalue);
+    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,
+                kind: ItemKind::Function,
+                substs: self.hir.tcx().mk_substs(Substs::empty())
+            }
+        }
     }
-    scope.cached_block = Some(block);
 
-    let remaining_cleanup_block = diverge_cleanup_helper(cfg, remaining);
-    cfg.terminate(block, Terminator::Goto { target: remaining_cleanup_block });
-    block
+    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: DUMMY_SP,
+            ty: self.hir.tcx().mk_static_str(),
+            literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name))
+        }, Constant {
+            span: DUMMY_SP,
+            ty: self.hir.tcx().types.u32,
+            literal: self.hir.usize_literal(span_lines.line)
+        })
+    }
 }
diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/graphviz.rs
new file mode 100644 (file)
index 0000000..1b8fe65
--- /dev/null
@@ -0,0 +1,133 @@
+// 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 dot;
+use rustc::mir::repr::*;
+use rustc::middle::ty;
+use std::fmt::Debug;
+use std::io::{self, Write};
+
+/// Write a graphviz DOT graph for the given MIR.
+pub fn write_mir_graphviz<W: Write>(mir: &Mir, w: &mut W) -> io::Result<()> {
+    try!(writeln!(w, "digraph Mir {{"));
+
+    // Global graph properties
+    try!(writeln!(w, r#"    graph [fontname="monospace"];"#));
+    try!(writeln!(w, r#"    node [fontname="monospace"];"#));
+    try!(writeln!(w, r#"    edge [fontname="monospace"];"#));
+
+    // Graph label
+    try!(write_graph_label(mir, w));
+
+    // Nodes
+    for block in mir.all_basic_blocks() {
+        try!(write_node(block, mir, w));
+    }
+
+    // Edges
+    for source in mir.all_basic_blocks() {
+        try!(write_edges(source, mir, w));
+    }
+
+    writeln!(w, "}}")
+}
+
+/// Write a graphviz DOT node for the given basic block.
+fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
+    let data = mir.basic_block_data(block);
+
+    // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
+    try!(write!(w, r#"    {} [shape="none", label=<"#, node(block)));
+    try!(write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#));
+
+    // Basic block number at the top.
+    try!(write!(w, r#"<tr><td bgcolor="gray" align="center">{}</td></tr>"#, block.index()));
+
+    // List of statements in the middle.
+    if !data.statements.is_empty() {
+        try!(write!(w, r#"<tr><td align="left" balign="left">"#));
+        for statement in &data.statements {
+            try!(write!(w, "{}<br/>", escape(statement)));
+        }
+        try!(write!(w, "</td></tr>"));
+    }
+
+    // Terminator head at the bottom, not including the list of successor blocks. Those will be
+    // displayed as labels on the edges between blocks.
+    let mut terminator_head = String::new();
+    data.terminator().fmt_head(&mut terminator_head).unwrap();
+    try!(write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head)));
+
+    // Close the table, node label, and the node itself.
+    writeln!(w, "</table>>];")
+}
+
+/// 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 labels = terminator.fmt_successor_labels();
+
+    for (&target, label) in terminator.successors().iter().zip(labels) {
+        try!(writeln!(w, r#"    {} -> {} [label="{}"];"#, node(source), node(target), label));
+    }
+
+    Ok(())
+}
+
+/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
+/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
+/// all the variables and temporaries.
+fn write_graph_label<W: Write>(mir: &Mir, w: &mut W) -> io::Result<()> {
+    try!(write!(w, "    label=<fn("));
+
+    // fn argument types.
+    for (i, arg) in mir.arg_decls.iter().enumerate() {
+        if i > 0 {
+            try!(write!(w, ", "));
+        }
+        try!(write!(w, "{:?}: {}", Lvalue::Arg(i as u32), escape(&arg.ty)));
+    }
+
+    try!(write!(w, ") -&gt; "));
+
+    // fn return type.
+    match mir.return_ty {
+        ty::FnOutput::FnConverging(ty) => try!(write!(w, "{}", escape(ty))),
+        ty::FnOutput::FnDiverging => try!(write!(w, "!")),
+    }
+
+    try!(write!(w, r#"<br align="left"/>"#));
+
+    // User variable types (including the user's name in a comment).
+    for (i, var) in mir.var_decls.iter().enumerate() {
+        try!(write!(w, "let "));
+        if var.mutability == Mutability::Mut {
+            try!(write!(w, "mut "));
+        }
+        try!(write!(w, r#"{:?}: {}; // {}<br align="left"/>"#,
+                    Lvalue::Var(i as u32), escape(&var.ty), var.name));
+    }
+
+    // Compiler-introduced temporary types.
+    for (i, temp) in mir.temp_decls.iter().enumerate() {
+        try!(write!(w, r#"let mut {:?}: {};<br align="left"/>"#,
+                    Lvalue::Temp(i as u32), escape(&temp.ty)));
+    }
+
+    writeln!(w, ">;")
+}
+
+fn node(block: BasicBlock) -> String {
+    format!("bb{}", block.index())
+}
+
+fn escape<T: Debug>(t: &T) -> String {
+    dot::escape_html(&format!("{:?}", t))
+}
diff --git a/src/librustc_mir/graphviz/mod.rs b/src/librustc_mir/graphviz/mod.rs
deleted file mode 100644 (file)
index f8cec83..0000000
+++ /dev/null
@@ -1,166 +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 dot;
-use rustc::mir::repr::*;
-use std::borrow::IntoCow;
-
-#[derive(Copy, Clone, PartialEq, Eq)]
-pub struct EdgeIndex {
-    source: BasicBlock,
-    target: BasicBlock,
-    index: usize,
-}
-
-impl<'a,'tcx> dot::Labeller<'a, BasicBlock, EdgeIndex> for Mir<'tcx> {
-    fn graph_id(&'a self) -> dot::Id<'a> {
-        dot::Id::new("Mir").unwrap()
-    }
-
-    fn node_id(&'a self, n: &BasicBlock) -> dot::Id<'a> {
-        dot::Id::new(format!("BB{}", n.index())).unwrap()
-    }
-
-    fn node_shape(&'a self, _: &BasicBlock) -> Option<dot::LabelText<'a>> {
-        Some(dot::LabelText::label("none"))
-    }
-
-    fn node_label(&'a self, &n: &BasicBlock) -> dot::LabelText<'a> {
-        let mut buffer = String::new();
-        buffer.push_str("<TABLE ALIGN=\"LEFT\">");
-
-        buffer.push_str("<TR><TD>");
-        buffer.push_str(&format!("{:?}", n));
-        buffer.push_str("</TD></TR>");
-
-        let data = self.basic_block_data(n);
-        for statement in &data.statements {
-            buffer.push_str("<TR><TD>");
-            buffer.push_str(&escape(format!("{:?}", statement)));
-            buffer.push_str("</TD></TR>");
-        }
-
-        buffer.push_str("<TR><TD>");
-        buffer.push_str(&escape(format!("{:?}", &data.terminator)));
-        buffer.push_str("</TD></TR>");
-
-        buffer.push_str("</TABLE>");
-
-        dot::LabelText::html(buffer)
-    }
-
-    fn edge_label(&'a self, edge: &EdgeIndex) -> dot::LabelText<'a> {
-        dot::LabelText::label(format!("{}", edge.index))
-    }
-}
-
-impl<'a,'tcx> dot::GraphWalk<'a, BasicBlock, EdgeIndex> for Mir<'tcx> {
-    fn nodes(&'a self) -> dot::Nodes<'a, BasicBlock> {
-        self.all_basic_blocks().into_cow()
-    }
-
-    fn edges(&'a self) -> dot::Edges<'a, EdgeIndex> {
-        self.all_basic_blocks()
-            .into_iter()
-            .flat_map(|source| {
-                self.basic_block_data(source)
-                    .terminator
-                    .successors()
-                    .iter()
-                    .enumerate()
-                    .map(move |(index, &target)| {
-                        EdgeIndex {
-                            source: source,
-                            target: target,
-                            index: index,
-                        }
-                    })
-            })
-            .collect::<Vec<_>>()
-            .into_cow()
-    }
-
-    fn source(&'a self, edge: &EdgeIndex) -> BasicBlock {
-        edge.source
-    }
-
-    fn target(&'a self, edge: &EdgeIndex) -> BasicBlock {
-        edge.target
-    }
-}
-
-fn escape(text: String) -> String {
-    let text = dot::escape_html(&text);
-    let text = all_to_subscript("Temp", text);
-    let text = all_to_subscript("Var", text);
-    let text = all_to_subscript("Arg", text);
-    let text = all_to_subscript("BB", text);
-    text
-}
-
-/// A call like `all_to_subscript("Temp", "Temp(123)")` will convert
-/// to `Temp₁₂₃`.
-fn all_to_subscript(header: &str, mut text: String) -> String {
-    let mut offset = 0;
-    while offset < text.len() {
-        if let Some(text1) = to_subscript1(header, &text, &mut offset) {
-            text = text1;
-        }
-    }
-    return text;
-
-    /// Looks for `Foo(\d*)` where `header=="Foo"` and replaces the `\d` with subscripts.
-    /// Updates `offset` to point to the next location where we might want to search.
-    /// Returns an updated string if changes were made, else None.
-    fn to_subscript1(header: &str, text: &str, offset: &mut usize) -> Option<String> {
-        let a = match text[*offset..].find(header) {
-            None => {
-                *offset = text.len();
-                return None;
-            }
-            Some(a) => a + *offset,
-        };
-
-        // Example:
-        //
-        // header: "Foo"
-        // text:   ....Foo(123)...
-        //             ^  ^
-        //             a  b
-
-        let b = a + header.len();
-        *offset = b;
-
-        let mut chars = text[b..].chars();
-        if Some('(') != chars.next() {
-            return None;
-        }
-
-        let mut result = String::new();
-        result.push_str(&text[..b]);
-
-        while let Some(c) = chars.next() {
-            if c == ')' {
-                break;
-            }
-            if !c.is_digit(10) {
-                return None;
-            }
-
-            // 0x208 is _0 in unicode, 0x209 is _1, etc
-            const SUBSCRIPTS: &'static str = "₀₁₂₃₄₅₆₇₈₉";
-            let n = (c as usize) - ('0' as usize);
-            result.extend(SUBSCRIPTS.chars().skip(n).take(1));
-        }
-
-        result.extend(chars);
-        return Some(result);
-    }
-}
index 08826013ebc3771a13529832cebea0c8755b1e96..8090fca66bb6247904341cb8f1de0cd3bb5e33a4 100644 (file)
@@ -41,11 +41,65 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                                .map(|e| e.to_ref())
                                .collect();
                 ExprKind::Call {
+                    ty: expr.ty,
                     fun: expr.to_ref(),
                     args: args,
                 }
             }
 
+            hir::ExprCall(ref fun, ref args) => {
+                if cx.tcx.is_method_call(self.id) {
+                    // The callee is something implementing Fn, FnMut, or FnOnce.
+                    // Find the actual method implementation being called and
+                    // build the appropriate UFCS call expression with the
+                    // callee-object as self parameter.
+                    let method = method_callee(cx, self, ty::MethodCall::expr(self.id));
+                    let mut argrefs = vec![fun.to_ref()];
+                    argrefs.extend(args.iter().map(|a| a.to_ref()));
+
+                    ExprKind::Call {
+                        ty: method.ty,
+                        fun: method.to_ref(),
+                        args: argrefs,
+                    }
+                } else {
+                    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() {
+                                def::DefVariant(_, variant_id, false) => {
+                                    Some((adt_def, adt_def.variant_index_with_id(variant_id)))
+                                },
+                                def::DefStruct(_) => {
+                                    Some((adt_def, 0))
+                                },
+                                _ => None
+                            }
+                        })
+                    } else { None };
+                    if let Some((adt_def, index)) = adt_data {
+                        let substs = cx.tcx.mk_substs(cx.tcx.node_id_item_substs(fun.id).substs);
+                        let field_refs = args.iter().enumerate().map(|(idx, e)| FieldExprRef {
+                            name: Field::new(idx),
+                            expr: e.to_ref()
+                        }).collect();
+                        ExprKind::Adt {
+                            adt_def: adt_def,
+                            substs: substs,
+                            variant_index: index,
+                            fields: field_refs,
+                            base: None
+                        }
+                    } else {
+                        ExprKind::Call {
+                            ty: cx.tcx.node_id_to_type(fun.id),
+                            fun: fun.to_ref(),
+                            args: args.to_ref(),
+                        }
+                    }
+                }
+            }
+
             hir::ExprAddrOf(mutbl, ref expr) => {
                 let region = match expr_ty.sty {
                     ty::TyRef(r, _) => r,
@@ -320,14 +374,14 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                                   name: Field::new(index.node as usize) },
             hir::ExprCast(ref source, _) =>
                 ExprKind::Cast { source: source.to_ref() },
+            hir::ExprType(ref source, _) =>
+                return source.make_mirror(cx),
             hir::ExprBox(ref value) =>
                 ExprKind::Box { value: value.to_ref() },
             hir::ExprVec(ref fields) =>
                 ExprKind::Vec { fields: fields.to_ref() },
             hir::ExprTup(ref fields) =>
                 ExprKind::Tuple { fields: fields.to_ref() },
-            hir::ExprCall(ref fun, ref args) =>
-                ExprKind::Call { fun: fun.to_ref(), args: args.to_ref() },
         };
 
         let temp_lifetime = cx.tcx.region_maps.temporary_scope(self.id);
@@ -480,6 +534,7 @@ fn method_callee<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>,
         kind: ExprKind::Literal {
             literal: Literal::Item {
                 def_id: callee.def_id,
+                kind: ItemKind::Method,
                 substs: callee.substs,
             },
         },
@@ -514,30 +569,68 @@ fn convert_arm<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, arm: &'tcx hir::Arm) -> Arm<
 
 fn convert_path_expr<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr) -> ExprKind<'tcx> {
     let substs = cx.tcx.mk_substs(cx.tcx.node_id_item_substs(expr.id).substs);
-    match cx.tcx.def_map.borrow()[&expr.id].full_def() {
-        def::DefVariant(_, def_id, false) |
-        def::DefStruct(def_id) |
-        def::DefFn(def_id, _) |
-        def::DefConst(def_id) |
-        def::DefMethod(def_id) |
-        def::DefAssociatedConst(def_id) =>
-            ExprKind::Literal {
-                literal: Literal::Item { def_id: def_id, substs: substs }
+    // Otherwise there may be def_map borrow conflicts
+    let def = cx.tcx.def_map.borrow()[&expr.id].full_def();
+    let (def_id, kind) = match def {
+        // A regular function.
+        def::DefFn(def_id, _) => (def_id, ItemKind::Function),
+        def::DefMethod(def_id) => (def_id, ItemKind::Method),
+        def::DefStruct(def_id) => match cx.tcx.node_id_to_type(expr.id).sty {
+            // A tuple-struct constructor. Should only be reached if not called in the same
+            // expression.
+            ty::TyBareFn(..) => (def_id, ItemKind::Function),
+            // A unit struct which is used as a value. We return a completely different ExprKind
+            // here to account for this special case.
+            ty::TyStruct(adt_def, substs) => return ExprKind::Adt {
+                adt_def: adt_def,
+                variant_index: 0,
+                substs: substs,
+                fields: vec![],
+                base: None
             },
-
-        def::DefStatic(node_id, _) =>
-            ExprKind::StaticRef {
-                id: node_id,
+            ref sty => panic!("unexpected sty: {:?}", sty)
+        },
+        def::DefVariant(enum_id, variant_id, false) => match cx.tcx.node_id_to_type(expr.id).sty {
+            // A variant constructor. Should only be reached if not called in the same
+            // expression.
+            ty::TyBareFn(..) => (variant_id, ItemKind::Function),
+            // A unit variant, similar special case to the struct case above.
+            ty::TyEnum(adt_def, substs) => {
+                debug_assert!(adt_def.did == enum_id);
+                let index = adt_def.variant_index_with_id(variant_id);
+                return ExprKind::Adt {
+                    adt_def: adt_def,
+                    substs: substs,
+                    variant_index: index,
+                    fields: vec![],
+                    base: None
+                };
             },
+            ref sty => panic!("unexpected sty: {:?}", sty)
+        },
+        def::DefConst(def_id) |
+        def::DefAssociatedConst(def_id) => {
+            if let Some(v) = cx.try_const_eval_literal(expr) {
+                return ExprKind::Literal { literal: v };
+            } else {
+                (def_id, ItemKind::Constant)
+            }
+        }
+
+        def::DefStatic(node_id, _) => return ExprKind::StaticRef {
+            id: node_id,
+        },
 
         def @ def::DefLocal(..) |
-        def @ def::DefUpvar(..) =>
-            convert_var(cx, expr, def),
+        def @ def::DefUpvar(..) => return convert_var(cx, expr, def),
 
         def =>
             cx.tcx.sess.span_bug(
                 expr.span,
                 &format!("def `{:?}` not yet implemented", def)),
+    };
+    ExprKind::Literal {
+        literal: Literal::Item { def_id: def_id, kind: kind, substs: substs }
     }
 }
 
@@ -745,6 +838,7 @@ fn overloaded_operator<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>,
     // now create the call itself
     let fun = method_callee(cx, expr, method_call);
     ExprKind::Call {
+        ty: fun.ty,
         fun: fun.to_ref(),
         args: argrefs,
     }
@@ -835,6 +929,7 @@ fn loop_label<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr) -> Cod
     }
 }
 
+/// Converts a list of named fields (i.e. for struct-like struct/enum ADTs) into FieldExprRef.
 fn field_refs<'tcx>(variant: VariantDef<'tcx>,
                     fields: &'tcx [hir::Field])
                     -> Vec<FieldExprRef<'tcx>>
index 8c19e620e46ac56a0be42df6bdba1814295617fb..b49dc6d89624265d68b6f82c396acbc75065efbc 100644 (file)
@@ -19,9 +19,7 @@ use hair::*;
 use rustc::mir::repr::*;
 
 use rustc::middle::const_eval::{self, ConstVal};
-use rustc::middle::def_id::DefId;
 use rustc::middle::infer::InferCtxt;
-use rustc::middle::subst::{Subst, Substs};
 use rustc::middle::ty::{self, Ty};
 use syntax::codemap::Span;
 use syntax::parse::token;
@@ -48,10 +46,6 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> {
         ast.make_mirror(self)
     }
 
-    pub fn unit_ty(&mut self) -> Ty<'tcx> {
-        self.tcx.mk_nil()
-    }
-
     pub fn usize_ty(&mut self) -> Ty<'tcx> {
         self.tcx.types.usize
     }
@@ -64,6 +58,10 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> {
         self.tcx.types.bool
     }
 
+    pub fn str_literal(&mut self, value: token::InternedString) -> Literal<'tcx> {
+        Literal::Value { value: ConstVal::Str(value) }
+    }
+
     pub fn true_literal(&mut self) -> Literal<'tcx> {
         Literal::Value { value: ConstVal::Bool(true) }
     }
@@ -76,14 +74,11 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> {
         Literal::Value { value: const_eval::eval_const_expr(self.tcx, e) }
     }
 
-    pub fn partial_eq(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> {
-        let eq_def_id = self.tcx.lang_items.eq_trait().unwrap();
-        self.cmp_method_ref(eq_def_id, "eq", ty)
-    }
-
-    pub fn partial_le(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> {
-        let ord_def_id = self.tcx.lang_items.ord_trait().unwrap();
-        self.cmp_method_ref(ord_def_id, "le", ty)
+    pub fn try_const_eval_literal(&mut self, e: &hir::Expr) -> Option<Literal<'tcx>> {
+        let hint = const_eval::EvalHint::ExprTypeChecked;
+        const_eval::eval_const_expr_partial(self.tcx, e, hint, None)
+            .ok()
+            .map(|v| Literal::Value { value: v })
     }
 
     pub fn num_variants(&mut self, adt_def: ty::AdtDef<'tcx>) -> usize {
@@ -96,17 +91,8 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> {
             .collect()
     }
 
-    pub fn needs_drop(&mut self, ty: Ty<'tcx>, span: Span) -> bool {
-        if self.infcx.type_moves_by_default(ty, span) {
-            // FIXME(#21859) we should do an add'l check here to determine if
-            // any dtor will execute, but the relevant fn
-            // (`type_needs_drop`) is currently factored into
-            // `librustc_trans`, so we can't easily do so.
-            true
-        } else {
-            // if type implements Copy, cannot require drop
-            false
-        }
+    pub fn needs_drop(&mut self, ty: Ty<'tcx>) -> bool {
+        self.tcx.type_needs_drop_given_env(ty, &self.infcx.parameter_environment)
     }
 
     pub fn span_bug(&mut self, span: Span, message: &str) -> ! {
@@ -116,34 +102,6 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> {
     pub fn tcx(&self) -> &'a ty::ctxt<'tcx> {
         self.tcx
     }
-
-    fn cmp_method_ref(&mut self,
-                      trait_def_id: DefId,
-                      method_name: &str,
-                      arg_ty: Ty<'tcx>)
-                      -> ItemRef<'tcx> {
-        let method_name = token::intern(method_name);
-        let substs = Substs::new_trait(vec![arg_ty], vec![], arg_ty);
-        for trait_item in self.tcx.trait_items(trait_def_id).iter() {
-            match *trait_item {
-                ty::ImplOrTraitItem::MethodTraitItem(ref method) => {
-                    if method.name == method_name {
-                        let method_ty = self.tcx.lookup_item_type(method.def_id);
-                        let method_ty = method_ty.ty.subst(self.tcx, &substs);
-                        return ItemRef {
-                            ty: method_ty,
-                            def_id: method.def_id,
-                            substs: self.tcx.mk_substs(substs),
-                        };
-                    }
-                }
-                ty::ImplOrTraitItem::ConstTraitItem(..) |
-                ty::ImplOrTraitItem::TypeTraitItem(..) => {}
-            }
-        }
-
-        self.tcx.sess.bug(&format!("found no method `{}` in `{:?}`", method_name, trait_def_id));
-    }
 }
 
 mod block;
index 74fef6840065627d1db5a9c503a5992c653e584c..dc377ac731a6581ddf9346c4c7e1cebfb37d61ed 100644 (file)
@@ -14,11 +14,11 @@ use rustc_data_structures::fnv::FnvHashMap;
 use rustc::middle::const_eval;
 use rustc::middle::def;
 use rustc::middle::pat_util::{pat_is_resolved_const, pat_is_binding};
-use rustc::middle::subst::Substs;
 use rustc::middle::ty::{self, Ty};
 use rustc::mir::repr::*;
 use rustc_front::hir;
 use syntax::ast;
+use syntax::codemap::Span;
 use syntax::ptr::P;
 
 /// When there are multiple patterns in a single arm, each one has its
@@ -40,15 +40,15 @@ struct PatCx<'patcx, 'cx: 'patcx, 'tcx: 'cx> {
 }
 
 impl<'cx, 'tcx> Cx<'cx, 'tcx> {
-    pub fn irrefutable_pat(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> {
-        PatCx::new(self, None).to_pat(pat)
+    pub fn irrefutable_pat(&mut self, pat: &hir::Pat) -> Pattern<'tcx> {
+        PatCx::new(self, None).to_pattern(pat)
     }
 
     pub fn refutable_pat(&mut self,
                          binding_map: Option<&FnvHashMap<ast::Name, ast::NodeId>>,
-                         pat: &'tcx hir::Pat)
+                         pat: &hir::Pat)
                          -> Pattern<'tcx> {
-        PatCx::new(self, binding_map).to_pat(pat)
+        PatCx::new(self, binding_map).to_pattern(pat)
     }
 }
 
@@ -62,13 +62,12 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
         }
     }
 
-    fn to_pat(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> {
+    fn to_pattern(&mut self, pat: &hir::Pat) -> Pattern<'tcx> {
         let kind = match pat.node {
             hir::PatWild => PatternKind::Wild,
 
             hir::PatLit(ref value) => {
                 let value = const_eval::eval_const_expr(self.cx.tcx, value);
-                let value = Literal::Value { value: value };
                 PatternKind::Constant { value: value }
             }
 
@@ -86,20 +85,12 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
                 let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
                 match def {
                     def::DefConst(def_id) | def::DefAssociatedConst(def_id) =>
-                        match const_eval::lookup_const_by_id(self.cx.tcx, def_id, Some(pat.id)) {
+                        match const_eval::lookup_const_by_id(self.cx.tcx, def_id,
+                                                             Some(pat.id), None) {
                             Some(const_expr) => {
-                                let opt_value =
-                                    const_eval::eval_const_expr_partial(
-                                        self.cx.tcx, const_expr,
-                                        const_eval::EvalHint::ExprTypeChecked,
-                                        None);
-                                let literal = if let Ok(value) = opt_value {
-                                    Literal::Value { value: value }
-                                } else {
-                                    let substs = self.cx.tcx.mk_substs(Substs::empty());
-                                    Literal::Item { def_id: def_id, substs: substs }
-                                };
-                                PatternKind::Constant { value: literal }
+                                let pat = const_eval::const_expr_to_pat(self.cx.tcx, const_expr,
+                                                                        pat.span);
+                                return self.to_pattern(&*pat);
                             }
                             None => {
                                 self.cx.tcx.sess.span_bug(
@@ -116,7 +107,7 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
 
             hir::PatRegion(ref subpattern, _) |
             hir::PatBox(ref subpattern) => {
-                PatternKind::Deref { subpattern: self.to_pat(subpattern) }
+                PatternKind::Deref { subpattern: self.to_pattern(subpattern) }
             }
 
             hir::PatVec(ref prefix, ref slice, ref suffix) => {
@@ -127,14 +118,14 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
                             subpattern: Pattern {
                                 ty: mt.ty,
                                 span: pat.span,
-                                kind: Box::new(self.slice_or_array_pattern(pat, mt.ty, prefix,
+                                kind: Box::new(self.slice_or_array_pattern(pat.span, mt.ty, prefix,
                                                                            slice, suffix)),
                             },
                         },
 
                     ty::TySlice(..) |
                     ty::TyArray(..) =>
-                        self.slice_or_array_pattern(pat, ty, prefix, slice, suffix),
+                        self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix),
 
                     ref sty =>
                         self.cx.tcx.sess.span_bug(
@@ -149,7 +140,7 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
                                .enumerate()
                                .map(|(i, subpattern)| FieldPattern {
                                    field: Field::new(i),
-                                   pattern: self.to_pat(subpattern),
+                                   pattern: self.to_pattern(subpattern),
                                })
                                .collect();
 
@@ -184,7 +175,7 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
                     name: ident.node.name,
                     var: id,
                     ty: var_ty,
-                    subpattern: self.to_opt_pat(sub),
+                    subpattern: self.to_opt_pattern(sub),
                 }
             }
 
@@ -199,7 +190,7 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
                                    .enumerate()
                                    .map(|(i, field)| FieldPattern {
                                        field: Field::new(i),
-                                       pattern: self.to_pat(field),
+                                       pattern: self.to_pattern(field),
                                    })
                                    .collect();
                 self.variant_or_leaf(pat, subpatterns)
@@ -230,7 +221,7 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
                               });
                               FieldPattern {
                                   field: Field::new(index),
-                                  pattern: self.to_pat(&field.node.pat),
+                                  pattern: self.to_pattern(&field.node.pat),
                               }
                           })
                           .collect();
@@ -252,28 +243,28 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
         }
     }
 
-    fn to_pats(&mut self, pats: &'tcx Vec<P<hir::Pat>>) -> Vec<Pattern<'tcx>> {
-        pats.iter().map(|p| self.to_pat(p)).collect()
+    fn to_patterns(&mut self, pats: &[P<hir::Pat>]) -> Vec<Pattern<'tcx>> {
+        pats.iter().map(|p| self.to_pattern(p)).collect()
     }
 
-    fn to_opt_pat(&mut self, pat: &'tcx Option<P<hir::Pat>>) -> Option<Pattern<'tcx>> {
-        pat.as_ref().map(|p| self.to_pat(p))
+    fn to_opt_pattern(&mut self, pat: &Option<P<hir::Pat>>) -> Option<Pattern<'tcx>> {
+        pat.as_ref().map(|p| self.to_pattern(p))
     }
 
     fn slice_or_array_pattern(&mut self,
-                              pat: &'tcx hir::Pat,
+                              span: Span,
                               ty: Ty<'tcx>,
-                              prefix: &'tcx Vec<P<hir::Pat>>,
-                              slice: &'tcx Option<P<hir::Pat>>,
-                              suffix: &'tcx Vec<P<hir::Pat>>)
+                              prefix: &[P<hir::Pat>],
+                              slice: &Option<P<hir::Pat>>,
+                              suffix: &[P<hir::Pat>])
                               -> PatternKind<'tcx> {
         match ty.sty {
             ty::TySlice(..) => {
                 // matching a slice or fixed-length array
                 PatternKind::Slice {
-                    prefix: self.to_pats(prefix),
-                    slice: self.to_opt_pat(slice),
-                    suffix: self.to_pats(suffix),
+                    prefix: self.to_patterns(prefix),
+                    slice: self.to_opt_pattern(slice),
+                    suffix: self.to_patterns(suffix),
                 }
             }
 
@@ -281,20 +272,20 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
                 // fixed-length array
                 assert!(len >= prefix.len() + suffix.len());
                 PatternKind::Array {
-                    prefix: self.to_pats(prefix),
-                    slice: self.to_opt_pat(slice),
-                    suffix: self.to_pats(suffix),
+                    prefix: self.to_patterns(prefix),
+                    slice: self.to_opt_pattern(slice),
+                    suffix: self.to_patterns(suffix),
                 }
             }
 
             _ => {
-                self.cx.tcx.sess.span_bug(pat.span, "unexpanded macro or bad constant etc");
+                self.cx.tcx.sess.span_bug(span, "unexpanded macro or bad constant etc");
             }
         }
     }
 
     fn variant_or_leaf(&mut self,
-                       pat: &'tcx hir::Pat,
+                       pat: &hir::Pat,
                        subpatterns: Vec<FieldPattern<'tcx>>)
                        -> PatternKind<'tcx> {
         let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
index da200a8a33f083616333f42641c4a77c9b96fe88..24fcc2f4fcd56a6eac016b554ff81be6dc5d7914 100644 (file)
@@ -61,3 +61,13 @@ impl<'a,'tcx:'a,T,U> ToRef for &'tcx Vec<T>
         self.iter().map(|expr| expr.to_ref()).collect()
     }
 }
+
+impl<'a,'tcx:'a,T,U> ToRef for &'tcx P<[T]>
+    where &'tcx T: ToRef<Output=U>
+{
+    type Output = Vec<U>;
+
+    fn to_ref(self) -> Vec<U> {
+        self.iter().map(|expr| expr.to_ref()).collect()
+    }
+}
index 9a774ff3f57125902d72eae3323516286526429f..fb81cc7e6d97a4394d65702fbf809466616046d0 100644 (file)
 //! unit-tested and separated from the Rust source and compiler data
 //! structures.
 
-use rustc::mir::repr::{BinOp, BorrowKind, Field, Literal, Mutability, UnOp};
+use rustc::mir::repr::{BinOp, BorrowKind, Field, Literal, Mutability, UnOp, ItemKind};
+use rustc::middle::const_eval::ConstVal;
 use rustc::middle::def_id::DefId;
 use rustc::middle::region::CodeExtent;
 use rustc::middle::subst::Substs;
-use rustc::middle::ty::{AdtDef, ClosureSubsts, Region, Ty};
+use rustc::middle::ty::{self, AdtDef, ClosureSubsts, Region, Ty};
 use rustc_front::hir;
 use syntax::ast;
 use syntax::codemap::Span;
@@ -29,6 +30,7 @@ pub mod cx;
 #[derive(Clone, Debug)]
 pub struct ItemRef<'tcx> {
     pub ty: Ty<'tcx>,
+    pub kind: ItemKind,
     pub def_id: DefId,
     pub substs: &'tcx Substs<'tcx>,
 }
@@ -122,6 +124,7 @@ pub enum ExprKind<'tcx> {
         value: ExprRef<'tcx>,
     },
     Call {
+        ty: ty::Ty<'tcx>,
         fun: ExprRef<'tcx>,
         args: Vec<ExprRef<'tcx>>,
     },
@@ -304,7 +307,7 @@ pub enum PatternKind<'tcx> {
     }, // box P, &P, &mut P, etc
 
     Constant {
-        value: Literal<'tcx>,
+        value: ConstVal,
     },
 
     Range {
index 710d5ba4b43684d1af0dd32aff4fae439f857f36..53609e65d075c1a13cb6a1b1920a80223259262d 100644 (file)
@@ -17,9 +17,10 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
 #![crate_name = "rustc_mir"]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
+#![unstable(feature = "rustc_private", issue = "27812")]
 
 #![feature(rustc_private)]
-#![feature(into_cow)]
+#![feature(staged_api)]
 
 #[macro_use] extern crate log;
 extern crate graphviz as dot;
@@ -30,8 +31,8 @@ extern crate rustc_back;
 extern crate syntax;
 
 pub mod build;
-pub mod mir_map;
+pub mod graphviz;
 mod hair;
-mod graphviz;
+pub mod mir_map;
+pub mod pretty;
 pub mod transform;
-
index 39a315f3c41c714fcb5028de3d23fde86341912e..ac15878dc5136232d6f0db35a6d1ebef2d13d298 100644 (file)
@@ -21,8 +21,10 @@ extern crate rustc;
 extern crate rustc_front;
 
 use build;
-use dot;
+use graphviz;
+use pretty;
 use transform::*;
+use rustc::dep_graph::DepNode;
 use rustc::mir::repr::Mir;
 use hair::cx::Cx;
 use std::fs::File;
@@ -47,7 +49,7 @@ pub fn build_mir_for_crate<'tcx>(tcx: &ty::ctxt<'tcx>) -> MirMap<'tcx> {
             tcx: tcx,
             map: &mut map,
         };
-        tcx.map.krate().visit_all_items(&mut dump);
+        tcx.visit_all_items_in_krate(DepNode::MirMapConstruction, &mut dump);
     }
     map
 }
@@ -152,27 +154,29 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
                                          .flat_map(|a| a.meta_item_list())
                                          .flat_map(|l| l.iter());
                 for item in meta_item_list {
-                    if item.check_name("graphviz") {
+                    if item.check_name("graphviz") || item.check_name("pretty") {
                         match item.value_str() {
                             Some(s) => {
-                                match
-                                    File::create(format!("{}{}", prefix, s))
-                                    .and_then(|ref mut output| dot::render(&mir, output))
-                                {
-                                    Ok(()) => { }
-                                    Err(e) => {
-                                        self.tcx.sess.span_fatal(
-                                            item.span,
-                                            &format!("Error writing graphviz \
-                                                      results to `{}`: {}",
-                                                     s, e));
+                                let filename = format!("{}{}", prefix, s);
+                                let result = File::create(&filename).and_then(|ref mut output| {
+                                    if item.check_name("graphviz") {
+                                        graphviz::write_mir_graphviz(&mir, output)
+                                    } else {
+                                        pretty::write_mir_pretty(&mir, output)
                                     }
+                                });
+
+                                if let Err(e) = result {
+                                    self.tcx.sess.span_fatal(
+                                        item.span,
+                                        &format!("Error writing MIR {} results to `{}`: {}",
+                                                 item.name(), filename, e));
                                 }
                             }
                             None => {
                                 self.tcx.sess.span_err(
                                     item.span,
-                                    "graphviz attribute requires a path");
+                                    &format!("{} attribute requires a path", item.name()));
                             }
                         }
                     }
diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs
new file mode 100644 (file)
index 0000000..ea4036a
--- /dev/null
@@ -0,0 +1,85 @@
+// 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 rustc::mir::repr::*;
+use rustc::middle::ty;
+use std::io::{self, Write};
+
+const INDENT: &'static str = "    ";
+
+/// Write out a human-readable textual representation for the given MIR.
+pub fn write_mir_pretty<W: Write>(mir: &Mir, w: &mut W) -> io::Result<()> {
+    try!(write_mir_intro(mir, w));
+
+    // Nodes
+    for block in mir.all_basic_blocks() {
+        try!(write_basic_block(block, mir, w));
+    }
+
+    writeln!(w, "}}")
+}
+
+/// Write out a human-readable textual representation for the given basic block.
+fn write_basic_block<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
+    let data = mir.basic_block_data(block);
+
+    // Basic block label at the top.
+    try!(writeln!(w, "\n{}{:?}: {{", INDENT, block));
+
+    // List of statements in the middle.
+    for statement in &data.statements {
+        try!(writeln!(w, "{0}{0}{1:?};", INDENT, statement));
+    }
+
+    // Terminator at the bottom.
+    try!(writeln!(w, "{0}{0}{1:?};", INDENT, data.terminator()));
+
+    writeln!(w, "{}}}", INDENT)
+}
+
+/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
+/// local variables (both user-defined bindings and compiler temporaries).
+fn write_mir_intro<W: Write>(mir: &Mir, w: &mut W) -> io::Result<()> {
+    try!(write!(w, "fn("));
+
+    // fn argument types.
+    for (i, arg) in mir.arg_decls.iter().enumerate() {
+        if i > 0 {
+            try!(write!(w, ", "));
+        }
+        try!(write!(w, "{:?}: {}", Lvalue::Arg(i as u32), arg.ty));
+    }
+
+    try!(write!(w, ") -> "));
+
+    // fn return type.
+    match mir.return_ty {
+        ty::FnOutput::FnConverging(ty) => try!(write!(w, "{}", ty)),
+        ty::FnOutput::FnDiverging => try!(write!(w, "!")),
+    }
+
+    try!(writeln!(w, " {{"));
+
+    // User variable types (including the user's name in a comment).
+    for (i, var) in mir.var_decls.iter().enumerate() {
+        try!(write!(w, "{}let ", INDENT));
+        if var.mutability == Mutability::Mut {
+            try!(write!(w, "mut "));
+        }
+        try!(writeln!(w, "{:?}: {}; // {}", Lvalue::Var(i as u32), var.ty, var.name));
+    }
+
+    // Compiler-introduced temporary types.
+    for (i, temp) in mir.temp_decls.iter().enumerate() {
+        try!(writeln!(w, "{}let mut {:?}: {};", INDENT, Lvalue::Temp(i as u32), temp.ty));
+    }
+
+    Ok(())
+}
index 1eb3bfd7e02299daeebdf575b1419adebd493c4e..9679654d958e9a24e202443a272f5a52e762a9f8 100644 (file)
@@ -59,7 +59,7 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> {
             self.erase_regions_statement(statement);
         }
 
-        self.erase_regions_terminator(&mut basic_block.terminator);
+        self.erase_regions_terminator(basic_block.terminator_mut());
     }
 
     fn erase_regions_statement(&mut self,
@@ -79,9 +79,8 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> {
                                 terminator: &mut Terminator<'tcx>) {
         match *terminator {
             Terminator::Goto { .. } |
-            Terminator::Diverge |
-            Terminator::Return |
-            Terminator::Panic { .. } => {
+            Terminator::Resume |
+            Terminator::Return => {
                 /* nothing to do */
             }
             Terminator::If { ref mut cond, .. } => {
@@ -90,23 +89,14 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> {
             Terminator::Switch { ref mut discr, .. } => {
                 self.erase_regions_lvalue(discr);
             }
-            Terminator::SwitchInt {
-                ref mut discr,
-                ref mut switch_ty,
-                ..
-            } => {
+            Terminator::SwitchInt { ref mut discr, ref mut switch_ty, .. } => {
                 self.erase_regions_lvalue(discr);
                 *switch_ty = self.tcx.erase_regions(switch_ty);
             },
-            Terminator::Call {
-                data: CallData {
-                    ref mut destination,
-                    ref mut func,
-                    ref mut args
-                },
-                ..
-            } => {
-                self.erase_regions_lvalue(destination);
+            Terminator::Call { ref mut func, ref mut args, ref mut kind } => {
+                if let Some(destination) = kind.destination_mut() {
+                    self.erase_regions_lvalue(destination);
+                }
                 self.erase_regions_operand(func);
                 for arg in &mut *args {
                     self.erase_regions_operand(arg);
index 558276a13a8b8f033f4157a7c58b8cc7cca280ed..7a5a00a8d560b6e83cde98c3418e7094c72730c6 100644 (file)
@@ -10,7 +10,6 @@
 
 use rustc::middle::const_eval::ConstVal;
 use rustc::mir::repr::*;
-use std::mem;
 use transform::util;
 use transform::MirPass;
 
@@ -27,11 +26,10 @@ impl SimplifyCfg {
         // These blocks are always required.
         seen[START_BLOCK.index()] = true;
         seen[END_BLOCK.index()] = true;
-        seen[DIVERGE_BLOCK.index()] = true;
 
         let mut worklist = vec![START_BLOCK];
         while let Some(bb) = worklist.pop() {
-            for succ in mir.basic_block_data(bb).terminator.successors() {
+            for succ in mir.basic_block_data(bb).terminator().successors() {
                 if !seen[succ.index()] {
                     seen[succ.index()] = true;
                     worklist.push(*succ);
@@ -51,7 +49,7 @@ impl SimplifyCfg {
 
             while mir.basic_block_data(target).statements.is_empty() {
                 match mir.basic_block_data(target).terminator {
-                    Terminator::Goto { target: next } => {
+                    Some(Terminator::Goto { target: next }) => {
                         if seen.contains(&next) {
                             return None;
                         }
@@ -67,9 +65,9 @@ impl SimplifyCfg {
 
         let mut changed = false;
         for bb in mir.all_basic_blocks() {
-            // Temporarily swap out the terminator we're modifying to keep borrowck happy
-            let mut terminator = Terminator::Diverge;
-            mem::swap(&mut terminator, &mut mir.basic_block_data_mut(bb).terminator);
+            // 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");
 
             for target in terminator.successors_mut() {
                 let new_target = match final_target(mir, *target) {
@@ -80,10 +78,8 @@ impl SimplifyCfg {
                 changed |= *target != new_target;
                 *target = new_target;
             }
-
-            mir.basic_block_data_mut(bb).terminator = terminator;
+            mir.basic_block_data_mut(bb).terminator = Some(terminator);
         }
-
         changed
     }
 
@@ -91,14 +87,13 @@ impl SimplifyCfg {
         let mut changed = false;
 
         for bb in mir.all_basic_blocks() {
-            // Temporarily swap out the terminator we're modifying to keep borrowck happy
-            let mut terminator = Terminator::Diverge;
-            mem::swap(&mut terminator, &mut mir.basic_block_data_mut(bb).terminator);
+            let basic_block = mir.basic_block_data_mut(bb);
+            let mut terminator = basic_block.terminator_mut();
 
-            mir.basic_block_data_mut(bb).terminator = match terminator {
-                Terminator::If { ref targets, .. } if targets[0] == targets[1] => {
+            *terminator = match *terminator {
+                Terminator::If { ref targets, .. } if targets.0 == targets.1 => {
                     changed = true;
-                    Terminator::Goto { target: targets[0] }
+                    Terminator::Goto { target: targets.0 }
                 }
                 Terminator::If { ref targets, cond: Operand::Constant(Constant {
                     literal: Literal::Value {
@@ -106,13 +101,16 @@ impl SimplifyCfg {
                     }, ..
                 }) } => {
                     changed = true;
-                    let target_idx = if cond { 0 } else { 1 };
-                    Terminator::Goto { target: targets[target_idx] }
+                    if cond {
+                        Terminator::Goto { target: targets.0 }
+                    } else {
+                        Terminator::Goto { target: targets.1 }
+                    }
                 }
                 Terminator::SwitchInt { ref targets, .. }  if targets.len() == 1 => {
                     Terminator::Goto { target: targets[0] }
                 }
-                _ => terminator
+                _ => continue
             }
         }
 
@@ -128,7 +126,6 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
             changed |= self.remove_goto_chains(mir);
             self.remove_dead_blocks(mir);
         }
-
         // FIXME: Should probably be moved into some kind of pass manager
         mir.basic_blocks.shrink_to_fit();
     }
index 951026945448527b40afda2e515e95e6e4524cca..7e44beb18a2e96c8029a5bf61974308ba0fa383f 100644 (file)
@@ -15,7 +15,7 @@ use rustc::mir::repr::*;
 /// in a single pass
 pub fn update_basic_block_ids(mir: &mut Mir, replacements: &[BasicBlock]) {
     for bb in mir.all_basic_blocks() {
-        for target in mir.basic_block_data_mut(bb).terminator.successors_mut() {
+        for target in mir.basic_block_data_mut(bb).terminator_mut().successors_mut() {
             *target = replacements[target.index()];
         }
     }
diff --git a/src/librustc_passes/const_fn.rs b/src/librustc_passes/const_fn.rs
new file mode 100644 (file)
index 0000000..cda5267
--- /dev/null
@@ -0,0 +1,117 @@
+// 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.
+
+//! Verifies that const fn arguments are immutable by value bindings
+//! and the const fn body doesn't contain any statements
+
+use rustc::session::Session;
+
+use syntax::ast;
+use syntax::visit::{self, Visitor, FnKind};
+use syntax::codemap::Span;
+
+pub fn check_crate(sess: &Session, krate: &ast::Crate) {
+    visit::walk_crate(&mut CheckConstFn{ sess: sess }, krate);
+    sess.abort_if_errors();
+}
+
+struct CheckConstFn<'a> {
+    sess: &'a Session,
+}
+
+struct CheckBlock<'a> {
+    sess: &'a Session,
+    kind: &'static str,
+}
+
+impl<'a, 'v> Visitor<'v> for CheckBlock<'a> {
+    fn visit_block(&mut self, block: &'v ast::Block) {
+        check_block(&self.sess, block, self.kind);
+        CheckConstFn{ sess: self.sess}.visit_block(block);
+    }
+    fn visit_expr(&mut self, e: &'v ast::Expr) {
+        if let ast::ExprClosure(..) = e.node {
+            CheckConstFn{ sess: self.sess}.visit_expr(e);
+        } else {
+            visit::walk_expr(self, e);
+        }
+    }
+    fn visit_item(&mut self, _i: &'v ast::Item) { panic!("should be handled in CheckConstFn") }
+    fn visit_fn(&mut self,
+                _fk: FnKind<'v>,
+                _fd: &'v ast::FnDecl,
+                _b: &'v ast::Block,
+                _s: Span,
+                _fn_id: ast::NodeId) { panic!("should be handled in CheckConstFn") }
+}
+
+fn check_block(sess: &Session, b: &ast::Block, kind: &'static str) {
+    // Check all statements in the block
+    for stmt in &b.stmts {
+        let span = match stmt.node {
+            ast::StmtDecl(ref decl, _) => {
+                match decl.node {
+                    ast::DeclLocal(_) => decl.span,
+
+                    // Item statements are allowed
+                    ast::DeclItem(_) => continue,
+                }
+            }
+            ast::StmtExpr(ref expr, _) => expr.span,
+            ast::StmtSemi(ref semi, _) => semi.span,
+            ast::StmtMac(..) => unreachable!(),
+        };
+        span_err!(sess, span, E0016,
+                  "blocks in {}s are limited to items and tail expressions", kind);
+    }
+}
+
+impl<'a, 'v> Visitor<'v> for CheckConstFn<'a> {
+    fn visit_item(&mut self, i: &'v ast::Item) {
+        visit::walk_item(self, i);
+        match i.node {
+            ast::ItemConst(_, ref e) => {
+                CheckBlock{ sess: self.sess, kind: "constant"}.visit_expr(e)
+            },
+            ast::ItemStatic(_, _, ref e) => {
+                CheckBlock{ sess: self.sess, kind: "static"}.visit_expr(e)
+            },
+            _ => {},
+        }
+    }
+
+    fn visit_fn(&mut self,
+                fk: FnKind<'v>,
+                fd: &'v ast::FnDecl,
+                b: &'v ast::Block,
+                s: Span,
+                _fn_id: ast::NodeId) {
+        visit::walk_fn(self, fk, fd, b, s);
+        match fk {
+            FnKind::ItemFn(_, _, _, ast::Constness::Const, _, _) => {},
+            FnKind::Method(_, m, _) if m.constness == ast::Constness::Const => {},
+            _ => return,
+        }
+
+        // Ensure the arguments are simple, not mutable/by-ref or patterns.
+        for arg in &fd.inputs {
+            match arg.pat.node {
+                ast::PatWild => {}
+                ast::PatIdent(ast::BindingMode::ByValue(ast::MutImmutable), _, None) => {}
+                _ => {
+                    span_err!(self.sess, arg.pat.span, E0022,
+                              "arguments of constant functions can only \
+                               be immutable by-value bindings");
+                }
+            }
+        }
+        check_block(&self.sess, b, "const function");
+    }
+}
diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs
new file mode 100644 (file)
index 0000000..380eada
--- /dev/null
@@ -0,0 +1,50 @@
+// 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.
+
+#![allow(non_snake_case)]
+
+register_long_diagnostics! {
+E0016: r##"
+Blocks in constants may only contain items (such as constant, function
+definition, etc...) and a tail expression. Example:
+
+```
+const FOO: i32 = { let x = 0; x }; // 'x' isn't an item!
+```
+
+To avoid it, you have to replace the non-item object:
+
+```
+const FOO: i32 = { const X : i32 = 0; X };
+```
+"##,
+
+E0022: r##"
+Constant functions are not allowed to mutate anything. Thus, binding to an
+argument with a mutable pattern is not allowed. For example,
+
+```
+const fn foo(mut x: u8) {
+    // do stuff
+}
+```
+
+is bad because the function body may not mutate `x`.
+
+Remove any mutable bindings from the argument list to fix this error. In case
+you need to mutate the argument, try lazily initializing a global variable
+instead of using a `const fn`, or refactoring the code to a functional style to
+avoid mutation if possible.
+"##,
+}
+
+register_diagnostics! {
+    E0472, // asm! is unsupported on this target
+}
diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs
new file mode 100644 (file)
index 0000000..4adaa0c
--- /dev/null
@@ -0,0 +1,36 @@
+// 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.
+
+//! Various checks
+//!
+//! # Note
+//!
+//! This API is completely unstable and subject to change.
+
+#![crate_name = "rustc_passes"]
+#![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/")]
+
+#![feature(rustc_diagnostic_macros)]
+#![feature(staged_api)]
+#![feature(rustc_private)]
+
+extern crate core;
+extern crate rustc;
+
+#[macro_use] extern crate syntax;
+
+pub mod diagnostics;
+pub mod const_fn;
+pub mod no_asm;
diff --git a/src/librustc_passes/no_asm.rs b/src/librustc_passes/no_asm.rs
new file mode 100644 (file)
index 0000000..3022d9f
--- /dev/null
@@ -0,0 +1,41 @@
+// 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.
+
+/// Run over the whole crate and check for ExprInlineAsm.
+/// Inline asm isn't allowed on virtual ISA based targets, so we reject it
+/// here.
+
+use rustc::session::Session;
+
+use syntax::ast;
+use syntax::visit::Visitor;
+use syntax::visit;
+
+pub fn check_crate(sess: &Session, krate: &ast::Crate) {
+    if sess.target.target.options.allow_asm { return; }
+
+    visit::walk_crate(&mut CheckNoAsm { sess: sess, }, krate);
+}
+
+#[derive(Copy, Clone)]
+struct CheckNoAsm<'a> {
+    sess: &'a Session,
+}
+
+impl<'a, 'v> Visitor<'v> for CheckNoAsm<'a> {
+    fn visit_expr(&mut self, e: &ast::Expr) {
+        match e.node {
+            ast::ExprInlineAsm(_) => span_err!(self.sess, e.span, E0472,
+                                               "asm! is unsupported on this target"),
+            _ => {},
+        }
+        visit::walk_expr(self, e)
+    }
+}
index 3de6863652ab79a47c2e81bccd6316bc3d85d50f..e857434682d158a8a16a844bdcb465493733227e 100644 (file)
@@ -8,10 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_platform_intrinsics"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![feature(staged_api, rustc_private)]
index 00f58c6af91554d8e63e6b1234e40be39413db70..5adde4304f57c9541b56f76523f0be4655863cb1 100644 (file)
@@ -13,7 +13,7 @@
 use syntax::ast;
 use syntax::attr;
 use syntax::codemap::Span;
-use syntax::diagnostic;
+use syntax::errors;
 use rustc_front::intravisit::Visitor;
 use rustc_front::hir;
 
@@ -33,7 +33,7 @@ impl<'v> Visitor<'v> for RegistrarFinder {
 }
 
 /// Find the function marked with `#[plugin_registrar]`, if any.
-pub fn find_plugin_registrar(diagnostic: &diagnostic::SpanHandler,
+pub fn find_plugin_registrar(diagnostic: &errors::Handler,
                              krate: &hir::Crate)
                              -> Option<ast::NodeId> {
     let mut finder = RegistrarFinder { registrars: Vec::new() };
@@ -46,11 +46,12 @@ pub fn find_plugin_registrar(diagnostic: &diagnostic::SpanHandler,
             Some(node_id)
         },
         _ => {
-            diagnostic.handler().err("multiple plugin registration functions found");
+            let mut e = diagnostic.struct_err("multiple plugin registration functions found");
             for &(_, span) in &finder.registrars {
-                diagnostic.span_note(span, "one is here");
+                e.span_note(span, "one is here");
             }
-            diagnostic.handler().abort_if_errors();
+            e.emit();
+            diagnostic.abort_if_errors();
             unreachable!();
         }
     }
index 5dedef7ab6c79b6d85f3eb461916b842cf5d38eb..333c226c2a373f0de6af00782b6f1018cd1ee44c 100644 (file)
 //! See the [Plugins Chapter](../../book/compiler-plugins.html) of the book
 //! for more examples.
 
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_plugin"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
index 0f9f00e1b49a9ffa9611b1313362baf5238521a2..3fbe3bc200534ac4a93173652b28a653af81221f 100644 (file)
@@ -21,13 +21,14 @@ trait Foo {
     fn dummy(&self) { }
 }
 
-pub trait Bar : Foo {} // error: private trait in exported type parameter bound
+pub trait Bar : Foo {} // error: private trait in public interface
 pub struct Bar<T: Foo>(pub T); // same error
 pub fn foo<T: Foo> (t: T) {} // same error
 ```
 
-To solve this error, please ensure that the trait is also public and accessible
-at the same level of the public functions or types which are bound on it.
+To solve this error, please ensure that the trait is also public. The trait
+can be made inaccessible if necessary by placing it into a private inner module,
+but it still has to be marked with `pub`.
 Example:
 
 ```
@@ -42,20 +43,22 @@ pub fn foo<T: Foo> (t: T) {} // ok!
 "##,
 
 E0446: r##"
-A private type was used in an exported type signature. Erroneous code example:
+A private type was used in a public type signature. Erroneous code example:
 
 ```
 mod Foo {
     struct Bar(u32);
 
-    pub fn bar() -> Bar { // error: private type in exported type signature
+    pub fn bar() -> Bar { // error: private type in public interface
         Bar(0)
     }
 }
 ```
 
-To solve this error, please ensure that the type is also public and accessible
-at the same level of the public functions or types which use it. Example:
+To solve this error, please ensure that the type is also public. The type
+can be made inaccessible if necessary by placing it into a private inner module,
+but it still has to be marked with `pub`.
+Example:
 
 ```
 mod Foo {
index 9f70598198eb9631f705991d498efe48a81ca68b..d3da93a3e080d53508928af23f0c8b0068568b03 100644 (file)
@@ -8,11 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_privacy"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -38,6 +35,8 @@ use std::mem::replace;
 use rustc_front::hir;
 use rustc_front::intravisit::{self, Visitor};
 
+use rustc::dep_graph::DepNode;
+use rustc::lint;
 use rustc::middle::def;
 use rustc::middle::def_id::DefId;
 use rustc::middle::privacy::{AccessLevel, AccessLevels};
@@ -45,8 +44,8 @@ use rustc::middle::privacy::ImportUse::*;
 use rustc::middle::privacy::LastPrivate::*;
 use rustc::middle::privacy::PrivateDep::*;
 use rustc::middle::privacy::ExternalExports;
-use rustc::middle::ty::{self, Ty};
-use rustc::util::nodemap::NodeMap;
+use rustc::middle::ty;
+use rustc::util::nodemap::{NodeMap, NodeSet};
 use rustc::front::map as ast_map;
 
 use syntax::ast;
@@ -330,9 +329,11 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
         // This code is here instead of in visit_item so that the
         // crate module gets processed as well.
         if self.prev_level.is_some() {
-            for export in self.export_map.get(&id).expect("module isn't found in export map") {
-                if let Some(node_id) = self.tcx.map.as_local_node_id(export.def_id) {
-                    self.update(node_id, Some(AccessLevel::Exported));
+            if let Some(exports) = self.export_map.get(&id) {
+                for export in exports {
+                    if let Some(node_id) = self.tcx.map.as_local_node_id(export.def_id) {
+                        self.update(node_id, Some(AccessLevel::Exported));
+                    }
                 }
             }
         }
@@ -599,13 +600,11 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
         match result {
             None => true,
             Some((span, msg, note)) => {
-                self.tcx.sess.span_err(span, &msg[..]);
-                match note {
-                    Some((span, msg)) => {
-                        self.tcx.sess.span_note(span, &msg[..])
-                    }
-                    None => {},
+                let mut err = self.tcx.sess.struct_span_err(span, &msg[..]);
+                if let Some((span, msg)) = note {
+                    err.span_note(span, &msg[..]);
                 }
+                err.emit();
                 false
             },
         }
@@ -1030,10 +1029,12 @@ impl<'a, 'tcx> SanePrivacyVisitor<'a, 'tcx> {
     fn check_sane_privacy(&self, item: &hir::Item) {
         let check_inherited = |sp, vis, note: &str| {
             if vis != hir::Inherited {
-                span_err!(self.tcx.sess, sp, E0449, "unnecessary visibility qualifier");
+                let mut err = struct_span_err!(self.tcx.sess, sp, E0449,
+                                               "unnecessary visibility qualifier");
                 if !note.is_empty() {
-                    self.tcx.sess.span_note(sp, note);
+                    err.span_note(sp, note);
                 }
+                err.emit();
             }
         };
 
@@ -1101,14 +1102,23 @@ impl<'a, 'tcx> SanePrivacyVisitor<'a, 'tcx> {
     }
 }
 
-struct VisiblePrivateTypesVisitor<'a, 'tcx: 'a> {
+///////////////////////////////////////////////////////////////////////////////
+/// Obsolete visitors for checking for private items in public interfaces.
+/// These visitors are supposed to be kept in frozen state and produce an
+/// "old error node set". For backward compatibility the new visitor reports
+/// warnings instead of hard errors when the erroneous node is not in this old set.
+///////////////////////////////////////////////////////////////////////////////
+
+struct ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx: 'a> {
     tcx: &'a ty::ctxt<'tcx>,
     access_levels: &'a AccessLevels,
     in_variant: bool,
+    // set of errors produced by this obsolete visitor
+    old_error_set: NodeSet,
 }
 
-struct CheckTypeForPrivatenessVisitor<'a, 'b: 'a, 'tcx: 'b> {
-    inner: &'a VisiblePrivateTypesVisitor<'b, 'tcx>,
+struct ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b: 'a, 'tcx: 'b> {
+    inner: &'a ObsoleteVisiblePrivateTypesVisitor<'b, 'tcx>,
     /// whether the type refers to private types.
     contains_private: bool,
     /// whether we've recurred at all (i.e. if we're pointing at the
@@ -1118,7 +1128,7 @@ struct CheckTypeForPrivatenessVisitor<'a, 'b: 'a, 'tcx: 'b> {
     outer_type_is_public_path: bool,
 }
 
-impl<'a, 'tcx> VisiblePrivateTypesVisitor<'a, 'tcx> {
+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.)
@@ -1146,14 +1156,11 @@ impl<'a, 'tcx> VisiblePrivateTypesVisitor<'a, 'tcx> {
         self.access_levels.is_public(trait_id)
     }
 
-    fn check_ty_param_bound(&self,
+    fn check_ty_param_bound(&mut self,
                             ty_param_bound: &hir::TyParamBound) {
         if let hir::TraitTyParamBound(ref trait_ref, _) = *ty_param_bound {
-            if !self.tcx.sess.features.borrow().visible_private_types &&
-                self.path_is_private_type(trait_ref.trait_ref.ref_id) {
-                    let span = trait_ref.trait_ref.path.span;
-                    span_err!(self.tcx.sess, span, E0445,
-                              "private trait in exported type parameter bound");
+            if self.path_is_private_type(trait_ref.trait_ref.ref_id) {
+                self.old_error_set.insert(trait_ref.trait_ref.ref_id);
             }
         }
     }
@@ -1163,7 +1170,7 @@ impl<'a, 'tcx> VisiblePrivateTypesVisitor<'a, 'tcx> {
     }
 }
 
-impl<'a, 'b, 'tcx, 'v> Visitor<'v> for CheckTypeForPrivatenessVisitor<'a, 'b, 'tcx> {
+impl<'a, 'b, 'tcx, 'v> Visitor<'v> for ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b, 'tcx> {
     fn visit_ty(&mut self, ty: &hir::Ty) {
         if let hir::TyPath(..) = ty.node {
             if self.inner.path_is_private_type(ty.id) {
@@ -1183,7 +1190,7 @@ impl<'a, 'b, 'tcx, 'v> Visitor<'v> for CheckTypeForPrivatenessVisitor<'a, 'b, 't
     fn visit_expr(&mut self, _: &hir::Expr) {}
 }
 
-impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> {
+impl<'a, 'tcx, 'v> Visitor<'v> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
     /// We want to visit items in the context of their containing
     /// module and so forth, so supply a crate for doing a deep walk.
     fn visit_nested_item(&mut self, item: hir::ItemId) {
@@ -1224,7 +1231,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> {
 
                 // check the properties of the Self type:
                 {
-                    let mut visitor = CheckTypeForPrivatenessVisitor {
+                    let mut visitor = ObsoleteCheckTypeForPrivatenessVisitor {
                         inner: self,
                         contains_private: false,
                         at_outer_type: true,
@@ -1401,11 +1408,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> {
 
     fn visit_ty(&mut self, t: &hir::Ty) {
         debug!("VisiblePrivateTypesVisitor checking ty {:?}", t);
-        if let hir::TyPath(_, ref p) = t.node {
-            if !self.tcx.sess.features.borrow().visible_private_types &&
-                self.path_is_private_type(t.id) {
-                span_err!(self.tcx.sess, p.span, E0446,
-                          "private type in exported type signature");
+        if let hir::TyPath(..) = t.node {
+            if self.path_is_private_type(t.id) {
+                self.old_error_set.insert(t.id);
             }
         }
         intravisit::walk_ty(self, t)
@@ -1439,10 +1444,241 @@ impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> {
     fn visit_expr(&mut self, _: &hir::Expr) {}
 }
 
+///////////////////////////////////////////////////////////////////////////////
+/// SearchInterfaceForPrivateItemsVisitor traverses an item's interface and
+/// finds any private components in it.
+/// PrivateItemsInPublicInterfacesVisitor ensures there are no private types
+/// and traits in public interfaces.
+///////////////////////////////////////////////////////////////////////////////
+
+struct SearchInterfaceForPrivateItemsVisitor<'a, 'tcx: 'a> {
+    tcx: &'a ty::ctxt<'tcx>,
+    // Do not report an error when a private type is found
+    is_quiet: bool,
+    // Is private component found?
+    is_public: bool,
+    old_error_set: &'a NodeSet,
+}
+
+impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
+    // Check if the type alias contain private types when substituted
+    fn is_public_type_alias(&self, item: &hir::Item, path: &hir::Path) -> bool {
+        // We substitute type aliases only when determining impl publicity
+        // FIXME: This will probably change and all type aliases will be substituted,
+        // requires an amendment to RFC 136.
+        if !self.is_quiet {
+            return false
+        }
+        // Type alias is considered public if the aliased type is
+        // public, even if the type alias itself is private. So, something
+        // like `type A = u8; pub fn f() -> A {...}` doesn't cause an error.
+        if let hir::ItemTy(ref ty, ref generics) = item.node {
+            let mut check = SearchInterfaceForPrivateItemsVisitor { is_public: true, ..*self };
+            check.visit_ty(ty);
+            // If a private type alias with default type parameters is used in public
+            // interface we must ensure, that the defaults are public if they are actually used.
+            // ```
+            // type Alias<T = Private> = T;
+            // pub fn f() -> Alias {...} // `Private` is implicitly used here, so it must be public
+            // ```
+            let provided_params = path.segments.last().unwrap().parameters.types().len();
+            for ty_param in &generics.ty_params[provided_params..] {
+                if let Some(ref default_ty) = ty_param.default {
+                    check.visit_ty(default_ty);
+                }
+            }
+            check.is_public
+        } else {
+            false
+        }
+    }
+}
+
+impl<'a, 'tcx: 'a, 'v> Visitor<'v> for SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
+    fn visit_ty(&mut self, ty: &hir::Ty) {
+        if self.is_quiet && !self.is_public {
+            // We are in quiet mode and a private type is already found, no need to proceed
+            return
+        }
+        if let hir::TyPath(_, ref path) = ty.node {
+            let def = self.tcx.def_map.borrow().get(&ty.id).unwrap().full_def();
+            match def {
+                def::DefPrimTy(..) | def::DefSelfTy(..) | def::DefTyParam(..) => {
+                    // Public
+                }
+                def::DefAssociatedTy(..) if self.is_quiet => {
+                    // Conservatively approximate the whole type alias as public without
+                    // recursing into its components when determining impl publicity.
+                    // For example, `impl <Type as Trait>::Alias {...}` may be a public impl
+                    // even if both `Type` and `Trait` are private.
+                    // Ideally, associated types should be substituted in the same way as
+                    // free type aliases, but this isn't done yet.
+                    return
+                }
+                def::DefStruct(def_id) | def::DefTy(def_id, _) |
+                def::DefTrait(def_id) | def::DefAssociatedTy(def_id, _) => {
+                    // Non-local means public (private items can't leave their crate, modulo bugs)
+                    if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) {
+                        let item = self.tcx.map.expect_item(node_id);
+                        if item.vis != hir::Public && !self.is_public_type_alias(item, path) {
+                            if !self.is_quiet {
+                                if self.old_error_set.contains(&ty.id) {
+                                    span_err!(self.tcx.sess, ty.span, E0446,
+                                              "private type in public interface");
+                                } else {
+                                    self.tcx.sess.add_lint (
+                                        lint::builtin::PRIVATE_IN_PUBLIC,
+                                        node_id,
+                                        ty.span,
+                                        format!("private type in public interface"),
+                                    );
+                                }
+                            }
+                            self.is_public = false;
+                        }
+                    }
+                }
+                _ => {}
+            }
+        }
+
+        intravisit::walk_ty(self, ty);
+    }
+
+    fn visit_trait_ref(&mut self, trait_ref: &hir::TraitRef) {
+        if self.is_quiet && !self.is_public {
+            // We are in quiet mode and a private type is already found, no need to proceed
+            return
+        }
+        // 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);
+        if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) {
+            let item = self.tcx.map.expect_item(node_id);
+            if item.vis != hir::Public {
+                if !self.is_quiet {
+                    if self.old_error_set.contains(&trait_ref.ref_id) {
+                        span_err!(self.tcx.sess, trait_ref.path.span, E0445,
+                                  "private trait in public interface");
+                    } else {
+                        self.tcx.sess.add_lint(lint::builtin::PRIVATE_IN_PUBLIC,
+                                               node_id,
+                                               trait_ref.path.span,
+                                               "private trait in public interface (error E0445)"
+                                                    .to_string());
+                    }
+                }
+                self.is_public = false;
+            }
+        }
+
+        intravisit::walk_trait_ref(self, trait_ref);
+    }
+
+    // Don't recurse into function bodies
+    fn visit_block(&mut self, _: &hir::Block) {}
+    // Don't recurse into expressions in array sizes or const initializers
+    fn visit_expr(&mut self, _: &hir::Expr) {}
+    // Don't recurse into patterns in function arguments
+    fn visit_pat(&mut self, _: &hir::Pat) {}
+}
+
+struct PrivateItemsInPublicInterfacesVisitor<'a, 'tcx: 'a> {
+    tcx: &'a ty::ctxt<'tcx>,
+    old_error_set: &'a NodeSet,
+}
+
+impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> {
+    // A type is considered public if it doesn't contain any private components
+    fn is_public_ty(&self, ty: &hir::Ty) -> bool {
+        let mut check = SearchInterfaceForPrivateItemsVisitor {
+            tcx: self.tcx, is_quiet: true, is_public: true, old_error_set: self.old_error_set
+        };
+        check.visit_ty(ty);
+        check.is_public
+    }
+
+    // A trait reference is considered public if it doesn't contain any private components
+    fn is_public_trait_ref(&self, trait_ref: &hir::TraitRef) -> bool {
+        let mut check = SearchInterfaceForPrivateItemsVisitor {
+            tcx: self.tcx, is_quiet: true, is_public: true, old_error_set: self.old_error_set
+        };
+        check.visit_trait_ref(trait_ref);
+        check.is_public
+    }
+}
+
+impl<'a, 'tcx, 'v> Visitor<'v> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> {
+    fn visit_item(&mut self, item: &hir::Item) {
+        let mut check = SearchInterfaceForPrivateItemsVisitor {
+            tcx: self.tcx, is_quiet: false, is_public: true, old_error_set: self.old_error_set
+        };
+        match item.node {
+            // Crates are always public
+            hir::ItemExternCrate(..) => {}
+            // All nested items are checked by visit_item
+            hir::ItemMod(..) => {}
+            // Checked in resolve
+            hir::ItemUse(..) => {}
+            // Subitems of these items have inherited publicity
+            hir::ItemConst(..) | hir::ItemStatic(..) | hir::ItemFn(..) |
+            hir::ItemEnum(..) | hir::ItemTrait(..) | hir::ItemTy(..) => {
+                if item.vis == hir::Public {
+                    check.visit_item(item);
+                }
+            }
+            // Subitems of foreign modules have their own publicity
+            hir::ItemForeignMod(ref foreign_mod) => {
+                for foreign_item in &foreign_mod.items {
+                    if foreign_item.vis == hir::Public {
+                        check.visit_foreign_item(foreign_item);
+                    }
+                }
+            }
+            // Subitems of structs have their own publicity
+            hir::ItemStruct(ref struct_def, ref generics) => {
+                if item.vis == hir::Public {
+                    check.visit_generics(generics);
+                    for field in struct_def.fields() {
+                        if field.node.kind.visibility() == hir::Public {
+                            check.visit_struct_field(field);
+                        }
+                    }
+                }
+            }
+            // The interface is empty
+            hir::ItemDefaultImpl(..) => {}
+            // An inherent impl is public when its type is public
+            // Subitems of inherent impls have their own publicity
+            hir::ItemImpl(_, _, ref generics, None, ref ty, ref impl_items) => {
+                if self.is_public_ty(ty) {
+                    check.visit_generics(generics);
+                    for impl_item in impl_items {
+                        if impl_item.vis == hir::Public {
+                            check.visit_impl_item(impl_item);
+                        }
+                    }
+                }
+            }
+            // A trait impl is public when both its type and its trait are public
+            // Subitems of trait impls have inherited publicity
+            hir::ItemImpl(_, _, ref generics, Some(ref trait_ref), ref ty, ref impl_items) => {
+                if self.is_public_ty(ty) && self.is_public_trait_ref(trait_ref) {
+                    check.visit_generics(generics);
+                    for impl_item in impl_items {
+                        check.visit_impl_item(impl_item);
+                    }
+                }
+            }
+        }
+    }
+}
+
 pub fn check_crate(tcx: &ty::ctxt,
                    export_map: &def::ExportMap,
                    external_exports: ExternalExports)
                    -> AccessLevels {
+    let _task = tcx.dep_graph.in_task(DepNode::Privacy);
+
     let krate = tcx.map.krate();
 
     // Sanity check to make sure that all privacy usage and controls are
@@ -1492,18 +1728,24 @@ pub fn check_crate(tcx: &ty::ctxt,
     }
     visitor.update(ast::CRATE_NODE_ID, Some(AccessLevel::Public));
 
-    let EmbargoVisitor { access_levels, .. } = visitor;
-
     {
-        let mut visitor = VisiblePrivateTypesVisitor {
+        let mut visitor = ObsoleteVisiblePrivateTypesVisitor {
             tcx: tcx,
-            access_levels: &access_levels,
+            access_levels: &visitor.access_levels,
             in_variant: false,
+            old_error_set: NodeSet(),
         };
         intravisit::walk_crate(&mut visitor, krate);
+
+        // Check for private types and traits in public interfaces
+        let mut visitor = PrivateItemsInPublicInterfacesVisitor {
+            tcx: tcx,
+            old_error_set: &visitor.old_error_set,
+        };
+        krate.visit_all_items(&mut visitor);
     }
 
-    access_levels
+    visitor.access_levels
 }
 
 __build_diagnostic_array! { librustc_privacy, DIAGNOSTICS }
index 766a6d361ddf61bf2931b44a5fd1db3f899462d3..2e713a2f50e0fcdb0d5dfe08db3c54f2107f20ab 100644 (file)
 use DefModifiers;
 use resolve_imports::ImportDirective;
 use resolve_imports::ImportDirectiveSubclass::{self, SingleImport, GlobImport};
-use resolve_imports::ImportResolution;
+use resolve_imports::{ImportResolution, ImportResolutionPerNamespace};
 use Module;
 use Namespace::{TypeNS, ValueNS};
 use NameBindings;
 use {names_to_string, module_to_string};
-use ParentLink::{self, ModuleParentLink, BlockParentLink};
+use ParentLink::{ModuleParentLink, BlockParentLink};
 use Resolver;
 use resolve_imports::Shadowable;
-use {resolve_error, ResolutionError};
+use {resolve_error, resolve_struct_error, ResolutionError};
 
 use self::DuplicateCheckingMode::*;
 
@@ -38,12 +38,12 @@ use syntax::parse::token::special_idents;
 use syntax::codemap::{Span, DUMMY_SP};
 
 use rustc_front::hir;
-use rustc_front::hir::{Block, Crate, DeclItem};
+use rustc_front::hir::{Block, DeclItem};
 use rustc_front::hir::{ForeignItem, ForeignItemFn, ForeignItemStatic};
 use rustc_front::hir::{Item, ItemConst, ItemEnum, ItemExternCrate, ItemFn};
 use rustc_front::hir::{ItemForeignMod, ItemImpl, ItemMod, ItemStatic, ItemDefaultImpl};
 use rustc_front::hir::{ItemStruct, ItemTrait, ItemTy, ItemUse};
-use rustc_front::hir::{NamedField, PathListIdent, PathListMod, Public};
+use rustc_front::hir::{NamedField, PathListIdent, PathListMod};
 use rustc_front::hir::StmtDecl;
 use rustc_front::hir::UnnamedField;
 use rustc_front::hir::{Variant, ViewPathGlob, ViewPathList, ViewPathSimple};
@@ -52,7 +52,6 @@ use rustc_front::intravisit::{self, Visitor};
 
 use std::mem::replace;
 use std::ops::{Deref, DerefMut};
-use std::rc::Rc;
 
 // Specifies how duplicates should be handled when adding a child item if
 // another item exists with the same name in some namespace.
@@ -86,7 +85,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
     /// Constructs the reduced graph for the entire crate.
     fn build_reduced_graph(self, krate: &hir::Crate) {
         let mut visitor = BuildReducedGraphVisitor {
-            parent: self.graph_root.clone(),
+            parent: self.graph_root,
             builder: self,
         };
         intravisit::walk_crate(&mut visitor, krate);
@@ -97,12 +96,12 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
     /// Returns the child's corresponding name bindings.
     fn add_child(&self,
                  name: Name,
-                 parent: &Rc<Module>,
+                 parent: Module<'b>,
                  duplicate_checking_mode: DuplicateCheckingMode,
                  // For printing errors
                  sp: Span)
-                 -> NameBindings {
-        self.check_for_conflicts_between_external_crates_and_items(&**parent, name, sp);
+                 -> NameBindings<'b> {
+        self.check_for_conflicts_between_external_crates_and_items(parent, name, sp);
 
         // Add or reuse the child.
         let child = parent.children.borrow().get(&name).cloned();
@@ -137,12 +136,16 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
 
                 // Record an error here by looking up the namespace that had the duplicate
                 let ns_str = match ns { TypeNS => "type or module", ValueNS => "value" };
-                resolve_error(self, sp, ResolutionError::DuplicateDefinition(ns_str, name));
+                let mut err = resolve_struct_error(self,
+                                                   sp,
+                                                   ResolutionError::DuplicateDefinition(ns_str,
+                                                                                        name));
 
                 if let Some(sp) = child[ns].span() {
                     let note = format!("first definition of {} `{}` here", ns_str, name);
-                    self.session.span_note(sp, &note);
+                    err.span_note(sp, &note);
                 }
+                err.emit();
                 child
             }
         }
@@ -174,12 +177,8 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
         return false;
     }
 
-    fn get_parent_link(&mut self, parent: &Rc<Module>, name: Name) -> ParentLink {
-        ModuleParentLink(Rc::downgrade(parent), name)
-    }
-
     /// Constructs the reduced graph for one item.
-    fn build_reduced_graph_for_item(&mut self, item: &Item, parent: &Rc<Module>) -> Rc<Module> {
+    fn build_reduced_graph_for_item(&mut self, item: &Item, parent: Module<'b>) -> Module<'b> {
         let name = item.name;
         let sp = item.span;
         let is_public = item.vis == hir::Public;
@@ -234,7 +233,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                         }
 
                         let subclass = SingleImport(binding, source_name);
-                        self.build_import_directive(&**parent,
+                        self.build_import_directive(parent,
                                                     module_path,
                                                     subclass,
                                                     view_path.span,
@@ -253,13 +252,13 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                                                     })
                                                     .collect::<Vec<Span>>();
                         if mod_spans.len() > 1 {
-                            resolve_error(self,
+                            let mut e = resolve_struct_error(self,
                                           mod_spans[0],
                                           ResolutionError::SelfImportCanOnlyAppearOnceInTheList);
                             for other_span in mod_spans.iter().skip(1) {
-                                self.session
-                                    .span_note(*other_span, "another `self` import appears here");
+                                e.span_note(*other_span, "another `self` import appears here");
                             }
+                            e.emit();
                         }
 
                         for source_item in source_items {
@@ -284,7 +283,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                                     (module_path.to_vec(), name, rename)
                                 }
                             };
-                            self.build_import_directive(&**parent,
+                            self.build_import_directive(parent,
                                                         module_path,
                                                         SingleImport(rename, name),
                                                         source_item.span,
@@ -294,7 +293,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                         }
                     }
                     ViewPathGlob(_) => {
-                        self.build_import_directive(&**parent,
+                        self.build_import_directive(parent,
                                                     module_path,
                                                     GlobImport,
                                                     view_path.span,
@@ -303,7 +302,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                                                     shadowable);
                     }
                 }
-                parent.clone()
+                parent
             }
 
             ItemExternCrate(_) => {
@@ -315,32 +314,32 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                         index: CRATE_DEF_INDEX,
                     };
                     self.external_exports.insert(def_id);
-                    let parent_link = ModuleParentLink(Rc::downgrade(parent), name);
+                    let parent_link = ModuleParentLink(parent, name);
                     let def = DefMod(def_id);
-                    let external_module = Module::new(parent_link, Some(def), false, true);
+                    let external_module = self.new_module(parent_link, Some(def), false, true);
 
                     debug!("(build reduced graph for item) found extern `{}`",
                            module_to_string(&*external_module));
-                    self.check_for_conflicts_between_external_crates(&**parent, name, sp);
+                    self.check_for_conflicts_for_external_crate(parent, name, sp);
                     parent.external_module_children
                           .borrow_mut()
-                          .insert(name, external_module.clone());
+                          .insert(name, external_module);
                     self.build_reduced_graph_for_external_crate(&external_module);
                 }
-                parent.clone()
+                parent
             }
 
             ItemMod(..) => {
                 let name_bindings = self.add_child(name, parent, ForbidDuplicateTypes, sp);
 
-                let parent_link = self.get_parent_link(parent, name);
+                let parent_link = ModuleParentLink(parent, name);
                 let def = DefMod(self.ast_map.local_def_id(item.id));
-                let module = Module::new(parent_link, Some(def), false, is_public);
+                let module = self.new_module(parent_link, Some(def), false, is_public);
                 name_bindings.define_module(module.clone(), sp);
                 module
             }
 
-            ItemForeignMod(..) => parent.clone(),
+            ItemForeignMod(..) => parent,
 
             // These items live in the value namespace.
             ItemStatic(_, m, _) => {
@@ -350,19 +349,19 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                 name_bindings.define_value(DefStatic(self.ast_map.local_def_id(item.id), mutbl),
                                            sp,
                                            modifiers);
-                parent.clone()
+                parent
             }
             ItemConst(_, _) => {
                 self.add_child(name, parent, ForbidDuplicateValues, sp)
                     .define_value(DefConst(self.ast_map.local_def_id(item.id)), sp, modifiers);
-                parent.clone()
+                parent
             }
             ItemFn(_, _, _, _, _, _) => {
                 let name_bindings = self.add_child(name, parent, ForbidDuplicateValues, sp);
 
                 let def = DefFn(self.ast_map.local_def_id(item.id), false);
                 name_bindings.define_value(def, sp, modifiers);
-                parent.clone()
+                parent
             }
 
             // These items live in the type namespace.
@@ -372,11 +371,11 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                                                    ForbidDuplicateTypes,
                                                    sp);
 
-                let parent_link = self.get_parent_link(parent, name);
+                let parent_link = ModuleParentLink(parent, name);
                 let def = DefTy(self.ast_map.local_def_id(item.id), false);
-                let module = Module::new(parent_link, Some(def), false, is_public);
+                let module = self.new_module(parent_link, Some(def), false, is_public);
                 name_bindings.define_module(module, sp);
-                parent.clone()
+                parent
             }
 
             ItemEnum(ref enum_definition, _) => {
@@ -385,16 +384,22 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                                                    ForbidDuplicateTypes,
                                                    sp);
 
-                let parent_link = self.get_parent_link(parent, name);
+                let parent_link = ModuleParentLink(parent, name);
                 let def = DefTy(self.ast_map.local_def_id(item.id), true);
-                let module = Module::new(parent_link, Some(def), false, is_public);
+                let module = self.new_module(parent_link, Some(def), false, is_public);
                 name_bindings.define_module(module.clone(), sp);
 
+                let variant_modifiers = if is_public {
+                    DefModifiers::empty()
+                } else {
+                    DefModifiers::PRIVATE_VARIANT
+                };
                 for variant in &(*enum_definition).variants {
                     let item_def_id = self.ast_map.local_def_id(item.id);
-                    self.build_reduced_graph_for_variant(variant, item_def_id, &module);
+                    self.build_reduced_graph_for_variant(variant, item_def_id,
+                                                         &module, variant_modifiers);
                 }
-                parent.clone()
+                parent
             }
 
             // These items live in both the type and value namespaces.
@@ -434,11 +439,11 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                 let item_def_id = self.ast_map.local_def_id(item.id);
                 self.structs.insert(item_def_id, named_fields);
 
-                parent.clone()
+                parent
             }
 
             ItemDefaultImpl(_, _) |
-            ItemImpl(..) => parent.clone(),
+            ItemImpl(..) => parent,
 
             ItemTrait(_, _, _, ref items) => {
                 let name_bindings = self.add_child(name,
@@ -449,9 +454,9 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                 let def_id = self.ast_map.local_def_id(item.id);
 
                 // Add all the items within to a new module.
-                let parent_link = self.get_parent_link(parent, name);
+                let parent_link = ModuleParentLink(parent, name);
                 let def = DefTrait(def_id);
-                let module_parent = Module::new(parent_link, Some(def), false, is_public);
+                let module_parent = self.new_module(parent_link, Some(def), false, is_public);
                 name_bindings.define_module(module_parent.clone(), sp);
 
                 // Add the names of all the items to the trait info.
@@ -484,7 +489,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                     self.trait_item_map.insert((trait_item.name, def_id), trait_item_def_id);
                 }
 
-                parent.clone()
+                parent
             }
         }
     }
@@ -494,7 +499,8 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
     fn build_reduced_graph_for_variant(&mut self,
                                        variant: &Variant,
                                        item_id: DefId,
-                                       parent: &Rc<Module>) {
+                                       parent: Module<'b>,
+                                       variant_modifiers: DefModifiers) {
         let name = variant.node.name;
         let is_exported = if variant.node.data.is_struct() {
             // Not adding fields for variants as they are not accessed with a self receiver
@@ -512,18 +518,18 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                                       self.ast_map.local_def_id(variant.node.data.id()),
                                       is_exported),
                            variant.span,
-                           DefModifiers::PUBLIC | DefModifiers::IMPORTABLE);
+                           DefModifiers::PUBLIC | DefModifiers::IMPORTABLE | variant_modifiers);
         child.define_type(DefVariant(item_id,
                                      self.ast_map.local_def_id(variant.node.data.id()),
                                      is_exported),
                           variant.span,
-                          DefModifiers::PUBLIC | DefModifiers::IMPORTABLE);
+                          DefModifiers::PUBLIC | DefModifiers::IMPORTABLE | variant_modifiers);
     }
 
     /// Constructs the reduced graph for one foreign item.
     fn build_reduced_graph_for_foreign_item(&mut self,
                                             foreign_item: &ForeignItem,
-                                            parent: &Rc<Module>) {
+                                            parent: Module<'b>) {
         let name = foreign_item.name;
         let is_public = foreign_item.vis == hir::Public;
         let modifiers = if is_public {
@@ -544,7 +550,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
         name_bindings.define_value(def, foreign_item.span, modifiers);
     }
 
-    fn build_reduced_graph_for_block(&mut self, block: &Block, parent: &Rc<Module>) -> Rc<Module> {
+    fn build_reduced_graph_for_block(&mut self, block: &Block, parent: Module<'b>) -> Module<'b> {
         if self.block_needs_anonymous_module(block) {
             let block_id = block.id;
 
@@ -552,22 +558,22 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                     {}",
                    block_id);
 
-            let parent_link = BlockParentLink(Rc::downgrade(parent), block_id);
-            let new_module = Module::new(parent_link, None, false, false);
-            parent.anonymous_children.borrow_mut().insert(block_id, new_module.clone());
+            let parent_link = BlockParentLink(parent, block_id);
+            let new_module = self.new_module(parent_link, None, false, false);
+            parent.anonymous_children.borrow_mut().insert(block_id, new_module);
             new_module
         } else {
-            parent.clone()
+            parent
         }
     }
 
     fn handle_external_def(&mut self,
                            def: Def,
                            vis: Visibility,
-                           child_name_bindings: &NameBindings,
+                           child_name_bindings: &NameBindings<'b>,
                            final_ident: &str,
                            name: Name,
-                           new_parent: &Rc<Module>) {
+                           new_parent: Module<'b>) {
         debug!("(building reduced graph for external crate) building external def {}, priv {:?}",
                final_ident,
                vis);
@@ -598,8 +604,8 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                     debug!("(building reduced graph for external crate) building module {} {}",
                            final_ident,
                            is_public);
-                    let parent_link = self.get_parent_link(new_parent, name);
-                    let module = Module::new(parent_link, Some(def), true, is_public);
+                    let parent_link = ModuleParentLink(new_parent, name);
+                    let module = self.new_module(parent_link, Some(def), true, is_public);
                     child_name_bindings.define_module(module, DUMMY_SP);
                 }
             }
@@ -670,8 +676,8 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                 }
 
                 // Define a module if necessary.
-                let parent_link = self.get_parent_link(new_parent, name);
-                let module = Module::new(parent_link, Some(def), true, is_public);
+                let parent_link = ModuleParentLink(new_parent, name);
+                let module = self.new_module(parent_link, Some(def), true, is_public);
                 child_name_bindings.define_module(module, DUMMY_SP);
             }
             DefTy(..) | DefAssociatedTy(..) => {
@@ -706,10 +712,10 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
             DefLocal(..) |
             DefPrimTy(..) |
             DefTyParam(..) |
-            DefUse(..) |
             DefUpvar(..) |
             DefLabel(..) |
-            DefSelfTy(..) => {
+            DefSelfTy(..) |
+            DefErr => {
                 panic!("didn't expect `{:?}`", def);
             }
         }
@@ -717,7 +723,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
 
     /// Builds the reduced graph for a single item in an external crate.
     fn build_reduced_graph_for_external_crate_def(&mut self,
-                                                  root: &Rc<Module>,
+                                                  root: Module<'b>,
                                                   xcdef: ChildItem) {
         match xcdef.def {
             DlDef(def) => {
@@ -755,9 +761,9 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
     }
 
     /// Builds the reduced graph rooted at the given external module.
-    fn populate_external_module(&mut self, module: &Rc<Module>) {
+    fn populate_external_module(&mut self, module: Module<'b>) {
         debug!("(populating external module) attempting to populate {}",
-               module_to_string(&**module));
+               module_to_string(module));
 
         let def_id = match module.def_id() {
             None => {
@@ -777,7 +783,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
 
     /// Ensures that the reduced graph rooted at the given external module
     /// is built, building it if it is not.
-    fn populate_module_if_necessary(&mut self, module: &Rc<Module>) {
+    fn populate_module_if_necessary(&mut self, module: Module<'b>) {
         if !module.populated.get() {
             self.populate_external_module(module)
         }
@@ -786,7 +792,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
 
     /// Builds the reduced graph rooted at the 'use' directive for an external
     /// crate.
-    fn build_reduced_graph_for_external_crate(&mut self, root: &Rc<Module>) {
+    fn build_reduced_graph_for_external_crate(&mut self, root: Module<'b>) {
         let root_cnum = root.def_id().unwrap().krate;
         for child in self.session.cstore.crate_top_level_items(root_cnum) {
             self.build_reduced_graph_for_external_crate_def(root, child);
@@ -795,7 +801,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
 
     /// Creates and adds an import directive to the given module.
     fn build_import_directive(&mut self,
-                              module_: &Module,
+                              module_: Module<'b>,
                               module_path: Vec<Name>,
                               subclass: ImportDirectiveSubclass,
                               span: Span,
@@ -822,22 +828,23 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
 
                 let mut import_resolutions = module_.import_resolutions.borrow_mut();
                 match import_resolutions.get_mut(&target) {
-                    Some(resolution) => {
+                    Some(resolution_per_ns) => {
                         debug!("(building import directive) bumping reference");
-                        resolution.outstanding_references += 1;
+                        resolution_per_ns.outstanding_references += 1;
 
                         // the source of this name is different now
-                        resolution.type_id = id;
-                        resolution.value_id = id;
-                        resolution.is_public = is_public;
+                        let resolution =
+                            ImportResolution { id: id, is_public: is_public, target: None };
+                        resolution_per_ns[TypeNS] = resolution.clone();
+                        resolution_per_ns[ValueNS] = resolution;
                         return;
                     }
                     None => {}
                 }
                 debug!("(building import directive) creating new");
-                let mut resolution = ImportResolution::new(id, is_public);
-                resolution.outstanding_references = 1;
-                import_resolutions.insert(target, resolution);
+                let mut import_resolution_per_ns = ImportResolutionPerNamespace::new(id, is_public);
+                import_resolution_per_ns.outstanding_references = 1;
+                import_resolutions.insert(target, import_resolution_per_ns);
             }
             GlobImport => {
                 // Set the glob flag. This tells us that we don't know the
@@ -854,7 +861,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
 
 struct BuildReducedGraphVisitor<'a, 'b: 'a, 'tcx: 'b> {
     builder: GraphBuilder<'a, 'b, 'tcx>,
-    parent: Rc<Module>,
+    parent: Module<'b>,
 }
 
 impl<'a, 'b, 'v, 'tcx> Visitor<'v> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
@@ -885,6 +892,7 @@ pub fn build_reduced_graph(resolver: &mut Resolver, krate: &hir::Crate) {
     GraphBuilder { resolver: resolver }.build_reduced_graph(krate);
 }
 
-pub fn populate_module_if_necessary(resolver: &mut Resolver, module: &Rc<Module>) {
+pub fn populate_module_if_necessary<'a, 'tcx>(resolver: &mut Resolver<'a, 'tcx>,
+                                              module: Module<'a>) {
     GraphBuilder { resolver: resolver }.populate_module_if_necessary(module);
 }
index 85fb5d9ccf9e5c1f554c7597dc22569811867091..04ab3fe70e9fabdea01732ec67a15b205bf09c21 100644 (file)
@@ -274,7 +274,7 @@ https://doc.rust-lang.org/reference.html#use-declarations
 "##,
 
 E0401: r##"
-Inner functions do not inherit type parameters from the functions they are
+Inner items do not inherit type parameters from the functions they are
 embedded in. For example, this will not compile:
 
 ```
@@ -286,12 +286,32 @@ fn foo<T>(x: T) {
 }
 ```
 
-Functions inside functions are basically just like top-level functions, except
-that they can only be called from the function they are in.
+nor will this:
+
+```
+fn foo<T>(x: T) {
+    type MaybeT = Option<T>;
+    // ...
+}
+```
+
+or this:
+
+```
+fn foo<T>(x: T) {
+    struct Foo {
+        x: T,
+    }
+    // ...
+}
+```
+
+Items inside functions are basically just like top-level items, except
+that they can only be used from the function they are in.
 
 There are a couple of solutions for this.
 
-You can use a closure:
+If the item is a function, you may use a closure:
 
 ```
 fn foo<T>(x: T) {
@@ -302,7 +322,7 @@ fn foo<T>(x: T) {
 }
 ```
 
-or copy over the parameters:
+For a generic item, you can copy over the parameters:
 
 ```
 fn foo<T>(x: T) {
@@ -313,6 +333,12 @@ fn foo<T>(x: T) {
 }
 ```
 
+```
+fn foo<T>(x: T) {
+    type MaybeT<T> = Option<T>;
+}
+```
+
 Be sure to copy over any bounds as well:
 
 ```
@@ -324,10 +350,18 @@ fn foo<T: Copy>(x: T) {
 }
 ```
 
+```
+fn foo<T: Copy>(x: T) {
+    struct Foo<T: Copy> {
+        x: T,
+    }
+}
+```
+
 This may require additional type hints in the function body.
 
-In case the function is in an `impl`, defining a private helper function might
-be easier:
+In case the item is a function inside an `impl`, defining a private helper
+function might be easier:
 
 ```
 impl<T> Foo<T> {
index ddada1b513d745248f324f15a31d4bee5b94c5e7..8464d3ef298708187449cd32d5cd78b6ff86b477 100644 (file)
@@ -8,11 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_resolve"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
 extern crate log;
 #[macro_use]
 extern crate syntax;
+extern crate arena;
 #[macro_use]
 #[no_link]
 extern crate rustc_bitflags;
 extern crate rustc_front;
-
 extern crate rustc;
 
 use self::PatternBindingMode::*;
@@ -64,18 +61,19 @@ use rustc::middle::ty::{Freevar, FreevarMap, TraitMap, GlobMap};
 use rustc::util::nodemap::{NodeMap, DefIdSet, FnvHashMap};
 
 use syntax::ast;
-use syntax::ast::{CRATE_NODE_ID, Ident, Name, NodeId, CrateNum, TyIs, TyI8, TyI16, TyI32, TyI64};
+use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, TyIs, TyI8, TyI16, TyI32, TyI64};
 use syntax::ast::{TyUs, TyU8, TyU16, TyU32, TyU64, TyF64, TyF32};
 use syntax::attr::AttrMetaMethods;
-use syntax::parse::token::{self, special_names, special_idents};
 use syntax::codemap::{self, Span, Pos};
-use syntax::util::lev_distance::{lev_distance, max_suggestion_distance};
+use syntax::errors::DiagnosticBuilder;
+use syntax::parse::token::{self, special_names, special_idents};
+use syntax::util::lev_distance::find_best_match_for_name;
 
 use rustc_front::intravisit::{self, FnKind, Visitor};
 use rustc_front::hir;
 use rustc_front::hir::{Arm, BindByRef, BindByValue, BindingMode, Block};
 use rustc_front::hir::Crate;
-use rustc_front::hir::{Expr, ExprAgain, ExprBreak, ExprField};
+use rustc_front::hir::{Expr, ExprAgain, ExprBreak, ExprCall, ExprField};
 use rustc_front::hir::{ExprLoop, ExprWhile, ExprMethodCall};
 use rustc_front::hir::{ExprPath, ExprStruct, FnDecl};
 use rustc_front::hir::{ForeignItemFn, ForeignItemStatic, Generics};
@@ -93,10 +91,9 @@ use std::collections::{HashMap, HashSet};
 use std::cell::{Cell, RefCell};
 use std::fmt;
 use std::mem::replace;
-use std::rc::{Rc, Weak};
-use std::usize;
+use std::rc::Rc;
 
-use resolve_imports::{Target, ImportDirective, ImportResolution};
+use resolve_imports::{Target, ImportDirective, ImportResolutionPerNamespace};
 use resolve_imports::Shadowable;
 
 // NB: This module needs to be declared first so diagnostics are
@@ -104,7 +101,6 @@ use resolve_imports::Shadowable;
 pub mod diagnostics;
 
 mod check_unused;
-mod record_exports;
 mod build_reduced_graph;
 mod resolve_imports;
 
@@ -121,11 +117,13 @@ macro_rules! execute_callback {
 
 enum SuggestionType {
     Macro(String),
-    Function(String),
+    Function(token::InternedString),
     NotFound,
 }
 
 pub enum ResolutionError<'a> {
+    /// error E0260: name conflicts with an extern crate
+    NameConflictsWithExternCrate(Name),
     /// error E0401: can't use type parameters from outer function
     TypeParametersFromOuterFunction,
     /// error E0402: cannot use an outer type parameter in this context
@@ -179,7 +177,7 @@ pub enum ResolutionError<'a> {
     /// error E0424: `self` is not available in a static method
     SelfNotAvailableInStaticMethod,
     /// error E0425: unresolved name
-    UnresolvedName(&'a str, &'a str),
+    UnresolvedName(&'a str, &'a str, UnresolvedNameContext),
     /// error E0426: use of undeclared label
     UndeclaredLabel(&'a str),
     /// error E0427: cannot use `ref` binding mode with ...
@@ -202,278 +200,352 @@ pub enum ResolutionError<'a> {
     AttemptToUseNonConstantValueInConstant,
 }
 
+/// Context of where `ResolutionError::UnresolvedName` arose.
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum UnresolvedNameContext {
+    /// `PathIsMod(id)` indicates that a given path, used in
+    /// expression context, actually resolved to a module rather than
+    /// a value. The `id` attached to the variant is the node id of
+    /// the erroneous path expression.
+    PathIsMod(ast::NodeId),
+
+    /// `Other` means we have no extra information about the context
+    /// of the unresolved name error. (Maybe we could eliminate all
+    /// such cases; but for now, this is an information-free default.)
+    Other,
+}
+
 fn resolve_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
                                        span: syntax::codemap::Span,
                                        resolution_error: ResolutionError<'b>) {
+    resolve_struct_error(resolver, span, resolution_error).emit();
+}
+
+fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
+                                              span: syntax::codemap::Span,
+                                              resolution_error: ResolutionError<'b>)
+                                              -> DiagnosticBuilder<'a> {
     if !resolver.emit_errors {
-        return;
+        return resolver.session.diagnostic().struct_dummy();
     }
+
     match resolution_error {
+        ResolutionError::NameConflictsWithExternCrate(name) => {
+            struct_span_err!(resolver.session,
+                             span,
+                             E0260,
+                             "the name `{}` conflicts with an external crate \
+                             that has been imported into this module",
+                             name)
+        }
         ResolutionError::TypeParametersFromOuterFunction => {
-            span_err!(resolver.session,
-                      span,
-                      E0401,
-                      "can't use type parameters from outer function; try using a local type \
-                       parameter instead");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0401,
+                             "can't use type parameters from outer function; try using a local \
+                              type parameter instead")
         }
         ResolutionError::OuterTypeParameterContext => {
-            span_err!(resolver.session,
-                      span,
-                      E0402,
-                      "cannot use an outer type parameter in this context");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0402,
+                             "cannot use an outer type parameter in this context")
         }
         ResolutionError::NameAlreadyUsedInTypeParameterList(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0403,
-                      "the name `{}` is already used for a type parameter in this type parameter \
-                       list",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0403,
+                             "the name `{}` is already used for a type parameter in this type \
+                              parameter list",
+                             name)
         }
         ResolutionError::IsNotATrait(name) => {
-            span_err!(resolver.session, span, E0404, "`{}` is not a trait", name);
+            struct_span_err!(resolver.session, span, E0404, "`{}` is not a trait", name)
         }
         ResolutionError::UndeclaredTraitName(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0405,
-                      "use of undeclared trait name `{}`",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0405,
+                             "use of undeclared trait name `{}`",
+                             name)
         }
         ResolutionError::UndeclaredAssociatedType => {
-            span_err!(resolver.session, span, E0406, "undeclared associated type");
+            struct_span_err!(resolver.session, span, E0406, "undeclared associated type")
         }
         ResolutionError::MethodNotMemberOfTrait(method, trait_) => {
-            span_err!(resolver.session,
-                      span,
-                      E0407,
-                      "method `{}` is not a member of trait `{}`",
-                      method,
-                      trait_);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0407,
+                             "method `{}` is not a member of trait `{}`",
+                             method,
+                             trait_)
         }
         ResolutionError::TypeNotMemberOfTrait(type_, trait_) => {
-            span_err!(resolver.session,
-                      span,
-                      E0437,
-                      "type `{}` is not a member of trait `{}`",
-                      type_,
-                      trait_);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0437,
+                             "type `{}` is not a member of trait `{}`",
+                             type_,
+                             trait_)
         }
         ResolutionError::ConstNotMemberOfTrait(const_, trait_) => {
-            span_err!(resolver.session,
-                      span,
-                      E0438,
-                      "const `{}` is not a member of trait `{}`",
-                      const_,
-                      trait_);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0438,
+                             "const `{}` is not a member of trait `{}`",
+                             const_,
+                             trait_)
         }
         ResolutionError::VariableNotBoundInPattern(variable_name, pattern_number) => {
-            span_err!(resolver.session,
-                      span,
-                      E0408,
-                      "variable `{}` from pattern #1 is not bound in pattern #{}",
-                      variable_name,
-                      pattern_number);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0408,
+                             "variable `{}` from pattern #1 is not bound in pattern #{}",
+                             variable_name,
+                             pattern_number)
         }
         ResolutionError::VariableBoundWithDifferentMode(variable_name, pattern_number) => {
-            span_err!(resolver.session,
-                      span,
-                      E0409,
-                      "variable `{}` is bound with different mode in pattern #{} than in pattern \
-                       #1",
-                      variable_name,
-                      pattern_number);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0409,
+                             "variable `{}` is bound with different mode in pattern #{} than in \
+                              pattern #1",
+                             variable_name,
+                             pattern_number)
         }
         ResolutionError::VariableNotBoundInParentPattern(variable_name, pattern_number) => {
-            span_err!(resolver.session,
-                      span,
-                      E0410,
-                      "variable `{}` from pattern #{} is not bound in pattern #1",
-                      variable_name,
-                      pattern_number);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0410,
+                             "variable `{}` from pattern #{} is not bound in pattern #1",
+                             variable_name,
+                             pattern_number)
         }
         ResolutionError::SelfUsedOutsideImplOrTrait => {
-            span_err!(resolver.session,
-                      span,
-                      E0411,
-                      "use of `Self` outside of an impl or trait");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0411,
+                             "use of `Self` outside of an impl or trait")
         }
         ResolutionError::UseOfUndeclared(kind, name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0412,
-                      "use of undeclared {} `{}`",
-                      kind,
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0412,
+                             "use of undeclared {} `{}`",
+                             kind,
+                             name)
         }
         ResolutionError::DeclarationShadowsEnumVariantOrUnitLikeStruct(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0413,
-                      "declaration of `{}` shadows an enum variant or unit-like struct in scope",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0413,
+                             "declaration of `{}` shadows an enum variant \
+                              or unit-like struct in scope",
+                             name)
         }
         ResolutionError::OnlyIrrefutablePatternsAllowedHere(did, name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0414,
-                      "only irrefutable patterns allowed here");
-            resolver.session.span_note(span,
-                                       "there already is a constant in scope sharing the same \
-                                        name as this pattern");
+            let mut err = struct_span_err!(resolver.session,
+                                           span,
+                                           E0414,
+                                           "only irrefutable patterns allowed here");
+            err.span_note(span,
+                          "there already is a constant in scope sharing the same \
+                           name as this pattern");
             if let Some(sp) = resolver.ast_map.span_if_local(did) {
-                resolver.session.span_note(sp, "constant defined here");
+                err.span_note(sp, "constant defined here");
             }
             if let Some(directive) = resolver.current_module
                                              .import_resolutions
                                              .borrow()
                                              .get(&name) {
-                let item = resolver.ast_map.expect_item(directive.value_id);
-                resolver.session.span_note(item.span, "constant imported here");
+                let item = resolver.ast_map.expect_item(directive.value_ns.id);
+                err.span_note(item.span, "constant imported here");
             }
+            err
         }
         ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => {
-            span_err!(resolver.session,
-                      span,
-                      E0415,
-                      "identifier `{}` is bound more than once in this parameter list",
-                      identifier);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0415,
+                             "identifier `{}` is bound more than once in this parameter list",
+                             identifier)
         }
         ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => {
-            span_err!(resolver.session,
-                      span,
-                      E0416,
-                      "identifier `{}` is bound more than once in the same pattern",
-                      identifier);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0416,
+                             "identifier `{}` is bound more than once in the same pattern",
+                             identifier)
         }
         ResolutionError::StaticVariableReference => {
-            span_err!(resolver.session,
-                      span,
-                      E0417,
-                      "static variables cannot be referenced in a pattern, use a `const` instead");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0417,
+                             "static variables cannot be referenced in a pattern, use a \
+                              `const` instead")
         }
         ResolutionError::NotAnEnumVariantStructOrConst(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0418,
-                      "`{}` is not an enum variant, struct or const",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0418,
+                             "`{}` is not an enum variant, struct or const",
+                             name)
         }
         ResolutionError::UnresolvedEnumVariantStructOrConst(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0419,
-                      "unresolved enum variant, struct or const `{}`",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0419,
+                             "unresolved enum variant, struct or const `{}`",
+                             name)
         }
         ResolutionError::NotAnAssociatedConst(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0420,
-                      "`{}` is not an associated const",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0420,
+                             "`{}` is not an associated const",
+                             name)
         }
         ResolutionError::UnresolvedAssociatedConst(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0421,
-                      "unresolved associated const `{}`",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0421,
+                             "unresolved associated const `{}`",
+                             name)
         }
         ResolutionError::DoesNotNameAStruct(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0422,
-                      "`{}` does not name a structure",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0422,
+                             "`{}` does not name a structure",
+                             name)
         }
         ResolutionError::StructVariantUsedAsFunction(path_name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0423,
-                      "`{}` is the name of a struct or struct variant, but this expression uses \
-                       it like a function name",
-                      path_name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0423,
+                             "`{}` is the name of a struct or struct variant, but this expression \
+                             uses it like a function name",
+                             path_name)
         }
         ResolutionError::SelfNotAvailableInStaticMethod => {
-            span_err!(resolver.session,
-                      span,
-                      E0424,
-                      "`self` is not available in a static method. Maybe a `self` argument is \
-                       missing?");
-        }
-        ResolutionError::UnresolvedName(path, name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0425,
-                      "unresolved name `{}`{}",
-                      path,
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0424,
+                             "`self` is not available in a static method. Maybe a `self` \
+                             argument is missing?")
+        }
+        ResolutionError::UnresolvedName(path, msg, context) => {
+            let mut err = struct_span_err!(resolver.session,
+                                           span,
+                                           E0425,
+                                           "unresolved name `{}`{}",
+                                           path,
+                                           msg);
+
+            match context {
+                UnresolvedNameContext::Other => { } // no help available
+                UnresolvedNameContext::PathIsMod(id) => {
+                    let mut help_msg = String::new();
+                    let parent_id = resolver.ast_map.get_parent_node(id);
+                    if let Some(hir_map::Node::NodeExpr(e)) = resolver.ast_map.find(parent_id) {
+                        match e.node {
+                            ExprField(_, ident) => {
+                                help_msg = format!("To reference an item from the \
+                                                    `{module}` module, use \
+                                                    `{module}::{ident}`",
+                                                   module = &*path,
+                                                   ident = ident.node);
+                            }
+                            ExprMethodCall(ident, _, _) => {
+                                help_msg = format!("To call a function from the \
+                                                    `{module}` module, use \
+                                                    `{module}::{ident}(..)`",
+                                                   module = &*path,
+                                                   ident = ident.node);
+                            }
+                            ExprCall(_, _) => {
+                                help_msg = format!("No function corresponds to `{module}(..)`",
+                                                   module = &*path);
+                            }
+                            _ => { } // no help available
+                        }
+                    } else {
+                        help_msg = format!("Module `{module}` cannot be the value of an expression",
+                                           module = &*path);
+                    }
+
+                    if !help_msg.is_empty() {
+                        err.fileline_help(span, &help_msg);
+                    }
+                }
+            }
+            err
         }
         ResolutionError::UndeclaredLabel(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0426,
-                      "use of undeclared label `{}`",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0426,
+                             "use of undeclared label `{}`",
+                             name)
         }
         ResolutionError::CannotUseRefBindingModeWith(descr) => {
-            span_err!(resolver.session,
-                      span,
-                      E0427,
-                      "cannot use `ref` binding mode with {}",
-                      descr);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0427,
+                             "cannot use `ref` binding mode with {}",
+                             descr)
         }
         ResolutionError::DuplicateDefinition(namespace, name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0428,
-                      "duplicate definition of {} `{}`",
-                      namespace,
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0428,
+                             "duplicate definition of {} `{}`",
+                             namespace,
+                             name)
         }
         ResolutionError::SelfImportsOnlyAllowedWithin => {
-            span_err!(resolver.session,
-                      span,
-                      E0429,
-                      "{}",
-                      "`self` imports are only allowed within a { } list");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0429,
+                             "{}",
+                             "`self` imports are only allowed within a { } list")
         }
         ResolutionError::SelfImportCanOnlyAppearOnceInTheList => {
-            span_err!(resolver.session,
-                      span,
-                      E0430,
-                      "`self` import can only appear once in the list");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0430,
+                             "`self` import can only appear once in the list")
         }
         ResolutionError::SelfImportOnlyInImportListWithNonEmptyPrefix => {
-            span_err!(resolver.session,
-                      span,
-                      E0431,
-                      "`self` import can only appear in an import list with a non-empty prefix");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0431,
+                             "`self` import can only appear in an import list with a \
+                              non-empty prefix")
         }
         ResolutionError::UnresolvedImport(name) => {
             let msg = match name {
                 Some((n, p)) => format!("unresolved import `{}`{}", n, p),
                 None => "unresolved import".to_owned(),
             };
-            span_err!(resolver.session, span, E0432, "{}", msg);
+            struct_span_err!(resolver.session, span, E0432, "{}", msg)
         }
         ResolutionError::FailedToResolve(msg) => {
-            span_err!(resolver.session, span, E0433, "failed to resolve. {}", msg);
+            struct_span_err!(resolver.session, span, E0433, "failed to resolve. {}", msg)
         }
         ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => {
-            span_err!(resolver.session,
-                      span,
-                      E0434,
-                      "{}",
-                      "can't capture dynamic environment in a fn item; use the || { ... } \
-                       closure form instead");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0434,
+                             "{}",
+                             "can't capture dynamic environment in a fn item; use the || { ... } \
+                              closure form instead")
         }
         ResolutionError::AttemptToUseNonConstantValueInConstant => {
-            span_err!(resolver.session,
-                      span,
-                      E0435,
-                      "attempt to use a non-constant value in a constant");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0435,
+                             "attempt to use a non-constant value in a constant")
         }
     }
 }
@@ -504,7 +576,7 @@ pub enum Namespace {
 /// a particular namespace. The result is either definitely-resolved,
 /// definitely- unresolved, or unknown.
 #[derive(Clone)]
-enum NamespaceResult {
+enum NamespaceResult<'a> {
     /// Means that resolve hasn't gathered enough information yet to determine
     /// whether the name is bound in this namespace. (That is, it hasn't
     /// resolved all `use` directives yet.)
@@ -514,10 +586,10 @@ enum NamespaceResult {
     UnboundResult,
     /// Means that resolve has determined that the name is bound in the Module
     /// argument, and specified by the NameBinding argument.
-    BoundResult(Rc<Module>, NameBinding),
+    BoundResult(Module<'a>, NameBinding<'a>),
 }
 
-impl NamespaceResult {
+impl<'a> NamespaceResult<'a> {
     fn is_unknown(&self) -> bool {
         match *self {
             UnknownResult => true,
@@ -566,6 +638,7 @@ impl<'a, 'v, 'tcx> Visitor<'v> for Resolver<'a, 'tcx> {
             Ok(def) => self.record_def(tref.trait_ref.ref_id, def),
             Err(_) => {
                 // error already reported
+                self.record_def(tref.trait_ref.ref_id, err_path_resolution())
             }
         }
         intravisit::walk_poly_trait_ref(self, tref, m);
@@ -693,9 +766,9 @@ enum UseLexicalScopeFlag {
     UseLexicalScope,
 }
 
-enum ModulePrefixResult {
+enum ModulePrefixResult<'a> {
     NoPrefixFound,
-    PrefixFound(Rc<Module>, usize),
+    PrefixFound(Module<'a>, usize),
 }
 
 #[derive(Copy, Clone)]
@@ -757,24 +830,24 @@ impl LocalDef {
 
 /// The link from a module up to its nearest parent node.
 #[derive(Clone,Debug)]
-enum ParentLink {
+enum ParentLink<'a> {
     NoParentLink,
-    ModuleParentLink(Weak<Module>, Name),
-    BlockParentLink(Weak<Module>, NodeId),
+    ModuleParentLink(Module<'a>, Name),
+    BlockParentLink(Module<'a>, NodeId),
 }
 
 /// One node in the tree of modules.
-pub struct Module {
-    parent_link: ParentLink,
+pub struct ModuleS<'a> {
+    parent_link: ParentLink<'a>,
     def: Cell<Option<Def>>,
     is_public: bool,
 
-    children: RefCell<HashMap<Name, NameBindings>>,
+    children: RefCell<HashMap<Name, NameBindings<'a>>>,
     imports: RefCell<Vec<ImportDirective>>,
 
     // The external module children of this node that were declared with
     // `extern crate`.
-    external_module_children: RefCell<HashMap<Name, Rc<Module>>>,
+    external_module_children: RefCell<HashMap<Name, Module<'a>>>,
 
     // The anonymous children of this node. Anonymous children are pseudo-
     // modules that are implicitly created around items contained within
@@ -790,10 +863,10 @@ pub struct Module {
     //
     // There will be an anonymous module created around `g` with the ID of the
     // entry block for `f`.
-    anonymous_children: RefCell<NodeMap<Rc<Module>>>,
+    anonymous_children: RefCell<NodeMap<Module<'a>>>,
 
     // The status of resolving each import in this module.
-    import_resolutions: RefCell<HashMap<Name, ImportResolution>>,
+    import_resolutions: RefCell<HashMap<Name, ImportResolutionPerNamespace<'a>>>,
 
     // The number of unresolved globs that this module exports.
     glob_count: Cell<usize>,
@@ -813,13 +886,11 @@ pub struct Module {
     populated: Cell<bool>,
 }
 
-impl Module {
-    fn new(parent_link: ParentLink,
-           def: Option<Def>,
-           external: bool,
-           is_public: bool)
-           -> Rc<Module> {
-        Rc::new(Module {
+pub type Module<'a> = &'a ModuleS<'a>;
+
+impl<'a> ModuleS<'a> {
+    fn new(parent_link: ParentLink<'a>, def: Option<Def>, external: bool, is_public: bool) -> Self {
+        ModuleS {
             parent_link: parent_link,
             def: Cell::new(def),
             is_public: is_public,
@@ -833,7 +904,7 @@ impl Module {
             pub_glob_count: Cell::new(0),
             resolved_import_count: Cell::new(0),
             populated: Cell::new(!external),
-        })
+        }
     }
 
     fn def_id(&self) -> Option<DefId> {
@@ -862,9 +933,7 @@ impl Module {
             self.imports.borrow().len() == self.resolved_import_count.get()
         }
     }
-}
 
-impl Module {
     pub fn inc_glob_count(&self) {
         self.glob_count.set(self.glob_count.get() + 1);
     }
@@ -888,7 +957,7 @@ impl Module {
     }
 }
 
-impl fmt::Debug for Module {
+impl<'a> fmt::Debug for ModuleS<'a> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f,
                "{:?}, {}",
@@ -904,27 +973,32 @@ impl fmt::Debug for Module {
 bitflags! {
     #[derive(Debug)]
     flags DefModifiers: u8 {
+        // Enum variants are always considered `PUBLIC`, this is needed for `use Enum::Variant`
+        // or `use Enum::*` to work on private enums.
         const PUBLIC     = 1 << 0,
         const IMPORTABLE = 1 << 1,
+        // Variants are considered `PUBLIC`, but some of them live in private enums.
+        // We need to track them to prohibit reexports like `pub use PrivEnum::Variant`.
+        const PRIVATE_VARIANT = 1 << 2,
     }
 }
 
 // Records a possibly-private value, type, or module definition.
 #[derive(Debug)]
-struct NsDef {
-    modifiers: DefModifiers, // see note in ImportResolution about how to use this
-    def_or_module: DefOrModule,
+struct NsDef<'a> {
+    modifiers: DefModifiers, // see note in ImportResolutionPerNamespace about how to use this
+    def_or_module: DefOrModule<'a>,
     span: Option<Span>,
 }
 
 #[derive(Debug)]
-enum DefOrModule {
+enum DefOrModule<'a> {
     Def(Def),
-    Module(Rc<Module>),
+    Module(Module<'a>),
 }
 
-impl NsDef {
-    fn create_from_module(module: Rc<Module>, span: Option<Span>) -> Self {
+impl<'a> NsDef<'a> {
+    fn create_from_module(module: Module<'a>, span: Option<Span>) -> Self {
         let modifiers = if module.is_public {
             DefModifiers::PUBLIC
         } else {
@@ -938,9 +1012,9 @@ impl NsDef {
         NsDef { modifiers: modifiers, def_or_module: DefOrModule::Def(def), span: span }
     }
 
-    fn module(&self) -> Option<Rc<Module>> {
+    fn module(&self) -> Option<Module<'a>> {
         match self.def_or_module {
-            DefOrModule::Module(ref module) => Some(module.clone()),
+            DefOrModule::Module(ref module) => Some(module),
             DefOrModule::Def(_) => None,
         }
     }
@@ -955,18 +1029,18 @@ impl NsDef {
 
 // Records at most one definition that a name in a namespace is bound to
 #[derive(Clone,Debug)]
-pub struct NameBinding(Rc<RefCell<Option<NsDef>>>);
+pub struct NameBinding<'a>(Rc<RefCell<Option<NsDef<'a>>>>);
 
-impl NameBinding {
+impl<'a> NameBinding<'a> {
     fn new() -> Self {
         NameBinding(Rc::new(RefCell::new(None)))
     }
 
-    fn create_from_module(module: Rc<Module>) -> Self {
+    fn create_from_module(module: Module<'a>) -> Self {
         NameBinding(Rc::new(RefCell::new(Some(NsDef::create_from_module(module, None)))))
     }
 
-    fn set(&self, ns_def: NsDef) {
+    fn set(&self, ns_def: NsDef<'a>) {
         *self.0.borrow_mut() = Some(ns_def);
     }
 
@@ -976,7 +1050,7 @@ impl NameBinding {
         }
     }
 
-    fn borrow(&self) -> ::std::cell::Ref<Option<NsDef>> {
+    fn borrow(&self) -> ::std::cell::Ref<Option<NsDef<'a>>> {
         self.0.borrow()
     }
 
@@ -984,7 +1058,7 @@ impl NameBinding {
     fn def(&self) -> Option<Def> {
         self.borrow().as_ref().and_then(NsDef::def)
     }
-    fn module(&self) -> Option<Rc<Module>> {
+    fn module(&self) -> Option<Module<'a>> {
         self.borrow().as_ref().and_then(NsDef::module)
     }
     fn span(&self) -> Option<Span> {
@@ -1015,20 +1089,20 @@ impl NameBinding {
 // Records the definitions (at most one for each namespace) that a name is
 // bound to.
 #[derive(Clone,Debug)]
-pub struct NameBindings {
-    type_ns: NameBinding, // < Meaning in type namespace.
-    value_ns: NameBinding, // < Meaning in value namespace.
+pub struct NameBindings<'a> {
+    type_ns: NameBinding<'a>, // < Meaning in type namespace.
+    value_ns: NameBinding<'a>, // < Meaning in value namespace.
 }
 
-impl ::std::ops::Index<Namespace> for NameBindings {
-    type Output = NameBinding;
-    fn index(&self, namespace: Namespace) -> &NameBinding {
+impl<'a> ::std::ops::Index<Namespace> for NameBindings<'a> {
+    type Output = NameBinding<'a>;
+    fn index(&self, namespace: Namespace) -> &NameBinding<'a> {
         match namespace { TypeNS => &self.type_ns, ValueNS => &self.value_ns }
     }
 }
 
-impl NameBindings {
-    fn new() -> NameBindings {
+impl<'a> NameBindings<'a> {
+    fn new() -> Self {
         NameBindings {
             type_ns: NameBinding::new(),
             value_ns: NameBinding::new(),
@@ -1036,7 +1110,7 @@ impl NameBindings {
     }
 
     /// Creates a new module in this set of name bindings.
-    fn define_module(&self, module: Rc<Module>, sp: Span) {
+    fn define_module(&self, module: Module<'a>, sp: Span) {
         self.type_ns.set(NsDef::create_from_module(module, Some(sp)));
     }
 
@@ -1092,7 +1166,7 @@ pub struct Resolver<'a, 'tcx: 'a> {
 
     ast_map: &'a hir_map::Map<'tcx>,
 
-    graph_root: Rc<Module>,
+    graph_root: Module<'a>,
 
     trait_item_map: FnvHashMap<(Name, DefId), DefId>,
 
@@ -1102,7 +1176,7 @@ pub struct Resolver<'a, 'tcx: 'a> {
     unresolved_imports: usize,
 
     // The module that represents the current item scope.
-    current_module: Rc<Module>,
+    current_module: Module<'a>,
 
     // The current set of local scopes, for values.
     // FIXME #4948: Reuse ribs to avoid allocation.
@@ -1148,6 +1222,12 @@ pub struct Resolver<'a, 'tcx: 'a> {
     // The intention is that the callback modifies this flag.
     // Once set, the resolver falls out of the walk, preserving the ribs.
     resolved: bool,
+
+    arenas: &'a ResolverArenas<'a>,
+}
+
+pub struct ResolverArenas<'a> {
+    modules: arena::TypedArena<ModuleS<'a>>,
 }
 
 #[derive(PartialEq)]
@@ -1159,10 +1239,12 @@ enum FallbackChecks {
 impl<'a, 'tcx> Resolver<'a, 'tcx> {
     fn new(session: &'a Session,
            ast_map: &'a hir_map::Map<'tcx>,
-           make_glob_map: MakeGlobMap)
+           make_glob_map: MakeGlobMap,
+           arenas: &'a ResolverArenas<'a>)
            -> Resolver<'a, 'tcx> {
         let root_def_id = ast_map.local_def_id(CRATE_NODE_ID);
-        let graph_root = Module::new(NoParentLink, Some(DefMod(root_def_id)), false, true);
+        let graph_root = ModuleS::new(NoParentLink, Some(DefMod(root_def_id)), false, true);
+        let graph_root = arenas.modules.alloc(graph_root);
 
         Resolver {
             session: session,
@@ -1171,7 +1253,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
             // The outermost module has def ID 0; this is not reflected in the
             // AST.
-            graph_root: graph_root.clone(),
+            graph_root: graph_root,
 
             trait_item_map: FnvHashMap(),
             structs: FnvHashMap(),
@@ -1203,9 +1285,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
             callback: None,
             resolved: false,
+
+            arenas: arenas,
+        }
+    }
+
+    fn arenas() -> ResolverArenas<'a> {
+        ResolverArenas {
+            modules: arena::TypedArena::new(),
         }
     }
 
+    fn new_module(&self,
+                  parent_link: ParentLink<'a>,
+                  def: Option<Def>,
+                  external: bool,
+                  is_public: bool) -> Module<'a> {
+        self.arenas.modules.alloc(ModuleS::new(parent_link, def, external, is_public))
+    }
+
     #[inline]
     fn record_import_use(&mut self, import_id: NodeId, name: Name) {
         if !self.make_glob_map {
@@ -1229,12 +1327,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
     }
 
-    /// Checks that the names of external crates don't collide with other
-    /// external crates.
-    fn check_for_conflicts_between_external_crates(&self,
-                                                   module: &Module,
-                                                   name: Name,
-                                                   span: Span) {
+    /// Check that an external crate doesn't collide with items or other external crates.
+    fn check_for_conflicts_for_external_crate(&self, module: Module<'a>, name: Name, span: Span) {
         if module.external_module_children.borrow().contains_key(&name) {
             span_err!(self.session,
                       span,
@@ -1242,38 +1336,42 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                       "an external crate named `{}` has already been imported into this module",
                       name);
         }
+        match module.children.borrow().get(&name) {
+            Some(name_bindings) if name_bindings.type_ns.defined() => {
+                resolve_error(self,
+                              name_bindings.type_ns.span().unwrap_or(codemap::DUMMY_SP),
+                              ResolutionError::NameConflictsWithExternCrate(name));
+            }
+            _ => {},
+        }
     }
 
     /// Checks that the names of items don't collide with external crates.
     fn check_for_conflicts_between_external_crates_and_items(&self,
-                                                             module: &Module,
+                                                             module: Module<'a>,
                                                              name: Name,
                                                              span: Span) {
         if module.external_module_children.borrow().contains_key(&name) {
-            span_err!(self.session,
-                      span,
-                      E0260,
-                      "the name `{}` conflicts with an external crate that has been imported \
-                       into this module",
-                      name);
+            resolve_error(self, span, ResolutionError::NameConflictsWithExternCrate(name));
         }
     }
 
     /// Resolves the given module path from the given root `module_`.
     fn resolve_module_path_from_root(&mut self,
-                                     module_: Rc<Module>,
+                                     module_: Module<'a>,
                                      module_path: &[Name],
                                      index: usize,
                                      span: Span,
                                      name_search_type: NameSearchType,
                                      lp: LastPrivate)
-                                     -> ResolveResult<(Rc<Module>, LastPrivate)> {
-        fn search_parent_externals(needle: Name, module: &Rc<Module>) -> Option<Rc<Module>> {
+                                     -> ResolveResult<(Module<'a>, LastPrivate)> {
+        fn search_parent_externals<'a>(needle: Name, module: Module<'a>)
+                                       -> Option<Module<'a>> {
             match module.external_module_children.borrow().get(&needle) {
-                Some(_) => Some(module.clone()),
+                Some(_) => Some(module),
                 None => match module.parent_link {
                     ModuleParentLink(ref parent, _) => {
-                        search_parent_externals(needle, &parent.upgrade().unwrap())
+                        search_parent_externals(needle, parent)
                     }
                     _ => None,
                 },
@@ -1290,14 +1388,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         // modules as we go.
         while index < module_path_len {
             let name = module_path[index];
-            match self.resolve_name_in_module(search_module.clone(),
+            match self.resolve_name_in_module(search_module,
                                               name,
                                               TypeNS,
                                               name_search_type,
                                               false) {
                 Failed(None) => {
                     let segment_name = name.as_str();
-                    let module_name = module_to_string(&*search_module);
+                    let module_name = module_to_string(search_module);
                     let mut span = span;
                     let msg = if "???" == &module_name[..] {
                         span.hi = span.lo + Pos::from_usize(segment_name.len());
@@ -1368,12 +1466,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     /// On success, returns the resolved module, and the closest *private*
     /// module found to the destination when resolving this path.
     fn resolve_module_path(&mut self,
-                           module_: Rc<Module>,
+                           module_: Module<'a>,
                            module_path: &[Name],
                            use_lexical_scope: UseLexicalScopeFlag,
                            span: Span,
                            name_search_type: NameSearchType)
-                           -> ResolveResult<(Rc<Module>, LastPrivate)> {
+                           -> ResolveResult<(Module<'a>, LastPrivate)> {
         let module_path_len = module_path.len();
         assert!(module_path_len > 0);
 
@@ -1382,7 +1480,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                module_to_string(&*module_));
 
         // Resolve the module prefix, if any.
-        let module_prefix_result = self.resolve_module_prefix(module_.clone(), module_path);
+        let module_prefix_result = self.resolve_module_prefix(module_, module_path);
 
         let search_module;
         let start_index;
@@ -1418,7 +1516,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     DontUseLexicalScope => {
                         // This is a crate-relative path. We will start the
                         // resolution process at index zero.
-                        search_module = self.graph_root.clone();
+                        search_module = self.graph_root;
                         start_index = 0;
                         last_private = LastMod(AllPublic);
                     }
@@ -1442,7 +1540,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 }
             }
             Success(PrefixFound(ref containing_module, index)) => {
-                search_module = containing_module.clone();
+                search_module = containing_module;
                 start_index = index;
                 last_private = LastMod(DependsOn(containing_module.def_id()
                                                                   .unwrap()));
@@ -1460,10 +1558,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     /// Invariant: This must only be called during main resolution, not during
     /// import resolution.
     fn resolve_item_in_lexical_scope(&mut self,
-                                     module_: Rc<Module>,
+                                     module_: Module<'a>,
                                      name: Name,
-                                     namespace: Namespace)
-                                     -> ResolveResult<(Target, bool)> {
+                                     namespace: Namespace,
+                                     record_used: bool)
+                                     -> ResolveResult<(Target<'a>, bool)> {
         debug!("(resolving item in lexical scope) resolving `{}` in namespace {:?} in `{}`",
                name,
                namespace,
@@ -1476,7 +1575,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         match module_.children.borrow().get(&name) {
             Some(name_bindings) if name_bindings[namespace].defined() => {
                 debug!("top name bindings succeeded");
-                return Success((Target::new(module_.clone(),
+                return Success((Target::new(module_,
                                             name_bindings[namespace].clone(),
                                             Shadowable::Never),
                                 false));
@@ -1491,7 +1590,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         // adjacent import statements are processed as though they mutated the
         // current scope.
         if let Some(import_resolution) = module_.import_resolutions.borrow().get(&name) {
-            match (*import_resolution).target_for_namespace(namespace) {
+            match import_resolution[namespace].target.clone() {
                 None => {
                     // Not found; continue.
                     debug!("(resolving item in lexical scope) found import resolution, but not \
@@ -1501,11 +1600,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 Some(target) => {
                     debug!("(resolving item in lexical scope) using import resolution");
                     // track used imports and extern crates as well
-                    let id = import_resolution.id(namespace);
-                    self.used_imports.insert((id, namespace));
-                    self.record_import_use(id, name);
-                    if let Some(DefId{krate: kid, ..}) = target.target_module.def_id() {
-                        self.used_crates.insert(kid);
+                    let id = import_resolution[namespace].id;
+                    if record_used {
+                        self.used_imports.insert((id, namespace));
+                        self.record_import_use(id, name);
+                        if let Some(DefId{krate: kid, ..}) = target.target_module.def_id() {
+                            self.used_crates.insert(kid);
+                        }
                     }
                     return Success((target, false));
                 }
@@ -1514,9 +1615,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
         // Search for external modules.
         if namespace == TypeNS {
-            // FIXME (21114): In principle unclear `child` *has* to be lifted.
-            let child = module_.external_module_children.borrow().get(&name).cloned();
-            if let Some(module) = child {
+            let children = module_.external_module_children.borrow();
+            if let Some(module) = children.get(&name) {
                 let name_binding = NameBinding::create_from_module(module);
                 debug!("lower name bindings succeeded");
                 return Success((Target::new(module_, name_binding, Shadowable::Never),
@@ -1528,7 +1628,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         let mut search_module = module_;
         loop {
             // Go to the next parent.
-            match search_module.parent_link.clone() {
+            match search_module.parent_link {
                 NoParentLink => {
                     // No more parents. This module was unresolved.
                     debug!("(resolving item in lexical scope) unresolved module");
@@ -1541,16 +1641,16 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                 searching through module parents");
                             return Failed(None);
                     } else {
-                        search_module = parent_module_node.upgrade().unwrap();
+                        search_module = parent_module_node;
                     }
                 }
-                BlockParentLink(ref parent_module_node, _) => {
-                    search_module = parent_module_node.upgrade().unwrap();
+                BlockParentLink(parent_module_node, _) => {
+                    search_module = parent_module_node;
                 }
             }
 
             // Resolve the name in the parent module.
-            match self.resolve_name_in_module(search_module.clone(),
+            match self.resolve_name_in_module(search_module,
                                               name,
                                               namespace,
                                               PathSearch,
@@ -1577,12 +1677,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
     /// Resolves a module name in the current lexical scope.
     fn resolve_module_in_lexical_scope(&mut self,
-                                       module_: Rc<Module>,
+                                       module_: Module<'a>,
                                        name: Name)
-                                       -> ResolveResult<Rc<Module>> {
+                                       -> ResolveResult<Module<'a>> {
         // If this module is an anonymous module, resolve the item in the
         // lexical scope. Otherwise, resolve the item from the crate root.
-        let resolve_result = self.resolve_item_in_lexical_scope(module_, name, TypeNS);
+        let resolve_result = self.resolve_item_in_lexical_scope(module_, name, TypeNS, true);
         match resolve_result {
             Success((target, _)) => {
                 if let Some(module_def) = target.binding.module() {
@@ -1605,14 +1705,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     }
 
     /// Returns the nearest normal module parent of the given module.
-    fn get_nearest_normal_module_parent(&mut self, module_: Rc<Module>) -> Option<Rc<Module>> {
+    fn get_nearest_normal_module_parent(&mut self, module_: Module<'a>) -> Option<Module<'a>> {
         let mut module_ = module_;
         loop {
-            match module_.parent_link.clone() {
+            match module_.parent_link {
                 NoParentLink => return None,
                 ModuleParentLink(new_module, _) |
                 BlockParentLink(new_module, _) => {
-                    let new_module = new_module.upgrade().unwrap();
+                    let new_module = new_module;
                     if new_module.is_normal() {
                         return Some(new_module);
                     }
@@ -1624,11 +1724,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
     /// Returns the nearest normal module parent of the given module, or the
     /// module itself if it is a normal module.
-    fn get_nearest_normal_module_parent_or_self(&mut self, module_: Rc<Module>) -> Rc<Module> {
+    fn get_nearest_normal_module_parent_or_self(&mut self, module_: Module<'a>) -> Module<'a> {
         if module_.is_normal() {
             return module_;
         }
-        match self.get_nearest_normal_module_parent(module_.clone()) {
+        match self.get_nearest_normal_module_parent(module_) {
             None => module_,
             Some(new_module) => new_module,
         }
@@ -1638,9 +1738,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     /// (b) some chain of `super::`.
     /// grammar: (SELF MOD_SEP ) ? (SUPER MOD_SEP) *
     fn resolve_module_prefix(&mut self,
-                             module_: Rc<Module>,
+                             module_: Module<'a>,
                              module_path: &[Name])
-                             -> ResolveResult<ModulePrefixResult> {
+                             -> ResolveResult<ModulePrefixResult<'a>> {
         // Start at the current module if we see `self` or `super`, or at the
         // top of the crate otherwise.
         let mut i = match &*module_path[0].as_str() {
@@ -1676,12 +1776,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     /// The boolean returned on success is an indicator of whether this lookup
     /// passed through a public re-export proxy.
     fn resolve_name_in_module(&mut self,
-                              module_: Rc<Module>,
+                              module_: Module<'a>,
                               name: Name,
                               namespace: Namespace,
                               name_search_type: NameSearchType,
                               allow_private_imports: bool)
-                              -> ResolveResult<(Target, bool)> {
+                              -> ResolveResult<(Target<'a>, bool)> {
         debug!("(resolving name in module) resolving `{}` in `{}`",
                name,
                module_to_string(&*module_));
@@ -1689,10 +1789,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         // First, check the direct children of the module.
         build_reduced_graph::populate_module_if_necessary(self, &module_);
 
-        match module_.children.borrow().get(&name) {
+        let children = module_.children.borrow();
+        match children.get(&name) {
             Some(name_bindings) if name_bindings[namespace].defined() => {
                 debug!("(resolving name in module) found node as child");
-                return Success((Target::new(module_.clone(),
+                return Success((Target::new(module_,
                                             name_bindings[namespace].clone(),
                                             Shadowable::Never),
                                 false));
@@ -1711,14 +1812,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
 
         // Check the list of resolved imports.
-        match module_.import_resolutions.borrow().get(&name) {
-            Some(import_resolution) if allow_private_imports || import_resolution.is_public => {
+        let children = module_.import_resolutions.borrow();
+        match children.get(&name) {
+            Some(import_resolution) if allow_private_imports ||
+                                       import_resolution[namespace].is_public => {
 
-                if import_resolution.is_public && import_resolution.outstanding_references != 0 {
+                if import_resolution[namespace].is_public &&
+                   import_resolution.outstanding_references != 0 {
                     debug!("(resolving name in module) import unresolved; bailing out");
                     return Indeterminate;
                 }
-                match import_resolution.target_for_namespace(namespace) {
+                match import_resolution[namespace].target.clone() {
                     None => {
                         debug!("(resolving name in module) name found, but not in namespace {:?}",
                                namespace);
@@ -1726,7 +1830,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     Some(target) => {
                         debug!("(resolving name in module) resolved to import");
                         // track used imports and extern crates as well
-                        let id = import_resolution.id(namespace);
+                        let id = import_resolution[namespace].id;
                         self.used_imports.insert((id, namespace));
                         self.record_import_use(id, name);
                         if let Some(DefId{krate: kid, ..}) = target.target_module.def_id() {
@@ -1741,9 +1845,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
         // Finally, search through external children.
         if namespace == TypeNS {
-            // FIXME (21114): In principle unclear `child` *has* to be lifted.
-            let child = module_.external_module_children.borrow().get(&name).cloned();
-            if let Some(module) = child {
+            let children = module_.external_module_children.borrow();
+            if let Some(module) = children.get(&name) {
                 let name_binding = NameBinding::create_from_module(module);
                 return Success((Target::new(module_, name_binding, Shadowable::Never),
                                 false));
@@ -1755,7 +1858,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         return Failed(None);
     }
 
-    fn report_unresolved_imports(&mut self, module_: Rc<Module>) {
+    fn report_unresolved_imports(&mut self, module_: Module<'a>) {
         let index = module_.resolved_import_count.get();
         let imports = module_.imports.borrow();
         let import_count = imports.len();
@@ -1780,7 +1883,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
 
         for (_, module_) in module_.anonymous_children.borrow().iter() {
-            self.report_unresolved_imports(module_.clone());
+            self.report_unresolved_imports(module_);
         }
     }
 
@@ -1805,7 +1908,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     fn with_scope<F>(&mut self, name: Option<Name>, f: F)
         where F: FnOnce(&mut Resolver)
     {
-        let orig_module = self.current_module.clone();
+        let orig_module = self.current_module;
 
         // Move down in the graph.
         match name {
@@ -2003,6 +2106,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                                   prefix.span,
                                                   ResolutionError::FailedToResolve(
                                                       &path_names_to_string(prefix, 0)));
+                                    self.record_def(item.id, err_path_resolution());
                                 }
                             }
                         }
@@ -2125,16 +2229,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 debug!("(resolving trait) found trait def: {:?}", path_res);
                 Ok(path_res)
             } else {
-                resolve_error(self,
-                              trait_path.span,
-                              ResolutionError::IsNotATrait(&*path_names_to_string(trait_path,
-                                                                                  path_depth)));
+                let mut err =
+                    resolve_struct_error(self,
+                                  trait_path.span,
+                                  ResolutionError::IsNotATrait(&*path_names_to_string(trait_path,
+                                                                                      path_depth)));
 
                 // If it's a typedef, give a note
                 if let DefTy(..) = path_res.base_def {
-                    self.session
-                        .span_note(trait_path.span, "`type` aliases cannot be used for traits");
+                    err.span_note(trait_path.span,
+                                  "`type` aliases cannot be used for traits");
                 }
+                err.emit();
                 Err(())
             }
         } else {
@@ -2162,6 +2268,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                         resolve_error(self,
                                       eq_pred.span,
                                       ResolutionError::UndeclaredAssociatedType);
+                        self.record_def(eq_pred.id, err_path_resolution());
                     }
                 }
             }
@@ -2192,6 +2299,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 self.record_def(trait_ref.ref_id, path_res);
                 new_val = Some((path_res.base_def.def_id(), trait_ref.clone()));
                 new_id = Some(path_res.base_def.def_id());
+            } else {
+                self.record_def(trait_ref.ref_id, err_path_resolution());
             }
             intravisit::walk_trait_ref(self, trait_ref);
         }
@@ -2387,14 +2496,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         self.value_ribs.push(Rib::new(NormalRibKind));
 
         // Move down in the graph, if there's an anonymous module rooted here.
-        let orig_module = self.current_module.clone();
+        let orig_module = self.current_module;
         match orig_module.anonymous_children.borrow().get(&block.id) {
             None => {
                 // Nothing to do.
             }
             Some(anonymous_module) => {
                 debug!("(resolving block) found anonymous module, moving down");
-                self.current_module = anonymous_module.clone();
+                self.current_module = anonymous_module;
             }
         }
 
@@ -2461,6 +2570,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                         self.record_def(ty.id, def);
                     }
                     None => {
+                        self.record_def(ty.id, err_path_resolution());
+
                         // Keep reporting some errors even if they're ignored above.
                         self.resolve_path(ty.id, path, 0, TypeNS, true);
 
@@ -2543,6 +2654,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                 ResolutionError::DeclarationShadowsEnumVariantOrUnitLikeStruct(
                                     renamed)
                             );
+                            self.record_def(pattern.id, err_path_resolution());
                         }
                         FoundConst(def, lp, _) if const_ok => {
                             debug!("(resolving pattern) resolving `{}` to constant", renamed);
@@ -2562,6 +2674,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                 ResolutionError::OnlyIrrefutablePatternsAllowedHere(def.def_id(),
                                                                                     name)
                             );
+                            self.record_def(pattern.id, err_path_resolution());
                         }
                         BareIdentifierPatternUnresolved => {
                             debug!("(resolving pattern) binding `{}`", renamed);
@@ -2645,6 +2758,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                 resolve_error(&self,
                                               path.span,
                                               ResolutionError::StaticVariableReference);
+                                self.record_def(pattern.id, err_path_resolution());
                             }
                             _ => {
                                 // If anything ends up here entirely resolved,
@@ -2663,6 +2777,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                                  .name
                                                  .as_str())
                                     );
+                                    self.record_def(pattern.id, err_path_resolution());
                                 } else {
                                     let const_name = path.segments
                                                          .last()
@@ -2682,6 +2797,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                             ResolutionError::UnresolvedEnumVariantStructOrConst(
                                 &path.segments.last().unwrap().identifier.name.as_str())
                         );
+                        self.record_def(pattern.id, err_path_resolution());
                     }
                     intravisit::walk_path(self, path);
                 }
@@ -2724,6 +2840,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                         &path.segments.last().unwrap().identifier.name.as_str()
                                     )
                                 );
+                                self.record_def(pattern.id, err_path_resolution());
                             }
                         }
                     } else {
@@ -2735,6 +2852,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                                                                       .identifier
                                                                                       .name
                                                                                       .as_str()));
+                        self.record_def(pattern.id, err_path_resolution());
                     }
                     intravisit::walk_pat(self, pattern);
                 }
@@ -2752,6 +2870,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                 ResolutionError::DoesNotNameAStruct(
                                     &*path_names_to_string(path, 0))
                             );
+                            self.record_def(pattern.id, err_path_resolution());
                         }
                     }
                     intravisit::walk_path(self, path);
@@ -2773,8 +2892,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                        name: Name,
                                        span: Span)
                                        -> BareIdentifierPatternResolution {
-        let module = self.current_module.clone();
-        match self.resolve_item_in_lexical_scope(module, name, ValueNS) {
+        let module = self.current_module;
+        match self.resolve_item_in_lexical_scope(module, name, ValueNS, true) {
             Success((target, _)) => {
                 debug!("(resolve bare identifier pattern) succeeded in finding {} at {:?}",
                        name,
@@ -2882,17 +3001,16 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
 
         // Try to find a path to an item in a module.
-        let unqualified_def = self.resolve_identifier(segments.last().unwrap().identifier,
-                                                      namespace,
-                                                      check_ribs);
-
+        let last_ident = segments.last().unwrap().identifier;
         if segments.len() <= 1 {
+            let unqualified_def = self.resolve_identifier(last_ident, namespace, check_ribs, true);
             return unqualified_def.and_then(|def| self.adjust_local_def(def, span))
                                   .map(|def| {
                                       PathResolution::new(def, LastMod(AllPublic), path_depth)
                                   });
         }
 
+        let unqualified_def = self.resolve_identifier(last_ident, namespace, check_ribs, false);
         let def = self.resolve_module_relative_path(span, segments, namespace);
         match (def, unqualified_def) {
             (Some((ref d, _)), Some(ref ud)) if *d == ud.def => {
@@ -2912,7 +3030,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     fn resolve_identifier(&mut self,
                           identifier: hir::Ident,
                           namespace: Namespace,
-                          check_ribs: bool)
+                          check_ribs: bool,
+                          record_used: bool)
                           -> Option<LocalDef> {
         // First, check to see whether the name is a primitive type.
         if namespace == TypeNS {
@@ -2929,7 +3048,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
         }
 
-        self.resolve_item_by_name_in_lexical_scope(identifier.unhygienic_name, namespace)
+        let name = identifier.unhygienic_name;
+        self.resolve_item_by_name_in_lexical_scope(name, namespace, record_used)
             .map(LocalDef::from_def)
     }
 
@@ -3036,7 +3156,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
         let containing_module;
         let last_private;
-        let current_module = self.current_module.clone();
+        let current_module = self.current_module;
         match self.resolve_module_path(current_module,
                                        &module_path[..],
                                        UseLexicalScope,
@@ -3063,7 +3183,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
 
         let name = segments.last().unwrap().identifier.name;
-        let def = match self.resolve_name_in_module(containing_module.clone(),
+        let def = match self.resolve_name_in_module(containing_module,
                                                     name,
                                                     namespace,
                                                     NameSearchType::PathSearch,
@@ -3094,7 +3214,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                   .map(|ps| ps.identifier.name)
                                   .collect::<Vec<_>>();
 
-        let root_module = self.graph_root.clone();
+        let root_module = self.graph_root;
 
         let containing_module;
         let last_private;
@@ -3180,11 +3300,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
     fn resolve_item_by_name_in_lexical_scope(&mut self,
                                              name: Name,
-                                             namespace: Namespace)
+                                             namespace: Namespace,
+                                             record_used: bool)
                                              -> Option<Def> {
         // Check the items.
-        let module = self.current_module.clone();
-        match self.resolve_item_in_lexical_scope(module, name, namespace) {
+        let module = self.current_module;
+        match self.resolve_item_in_lexical_scope(module, name, namespace, record_used) {
             Success((target, _)) => {
                 match target.binding.def() {
                     None => {
@@ -3245,11 +3366,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
         }
 
-        fn get_module(this: &mut Resolver,
-                      span: Span,
-                      name_path: &[ast::Name])
-                      -> Option<Rc<Module>> {
-            let root = this.current_module.clone();
+        fn get_module<'a, 'tcx>(this: &mut Resolver<'a, 'tcx>,
+                                span: Span,
+                                name_path: &[ast::Name])
+                                -> Option<Module<'a>> {
+            let root = this.current_module;
             let last_name = name_path.last().unwrap();
 
             if name_path.len() == 1 {
@@ -3350,39 +3471,22 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         NoSuggestion
     }
 
-    fn find_best_match_for_name(&mut self, name: &str) -> SuggestionType {
-        let mut maybes: Vec<token::InternedString> = Vec::new();
-        let mut values: Vec<usize> = Vec::new();
-
+    fn find_best_match(&mut self, name: &str) -> SuggestionType {
         if let Some(macro_name) = self.session.available_macros
-                                 .borrow().iter().find(|n| n.as_str() == name) {
+                                  .borrow().iter().find(|n| n.as_str() == name) {
             return SuggestionType::Macro(format!("{}!", macro_name));
         }
 
-        for rib in self.value_ribs.iter().rev() {
-            for (&k, _) in &rib.bindings {
-                maybes.push(k.as_str());
-                values.push(usize::MAX);
-            }
-        }
-
-        let mut smallest = 0;
-        for (i, other) in maybes.iter().enumerate() {
-            values[i] = lev_distance(name, &other);
+        let names = self.value_ribs
+                    .iter()
+                    .rev()
+                    .flat_map(|rib| rib.bindings.keys());
 
-            if values[i] <= values[smallest] {
-                smallest = i;
+        if let Some(found) = find_best_match_for_name(names, name, None) {
+            if name != &*found {
+                return SuggestionType::Function(found);
             }
-        }
-
-        let max_distance = max_suggestion_distance(name);
-        if !values.is_empty() && values[smallest] <= max_distance && name != &maybes[smallest][..] {
-
-            SuggestionType::Function(maybes[smallest].to_string())
-
-        } else {
-            SuggestionType::NotFound
-        }
+        } SuggestionType::NotFound
     }
 
     fn resolve_expr(&mut self, expr: &Expr) {
@@ -3417,17 +3521,19 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     if let DefVariant(_, _, true) = path_res.base_def {
                         let path_name = path_names_to_string(path, 0);
 
-                        resolve_error(self,
-                                      expr.span,
-                                      ResolutionError::StructVariantUsedAsFunction(&*path_name));
+                        let mut err = resolve_struct_error(self,
+                                        expr.span,
+                                        ResolutionError::StructVariantUsedAsFunction(&*path_name));
 
                         let msg = format!("did you mean to write: `{} {{ /* fields */ }}`?",
                                           path_name);
                         if self.emit_errors {
-                            self.session.fileline_help(expr.span, &msg);
+                            err.fileline_help(expr.span, &msg);
                         } else {
-                            self.session.span_help(expr.span, &msg);
+                            err.span_help(expr.span, &msg);
                         }
+                        err.emit();
+                        self.record_def(expr.id, err_path_resolution());
                     } else {
                         // Write the result into the def map.
                         debug!("(resolving expr) resolved `{}`",
@@ -3452,22 +3558,22 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     let type_res = self.with_no_errors(|this| {
                         this.resolve_path(expr.id, path, 0, TypeNS, false)
                     });
+
+                    self.record_def(expr.id, err_path_resolution());
                     match type_res.map(|r| r.base_def) {
                         Some(DefTy(struct_id, _)) if self.structs.contains_key(&struct_id) => {
-                            resolve_error(
-                                    self,
-                                    expr.span,
-                                    ResolutionError::StructVariantUsedAsFunction(
-                                        &*path_name)
-                                );
+                            let mut err = resolve_struct_error(self,
+                                expr.span,
+                                ResolutionError::StructVariantUsedAsFunction(&*path_name));
 
                             let msg = format!("did you mean to write: `{} {{ /* fields */ }}`?",
                                               path_name);
                             if self.emit_errors {
-                                self.session.fileline_help(expr.span, &msg);
+                                err.fileline_help(expr.span, &msg);
                             } else {
-                                self.session.span_help(expr.span, &msg);
+                                err.span_help(expr.span, &msg);
                             }
+                            err.emit();
                         }
                         _ => {
                             // Keep reporting some errors even if they're ignored above.
@@ -3493,7 +3599,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                     NoSuggestion => {
                                         // limit search to 5 to reduce the number
                                         // of stupid suggestions
-                                        match self.find_best_match_for_name(&path_name) {
+                                        match self.find_best_match(&path_name) {
                                             SuggestionType::Macro(s) => {
                                                 format!("the macro `{}`", s)
                                             }
@@ -3509,13 +3615,33 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                         format!("to call `{}::{}`", path_str, path_name),
                                 };
 
+                                let mut context =  UnresolvedNameContext::Other;
                                 if !msg.is_empty() {
-                                    msg = format!(". Did you mean {}?", msg)
+                                    msg = format!(". Did you mean {}?", msg);
+                                } else {
+                                    // we check if this a module and if so, we display a help
+                                    // message
+                                    let name_path = path.segments.iter()
+                                                        .map(|seg| seg.identifier.name)
+                                                        .collect::<Vec<_>>();
+                                    let current_module = self.current_module;
+
+                                    match self.resolve_module_path(current_module,
+                                                   &name_path[..],
+                                                   UseLexicalScope,
+                                                   expr.span,
+                                                   PathSearch) {
+                                        Success(_) => {
+                                            context = UnresolvedNameContext::PathIsMod(expr.id);
+                                        },
+                                        _ => {},
+                                    };
                                 }
 
                                 resolve_error(self,
                                               expr.span,
-                                              ResolutionError::UnresolvedName(&*path_name, &*msg));
+                                              ResolutionError::UnresolvedName(
+                                                  &*path_name, &*msg, context));
                             }
                         }
                     }
@@ -3538,6 +3664,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                       ResolutionError::DoesNotNameAStruct(
                                                                 &*path_names_to_string(path, 0))
                                      );
+                        self.record_def(expr.id, err_path_resolution());
                     }
                 }
 
@@ -3560,6 +3687,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             ExprBreak(Some(label)) | ExprAgain(Some(label)) => {
                 match self.search_label(label.node.name) {
                     None => {
+                        self.record_def(expr.id, err_path_resolution());
                         resolve_error(self,
                                       label.span,
                                       ResolutionError::UndeclaredLabel(&label.node.name.as_str()))
@@ -3618,7 +3746,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
 
         let mut found_traits = Vec::new();
-        let mut search_module = self.current_module.clone();
+        let mut search_module = self.current_module;
         loop {
             // Look for the current trait.
             match self.current_trait_ref {
@@ -3651,9 +3779,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
             // Look for imports.
             for (_, import) in search_module.import_resolutions.borrow().iter() {
-                let target = match import.target_for_namespace(TypeNS) {
+                let target = match import.type_ns.target {
                     None => continue,
-                    Some(target) => target,
+                    Some(ref target) => target,
                 };
                 let did = match target.binding.def() {
                     Some(DefTrait(trait_def_id)) => trait_def_id,
@@ -3661,7 +3789,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 };
                 if self.trait_item_map.contains_key(&(name, did)) {
                     add_trait_info(&mut found_traits, did, name);
-                    let id = import.type_id;
+                    let id = import.type_ns.id;
                     self.used_imports.insert((id, TypeNS));
                     let trait_name = self.get_trait_name(did);
                     self.record_import_use(id, trait_name);
@@ -3671,10 +3799,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 }
             }
 
-            match search_module.parent_link.clone() {
+            match search_module.parent_link {
                 NoParentLink | ModuleParentLink(..) => break,
                 BlockParentLink(parent_module, _) => {
-                    search_module = parent_module.upgrade().unwrap();
+                    search_module = parent_module;
                 }
             }
         }
@@ -3721,7 +3849,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     //
 
     #[allow(dead_code)]   // useful for debugging
-    fn dump_module(&mut self, module_: Rc<Module>) {
+    fn dump_module(&mut self, module_: Module<'a>) {
         debug!("Dump of module `{}`:", module_to_string(&*module_));
 
         debug!("Children:");
@@ -3734,7 +3862,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         let import_resolutions = module_.import_resolutions.borrow();
         for (&name, import_resolution) in import_resolutions.iter() {
             let value_repr;
-            match import_resolution.target_for_namespace(ValueNS) {
+            match import_resolution.value_ns.target {
                 None => {
                     value_repr = "".to_string();
                 }
@@ -3745,7 +3873,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
 
             let type_repr;
-            match import_resolution.target_for_namespace(TypeNS) {
+            match import_resolution.type_ns.target {
                 None => {
                     type_repr = "".to_string();
                 }
@@ -3784,20 +3912,20 @@ fn path_names_to_string(path: &Path, depth: usize) -> String {
 }
 
 /// A somewhat inefficient routine to obtain the name of a module.
-fn module_to_string(module: &Module) -> String {
+fn module_to_string<'a>(module: Module<'a>) -> String {
     let mut names = Vec::new();
 
-    fn collect_mod(names: &mut Vec<ast::Name>, module: &Module) {
+    fn collect_mod<'a>(names: &mut Vec<ast::Name>, module: Module<'a>) {
         match module.parent_link {
             NoParentLink => {}
             ModuleParentLink(ref module, name) => {
                 names.push(name);
-                collect_mod(names, &*module.upgrade().unwrap());
+                collect_mod(names, module);
             }
             BlockParentLink(ref module, _) => {
                 // danger, shouldn't be ident?
                 names.push(special_idents::opaque.name);
-                collect_mod(names, &*module.upgrade().unwrap());
+                collect_mod(names, module);
             }
         }
     }
@@ -3809,6 +3937,14 @@ fn module_to_string(module: &Module) -> String {
     names_to_string(&names.into_iter().rev().collect::<Vec<ast::Name>>())
 }
 
+fn err_path_resolution() -> PathResolution {
+    PathResolution {
+        base_def: DefErr,
+        last_private: LastMod(AllPublic),
+        depth: 0,
+    }
+}
+
 
 pub struct CrateMap {
     pub def_map: RefCell<DefMap>,
@@ -3831,10 +3967,10 @@ pub fn resolve_crate<'a, 'tcx>(session: &'a Session,
                                make_glob_map: MakeGlobMap)
                                -> CrateMap {
     let krate = ast_map.krate();
-    let mut resolver = create_resolver(session, ast_map, krate, make_glob_map, None);
+    let arenas = Resolver::arenas();
+    let mut resolver = create_resolver(session, ast_map, krate, make_glob_map, &arenas, None);
 
     resolver.resolve_crate(krate);
-    session.abort_if_errors();
 
     check_unused::check_crate(&mut resolver, krate);
 
@@ -3864,9 +4000,10 @@ pub fn create_resolver<'a, 'tcx>(session: &'a Session,
                                  ast_map: &'a hir_map::Map<'tcx>,
                                  krate: &'a Crate,
                                  make_glob_map: MakeGlobMap,
+                                 arenas: &'a ResolverArenas<'a>,
                                  callback: Option<Box<Fn(hir_map::Node, &mut bool) -> bool>>)
                                  -> Resolver<'a, 'tcx> {
-    let mut resolver = Resolver::new(session, ast_map, make_glob_map);
+    let mut resolver = Resolver::new(session, ast_map, make_glob_map, arenas);
 
     resolver.callback = callback;
 
@@ -3876,9 +4013,6 @@ pub fn create_resolver<'a, 'tcx>(session: &'a Session,
     resolve_imports::resolve_imports(&mut resolver);
     session.abort_if_errors();
 
-    record_exports::record(&mut resolver);
-    session.abort_if_errors();
-
     resolver
 }
 
diff --git a/src/librustc_resolve/record_exports.rs b/src/librustc_resolve/record_exports.rs
deleted file mode 100644 (file)
index 3a6a5a0..0000000
+++ /dev/null
@@ -1,154 +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.
-
-
-// Export recording
-//
-// This pass simply determines what all "export" keywords refer to and
-// writes the results into the export map.
-//
-// FIXME #4953 This pass will be removed once exports change to per-item.
-// Then this operation can simply be performed as part of item (or import)
-// processing.
-
-use {Module, NameBinding, Resolver};
-use Namespace::{TypeNS, ValueNS};
-
-use build_reduced_graph;
-use module_to_string;
-
-use rustc::middle::def::Export;
-use syntax::ast;
-
-use std::ops::{Deref, DerefMut};
-use std::rc::Rc;
-
-struct ExportRecorder<'a, 'b: 'a, 'tcx: 'b> {
-    resolver: &'a mut Resolver<'b, 'tcx>,
-}
-
-// Deref and DerefMut impls allow treating ExportRecorder as Resolver.
-impl<'a, 'b, 'tcx:'b> Deref for ExportRecorder<'a, 'b, 'tcx> {
-    type Target = Resolver<'b, 'tcx>;
-
-    fn deref<'c>(&'c self) -> &'c Resolver<'b, 'tcx> {
-        &*self.resolver
-    }
-}
-
-impl<'a, 'b, 'tcx:'b> DerefMut for ExportRecorder<'a, 'b, 'tcx> {
-    fn deref_mut<'c>(&'c mut self) -> &'c mut Resolver<'b, 'tcx> {
-        &mut *self.resolver
-    }
-}
-
-impl<'a, 'b, 'tcx> ExportRecorder<'a, 'b, 'tcx> {
-    fn record_exports_for_module_subtree(&mut self, module_: Rc<Module>) {
-        // If this isn't a local krate, then bail out. We don't need to record
-        // exports for nonlocal crates.
-
-        match module_.def_id() {
-            Some(def_id) if def_id.is_local() => {
-                // OK. Continue.
-                debug!("(recording exports for module subtree) recording exports for local \
-                        module `{}`",
-                       module_to_string(&*module_));
-            }
-            None => {
-                // Record exports for the root module.
-                debug!("(recording exports for module subtree) recording exports for root module \
-                        `{}`",
-                       module_to_string(&*module_));
-            }
-            Some(_) => {
-                // Bail out.
-                debug!("(recording exports for module subtree) not recording exports for `{}`",
-                       module_to_string(&*module_));
-                return;
-            }
-        }
-
-        self.record_exports_for_module(&*module_);
-        build_reduced_graph::populate_module_if_necessary(self.resolver, &module_);
-
-        for (_, child_name_bindings) in module_.children.borrow().iter() {
-            match child_name_bindings.type_ns.module() {
-                None => {
-                    // Nothing to do.
-                }
-                Some(child_module) => {
-                    self.record_exports_for_module_subtree(child_module);
-                }
-            }
-        }
-
-        for (_, child_module) in module_.anonymous_children.borrow().iter() {
-            self.record_exports_for_module_subtree(child_module.clone());
-        }
-    }
-
-    fn record_exports_for_module(&mut self, module_: &Module) {
-        let mut exports = Vec::new();
-
-        self.add_exports_for_module(&mut exports, module_);
-        match module_.def_id() {
-            Some(def_id) => {
-                let node_id = self.ast_map.as_local_node_id(def_id).unwrap();
-                self.export_map.insert(node_id, exports);
-                debug!("(computing exports) writing exports for {} (some)", node_id);
-            }
-            None => {}
-        }
-    }
-
-    fn add_export_of_namebinding(&mut self,
-                                 exports: &mut Vec<Export>,
-                                 name: ast::Name,
-                                 namebinding: &NameBinding) {
-        match namebinding.def() {
-            Some(d) => {
-                debug!("(computing exports) YES: export '{}' => {:?}",
-                       name,
-                       d.def_id());
-                exports.push(Export {
-                    name: name,
-                    def_id: d.def_id(),
-                });
-            }
-            d_opt => {
-                debug!("(computing exports) NO: {:?}", d_opt);
-            }
-        }
-    }
-
-    fn add_exports_for_module(&mut self, exports: &mut Vec<Export>, module_: &Module) {
-        for (name, import_resolution) in module_.import_resolutions.borrow().iter() {
-            if !import_resolution.is_public {
-                continue;
-            }
-            let xs = [TypeNS, ValueNS];
-            for &ns in &xs {
-                match import_resolution.target_for_namespace(ns) {
-                    Some(target) => {
-                        debug!("(computing exports) maybe export '{}'", name);
-                        self.add_export_of_namebinding(exports, *name, &target.binding)
-                    }
-                    _ => (),
-                }
-            }
-        }
-    }
-}
-
-pub fn record(resolver: &mut Resolver) {
-    let mut recorder = ExportRecorder { resolver: resolver };
-    let root_module = recorder.graph_root.clone();
-    recorder.record_exports_for_module_subtree(root_module);
-}
index 460c851e13f2e42eaa918752777a065bd6874927..abaf45cb1704d0abcf2ecf756f9306e2673b277d 100644 (file)
@@ -25,6 +25,7 @@ use {resolve_error, ResolutionError};
 
 use build_reduced_graph;
 
+use rustc::lint;
 use rustc::middle::def::*;
 use rustc::middle::def_id::DefId;
 use rustc::middle::privacy::*;
@@ -32,10 +33,9 @@ use rustc::middle::privacy::*;
 use syntax::ast::{NodeId, Name};
 use syntax::attr::AttrMetaMethods;
 use syntax::codemap::Span;
+use syntax::util::lev_distance::find_best_match_for_name;
 
 use std::mem::replace;
-use std::rc::Rc;
-
 
 /// Contains data for specific types of import directives.
 #[derive(Copy, Clone,Debug)]
@@ -58,7 +58,7 @@ pub struct ImportDirective {
     pub subclass: ImportDirectiveSubclass,
     pub span: Span,
     pub id: NodeId,
-    pub is_public: bool, // see note in ImportResolution about how to use this
+    pub is_public: bool, // see note in ImportResolutionPerNamespace about how to use this
     pub shadowable: Shadowable,
 }
 
@@ -83,17 +83,15 @@ impl ImportDirective {
 
 /// The item that an import resolves to.
 #[derive(Clone,Debug)]
-pub struct Target {
-    pub target_module: Rc<Module>,
-    pub binding: NameBinding,
+pub struct Target<'a> {
+    pub target_module: Module<'a>,
+    pub binding: NameBinding<'a>,
     pub shadowable: Shadowable,
 }
 
-impl Target {
-    pub fn new(target_module: Rc<Module>,
-               binding: NameBinding,
-               shadowable: Shadowable)
-               -> Target {
+impl<'a> Target<'a> {
+    pub fn new(target_module: Module<'a>, binding: NameBinding<'a>, shadowable: Shadowable)
+               -> Self {
         Target {
             target_module: target_module,
             binding: binding,
@@ -102,80 +100,61 @@ impl Target {
     }
 }
 
-/// An ImportResolution represents a particular `use` directive.
 #[derive(Debug)]
-pub struct ImportResolution {
-    /// Whether this resolution came from a `use` or a `pub use`. Note that this
-    /// should *not* be used whenever resolution is being performed. Privacy
-    /// testing occurs during a later phase of compilation.
+/// An ImportResolutionPerNamespace records what we know about an imported name.
+/// More specifically, it records the number of unresolved `use` directives that import the name,
+/// and for each namespace, it records the `use` directive importing the name in the namespace
+/// and the `Target` to which the name in the namespace resolves (if applicable).
+/// Different `use` directives may import the same name in different namespaces.
+pub struct ImportResolutionPerNamespace<'a> {
+    // When outstanding_references reaches zero, outside modules can count on the targets being
+    // correct. Before then, all bets are off; future `use` directives could override the name.
+    // Since shadowing is forbidden, the only way outstanding_references > 1 in a legal program
+    // is if the name is imported by exactly two `use` directives, one of which resolves to a
+    // value and the other of which resolves to a type.
+    pub outstanding_references: usize,
+    pub type_ns: ImportResolution<'a>,
+    pub value_ns: ImportResolution<'a>,
+}
+
+/// Records what we know about an imported name in a namespace (see `ImportResolutionPerNamespace`).
+#[derive(Clone,Debug)]
+pub struct ImportResolution<'a> {
+    /// Whether the name in the namespace was imported with a `use` or a `pub use`.
     pub is_public: bool,
 
-    // The number of outstanding references to this name. When this reaches
-    // zero, outside modules can count on the targets being correct. Before
-    // then, all bets are off; future imports could override this name.
-    // Note that this is usually either 0 or 1 - shadowing is forbidden the only
-    // way outstanding_references is > 1 in a legal program is if the name is
-    // used in both namespaces.
-    pub outstanding_references: usize,
+    /// Resolution of the name in the namespace
+    pub target: Option<Target<'a>>,
 
-    /// The value that this `use` directive names, if there is one.
-    pub value_target: Option<Target>,
-    /// The source node of the `use` directive leading to the value target
-    /// being non-none
-    pub value_id: NodeId,
-
-    /// The type that this `use` directive names, if there is one.
-    pub type_target: Option<Target>,
-    /// The source node of the `use` directive leading to the type target
-    /// being non-none
-    pub type_id: NodeId,
+    /// The source node of the `use` directive
+    pub id: NodeId,
 }
 
-impl ImportResolution {
-    pub fn new(id: NodeId, is_public: bool) -> ImportResolution {
-        ImportResolution {
-            type_id: id,
-            value_id: id,
-            outstanding_references: 0,
-            value_target: None,
-            type_target: None,
-            is_public: is_public,
-        }
+impl<'a> ::std::ops::Index<Namespace> for ImportResolutionPerNamespace<'a> {
+    type Output = ImportResolution<'a>;
+    fn index(&self, ns: Namespace) -> &ImportResolution<'a> {
+        match ns { TypeNS => &self.type_ns, ValueNS => &self.value_ns }
     }
+}
 
-    pub fn target_for_namespace(&self, namespace: Namespace) -> Option<Target> {
-        match namespace {
-            TypeNS => self.type_target.clone(),
-            ValueNS => self.value_target.clone(),
-        }
+impl<'a> ::std::ops::IndexMut<Namespace> for ImportResolutionPerNamespace<'a> {
+    fn index_mut(&mut self, ns: Namespace) -> &mut ImportResolution<'a> {
+        match ns { TypeNS => &mut self.type_ns, ValueNS => &mut self.value_ns }
     }
+}
 
-    pub fn id(&self, namespace: Namespace) -> NodeId {
-        match namespace {
-            TypeNS => self.type_id,
-            ValueNS => self.value_id,
+impl<'a> ImportResolutionPerNamespace<'a> {
+    pub fn new(id: NodeId, is_public: bool) -> Self {
+        let resolution = ImportResolution { id: id, is_public: is_public, target: None };
+        ImportResolutionPerNamespace {
+            outstanding_references: 0, type_ns: resolution.clone(), value_ns: resolution,
         }
     }
 
     pub fn shadowable(&self, namespace: Namespace) -> Shadowable {
-        let target = self.target_for_namespace(namespace);
-        if target.is_none() {
-            return Shadowable::Always;
-        }
-
-        target.unwrap().shadowable
-    }
-
-    pub fn set_target_and_id(&mut self, namespace: Namespace, target: Option<Target>, id: NodeId) {
-        match namespace {
-            TypeNS => {
-                self.type_target = target;
-                self.type_id = id;
-            }
-            ValueNS => {
-                self.value_target = target;
-                self.value_id = id;
-            }
+        match self[namespace].target {
+            Some(ref target) => target.shadowable,
+            None => Shadowable::Always,
         }
     }
 }
@@ -209,8 +188,8 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                    i,
                    self.resolver.unresolved_imports);
 
-            let module_root = self.resolver.graph_root.clone();
-            let errors = self.resolve_imports_for_module_subtree(module_root.clone());
+            let module_root = self.resolver.graph_root;
+            let errors = self.resolve_imports_for_module_subtree(module_root);
 
             if self.resolver.unresolved_imports == 0 {
                 debug!("(resolving imports) success");
@@ -243,13 +222,13 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
     /// Attempts to resolve imports for the given module and all of its
     /// submodules.
     fn resolve_imports_for_module_subtree(&mut self,
-                                          module_: Rc<Module>)
+                                          module_: Module<'b>)
                                           -> Vec<ImportResolvingError> {
         let mut errors = Vec::new();
         debug!("(resolving imports for module subtree) resolving {}",
                module_to_string(&*module_));
-        let orig_module = replace(&mut self.resolver.current_module, module_.clone());
-        errors.extend(self.resolve_imports_for_module(module_.clone()));
+        let orig_module = replace(&mut self.resolver.current_module, module_);
+        errors.extend(self.resolve_imports_for_module(module_));
         self.resolver.current_module = orig_module;
 
         build_reduced_graph::populate_module_if_necessary(self.resolver, &module_);
@@ -265,14 +244,14 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
         }
 
         for (_, child_module) in module_.anonymous_children.borrow().iter() {
-            errors.extend(self.resolve_imports_for_module_subtree(child_module.clone()));
+            errors.extend(self.resolve_imports_for_module_subtree(child_module));
         }
 
         errors
     }
 
     /// Attempts to resolve imports for the given module only.
-    fn resolve_imports_for_module(&mut self, module: Rc<Module>) -> Vec<ImportResolvingError> {
+    fn resolve_imports_for_module(&mut self, module: Module<'b>) -> Vec<ImportResolvingError> {
         let mut errors = Vec::new();
 
         if module.all_imports_resolved() {
@@ -286,7 +265,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
         let mut indeterminate_imports = Vec::new();
         while module.resolved_import_count.get() + indeterminate_imports.len() < import_count {
             let import_index = module.resolved_import_count.get();
-            match self.resolve_import_for_module(module.clone(), &imports[import_index]) {
+            match self.resolve_import_for_module(module, &imports[import_index]) {
                 ResolveResult::Failed(err) => {
                     let import_directive = &imports[import_index];
                     let (span, help) = match err {
@@ -324,7 +303,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
     /// currently-unresolved imports, or success if we know the name exists.
     /// If successful, the resolved bindings are written into the module.
     fn resolve_import_for_module(&mut self,
-                                 module_: Rc<Module>,
+                                 module_: Module<'b>,
                                  import_directive: &ImportDirective)
                                  -> ResolveResult<()> {
         let mut resolution_result = ResolveResult::Failed(None);
@@ -337,9 +316,9 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
         // First, resolve the module path for the directive, if necessary.
         let container = if module_path.is_empty() {
             // Use the crate root.
-            Some((self.resolver.graph_root.clone(), LastMod(AllPublic)))
+            Some((self.resolver.graph_root, LastMod(AllPublic)))
         } else {
-            match self.resolver.resolve_module_path(module_.clone(),
+            match self.resolver.resolve_module_path(module_,
                                                     &module_path[..],
                                                     UseLexicalScopeFlag::DontUseLexicalScope,
                                                     import_directive.span,
@@ -417,8 +396,8 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
     }
 
     fn resolve_single_import(&mut self,
-                             module_: &Module,
-                             target_module: Rc<Module>,
+                             module_: Module<'b>,
+                             target_module: Module<'b>,
                              target: Name,
                              source: Name,
                              directive: &ImportDirective,
@@ -443,46 +422,74 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
         };
 
         // We need to resolve both namespaces for this to succeed.
-        //
 
         let mut value_result = UnknownResult;
         let mut type_result = UnknownResult;
+        let mut lev_suggestion = "".to_owned();
 
         // Search for direct children of the containing module.
         build_reduced_graph::populate_module_if_necessary(self.resolver, &target_module);
 
         match target_module.children.borrow().get(&source) {
             None => {
-                // Continue.
+                let names = target_module.children.borrow();
+                if let Some(name) = find_best_match_for_name(names.keys(),
+                                                             &source.as_str(),
+                                                             None) {
+                    lev_suggestion = format!(". Did you mean to use `{}`?", name);
+                }
             }
             Some(ref child_name_bindings) => {
                 // pub_err makes sure we don't give the same error twice.
                 let mut pub_err = false;
                 if child_name_bindings.value_ns.defined() {
                     debug!("(resolving single import) found value binding");
-                    value_result = BoundResult(target_module.clone(),
+                    value_result = BoundResult(target_module,
                                                child_name_bindings.value_ns.clone());
                     if directive.is_public && !child_name_bindings.value_ns.is_public() {
                         let msg = format!("`{}` is private, and cannot be reexported", source);
                         let note_msg = format!("Consider marking `{}` as `pub` in the imported \
                                                 module",
                                                source);
-                        span_err!(self.resolver.session, directive.span, E0364, "{}", &msg);
-                        self.resolver.session.span_note(directive.span, &note_msg);
+                        struct_span_err!(self.resolver.session, directive.span, E0364, "{}", &msg)
+                            .span_note(directive.span, &note_msg)
+                            .emit();
+                        pub_err = true;
+                    }
+                    if directive.is_public && child_name_bindings.value_ns.
+                                              defined_with(DefModifiers::PRIVATE_VARIANT) {
+                        let msg = format!("variant `{}` is private, and cannot be reexported ( \
+                                           error E0364), consider declaring its enum as `pub`",
+                                           source);
+                        self.resolver.session.add_lint(lint::builtin::PRIVATE_IN_PUBLIC,
+                                                       directive.id,
+                                                       directive.span,
+                                                       msg);
                         pub_err = true;
                     }
                 }
                 if child_name_bindings.type_ns.defined() {
                     debug!("(resolving single import) found type binding");
-                    type_result = BoundResult(target_module.clone(),
+                    type_result = BoundResult(target_module,
                                               child_name_bindings.type_ns.clone());
                     if !pub_err && directive.is_public &&
                        !child_name_bindings.type_ns.is_public() {
                         let msg = format!("`{}` is private, and cannot be reexported", source);
                         let note_msg = format!("Consider declaring module `{}` as a `pub mod`",
                                                source);
-                        span_err!(self.resolver.session, directive.span, E0365, "{}", &msg);
-                        self.resolver.session.span_note(directive.span, &note_msg);
+                        struct_span_err!(self.resolver.session, directive.span, E0365, "{}", &msg)
+                            .span_note(directive.span, &note_msg)
+                            .emit();
+                    }
+                    if !pub_err && directive.is_public && child_name_bindings.type_ns.
+                                                    defined_with(DefModifiers::PRIVATE_VARIANT) {
+                        let msg = format!("variant `{}` is private, and cannot be reexported ( \
+                                           error E0365), consider declaring its enum as `pub`",
+                                           source);
+                        self.resolver.session.add_lint(lint::builtin::PRIVATE_IN_PUBLIC,
+                                                       directive.id,
+                                                       directive.span,
+                                                       msg);
                     }
                 }
             }
@@ -513,6 +520,17 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                         // therefore accurately report that the names are
                         // unbound.
 
+                        if lev_suggestion.is_empty() {  // skip if we already have a suggestion
+                            let names = target_module.import_resolutions.borrow();
+                            if let Some(name) = find_best_match_for_name(names.keys(),
+                                                                         &source.as_str(),
+                                                                         None) {
+                                lev_suggestion =
+                                    format!(". Did you mean to use the re-exported import `{}`?",
+                                            name);
+                            }
+                        }
+
                         if value_result.is_unknown() {
                             value_result = UnboundResult;
                         }
@@ -522,19 +540,19 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                     }
                     Some(import_resolution) if import_resolution.outstanding_references == 0 => {
 
-                        fn get_binding(this: &mut Resolver,
-                                       import_resolution: &ImportResolution,
-                                       namespace: Namespace,
-                                       source: Name)
-                                       -> NamespaceResult {
+                        fn get_binding<'a>(this: &mut Resolver,
+                                           import_resolution: &ImportResolutionPerNamespace<'a>,
+                                           namespace: Namespace,
+                                           source: Name)
+                                           -> NamespaceResult<'a> {
 
                             // Import resolutions must be declared with "pub"
                             // in order to be exported.
-                            if !import_resolution.is_public {
+                            if !import_resolution[namespace].is_public {
                                 return UnboundResult;
                             }
 
-                            match import_resolution.target_for_namespace(namespace) {
+                            match import_resolution[namespace].target.clone() {
                                 None => {
                                     return UnboundResult;
                                 }
@@ -545,7 +563,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                                 }) => {
                                     debug!("(resolving single import) found import in ns {:?}",
                                            namespace);
-                                    let id = import_resolution.id(namespace);
+                                    let id = import_resolution[namespace].id;
                                     // track used imports and extern crates as well
                                     this.used_imports.insert((id, namespace));
                                     this.record_import_use(id, source);
@@ -567,14 +585,14 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                                                        import_resolution,
                                                        ValueNS,
                                                        source);
-                            value_used_reexport = import_resolution.is_public;
+                            value_used_reexport = import_resolution.value_ns.is_public;
                         }
                         if type_result.is_unknown() {
                             type_result = get_binding(self.resolver,
                                                       import_resolution,
                                                       TypeNS,
                                                       source);
-                            type_used_reexport = import_resolution.is_public;
+                            type_used_reexport = import_resolution.type_ns.is_public;
                         }
 
                     }
@@ -619,7 +637,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
         match type_result {
             BoundResult(..) => {}
             _ => {
-                match target_module.external_module_children.borrow_mut().get(&source).cloned() {
+                match target_module.external_module_children.borrow_mut().get(&source) {
                     None => {} // Continue.
                     Some(module) => {
                         debug!("(resolving single import) found external module");
@@ -631,7 +649,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                             _ => {}
                         }
                         let name_binding = NameBinding::create_from_module(module);
-                        type_result = BoundResult(target_module.clone(), name_binding);
+                        type_result = BoundResult(target_module, name_binding);
                         type_used_public = true;
                     }
                 }
@@ -663,11 +681,15 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                                                              directive.span,
                                                              target);
 
-                        let target = Some(Target::new(target_module.clone(),
-                                                      name_binding.clone(),
-                                                      directive.shadowable));
-                        import_resolution.set_target_and_id(namespace, target, directive.id);
-                        import_resolution.is_public = directive.is_public;
+                        import_resolution[namespace] = ImportResolution {
+                            target: Some(Target::new(target_module,
+                                                     name_binding.clone(),
+                                                     directive.shadowable)),
+                            id: directive.id,
+                            is_public: directive.is_public
+                        };
+
+                        self.add_export(module_, target, &import_resolution[namespace]);
                         *used_public = name_binding.is_public();
                     }
                     UnboundResult => {
@@ -688,9 +710,9 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                                                            target);
 
         if value_result.is_unbound() && type_result.is_unbound() {
-            let msg = format!("There is no `{}` in `{}`",
+            let msg = format!("There is no `{}` in `{}`{}",
                               source,
-                              module_to_string(&target_module));
+                              module_to_string(&target_module), lev_suggestion);
             return ResolveResult::Failed(Some((directive.span, msg)));
         }
         let value_used_public = value_used_reexport || value_used_public;
@@ -702,7 +724,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
         // Record what this import resolves to for later uses in documentation,
         // this may resolve to either a value or a type, but for documentation
         // purposes it's good enough to just favor one over the other.
-        let value_def_and_priv = import_resolution.value_target.as_ref().map(|target| {
+        let value_def_and_priv = import_resolution.value_ns.target.as_ref().map(|target| {
             let def = target.binding.def().unwrap();
             (def,
              if value_used_public {
@@ -711,7 +733,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                 DependsOn(def.def_id())
             })
         });
-        let type_def_and_priv = import_resolution.type_target.as_ref().map(|target| {
+        let type_def_and_priv = import_resolution.type_ns.target.as_ref().map(|target| {
             let def = target.binding.def().unwrap();
             (def,
              if type_used_public {
@@ -754,8 +776,8 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
     // that exports nothing is valid). target_module is the module we are
     // actually importing, i.e., `foo` in `use foo::*`.
     fn resolve_glob_import(&mut self,
-                           module_: &Module,
-                           target_module: Rc<Module>,
+                           module_: Module<'b>,
+                           target_module: Module<'b>,
                            import_directive: &ImportDirective,
                            lp: LastPrivate)
                            -> ResolveResult<()> {
@@ -791,54 +813,27 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                    *name,
                    module_to_string(module_));
 
-            if !target_import_resolution.is_public {
-                debug!("(resolving glob import) nevermind, just kidding");
-                continue;
-            }
-
             // Here we merge two import resolutions.
             let mut import_resolutions = module_.import_resolutions.borrow_mut();
-            match import_resolutions.get_mut(name) {
-                Some(dest_import_resolution) => {
-                    // Merge the two import resolutions at a finer-grained
-                    // level.
-
-                    match target_import_resolution.value_target {
-                        None => {
-                            // Continue.
-                        }
-                        Some(ref value_target) => {
-                            self.check_for_conflicting_import(&dest_import_resolution,
-                                                              import_directive.span,
-                                                              *name,
-                                                              ValueNS);
-                            dest_import_resolution.value_target = Some(value_target.clone());
-                        }
-                    }
-                    match target_import_resolution.type_target {
-                        None => {
-                            // Continue.
-                        }
-                        Some(ref type_target) => {
-                            self.check_for_conflicting_import(&dest_import_resolution,
-                                                              import_directive.span,
-                                                              *name,
-                                                              TypeNS);
-                            dest_import_resolution.type_target = Some(type_target.clone());
-                        }
+            let mut dest_import_resolution = import_resolutions.entry(*name).or_insert_with(|| {
+                ImportResolutionPerNamespace::new(id, is_public)
+            });
+
+            for &ns in [TypeNS, ValueNS].iter() {
+                match target_import_resolution[ns].target {
+                    Some(ref target) if target_import_resolution[ns].is_public => {
+                        self.check_for_conflicting_import(&dest_import_resolution,
+                                                          import_directive.span,
+                                                          *name,
+                                                          ns);
+                        dest_import_resolution[ns] = ImportResolution {
+                            id: id, is_public: is_public, target: Some(target.clone())
+                        };
+                        self.add_export(module_, *name, &dest_import_resolution[ns]);
                     }
-                    dest_import_resolution.is_public = is_public;
-                    continue;
+                    _ => {}
                 }
-                None => {}
             }
-
-            // Simple: just copy the old import resolution.
-            let mut new_import_resolution = ImportResolution::new(id, is_public);
-            new_import_resolution.value_target = target_import_resolution.value_target.clone();
-            new_import_resolution.type_target = target_import_resolution.type_target.clone();
-
-            import_resolutions.insert(*name, new_import_resolution);
         }
 
         // Add all children from the containing module.
@@ -846,7 +841,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
 
         for (&name, name_bindings) in target_module.children.borrow().iter() {
             self.merge_import_resolution(module_,
-                                         target_module.clone(),
+                                         target_module,
                                          import_directive,
                                          name,
                                          name_bindings.clone());
@@ -868,19 +863,18 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
     }
 
     fn merge_import_resolution(&mut self,
-                               module_: &Module,
-                               containing_module: Rc<Module>,
+                               module_: Module<'b>,
+                               containing_module: Module<'b>,
                                import_directive: &ImportDirective,
                                name: Name,
-                               name_bindings: NameBindings) {
+                               name_bindings: NameBindings<'b>) {
         let id = import_directive.id;
         let is_public = import_directive.is_public;
 
         let mut import_resolutions = module_.import_resolutions.borrow_mut();
-        let dest_import_resolution = import_resolutions.entry(name)
-                                                       .or_insert_with(|| {
-                                                           ImportResolution::new(id, is_public)
-                                                       });
+        let dest_import_resolution = import_resolutions.entry(name).or_insert_with(|| {
+            ImportResolutionPerNamespace::new(id, is_public)
+        });
 
         debug!("(resolving glob import) writing resolution `{}` in `{}` to `{}`",
                name,
@@ -888,10 +882,22 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                module_to_string(module_));
 
         // Merge the child item into the import resolution.
+        // pub_err makes sure we don't give the same error twice.
+        let mut pub_err = false;
         {
             let mut merge_child_item = |namespace| {
-                let modifier = DefModifiers::IMPORTABLE | DefModifiers::PUBLIC;
+                if !pub_err && is_public &&
+                        name_bindings[namespace].defined_with(DefModifiers::PRIVATE_VARIANT) {
+                    let msg = format!("variant `{}` is private, and cannot be reexported (error \
+                                       E0364), consider declaring its enum as `pub`", name);
+                    self.resolver.session.add_lint(lint::builtin::PRIVATE_IN_PUBLIC,
+                                                   import_directive.id,
+                                                   import_directive.span,
+                                                   msg);
+                    pub_err = true;
+                }
 
+                let modifier = DefModifiers::IMPORTABLE | DefModifiers::PUBLIC;
                 if name_bindings[namespace].defined_with(modifier) {
                     let namespace_name = match namespace {
                         TypeNS => "type",
@@ -909,37 +915,55 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                                   "{}",
                                   msg);
                     } else {
-                        let target = Target::new(containing_module.clone(),
-                                                 name_bindings[namespace].clone(),
-                                                 import_directive.shadowable);
-                        dest_import_resolution.set_target_and_id(namespace, Some(target), id);
+                        dest_import_resolution[namespace] = ImportResolution {
+                            target: Some(Target::new(containing_module,
+                                                     name_bindings[namespace].clone(),
+                                                     import_directive.shadowable)),
+                            id: id,
+                            is_public: is_public
+                        };
+                        self.add_export(module_, name, &dest_import_resolution[namespace]);
                     }
+                } else {
+                    // FIXME #30159: This is required for backwards compatability.
+                    dest_import_resolution[namespace].is_public |= is_public;
                 }
             };
             merge_child_item(ValueNS);
             merge_child_item(TypeNS);
         }
 
-        dest_import_resolution.is_public = is_public;
-
         self.check_for_conflicts_between_imports_and_items(module_,
                                                            dest_import_resolution,
                                                            import_directive.span,
                                                            name);
     }
 
+    fn add_export(&mut self, module: Module<'b>, name: Name, resolution: &ImportResolution<'b>) {
+        if !resolution.is_public { return }
+        let node_id = match module.def_id() {
+            Some(def_id) => self.resolver.ast_map.as_local_node_id(def_id).unwrap(),
+            None => return,
+        };
+        let export = match resolution.target.as_ref().unwrap().binding.def() {
+            Some(def) => Export { name: name, def_id: def.def_id() },
+            None => return,
+        };
+        self.resolver.export_map.entry(node_id).or_insert(Vec::new()).push(export);
+    }
+
     /// Checks that imported names and items don't have the same name.
     fn check_for_conflicting_import(&mut self,
-                                    import_resolution: &ImportResolution,
+                                    import_resolution: &ImportResolutionPerNamespace,
                                     import_span: Span,
                                     name: Name,
                                     namespace: Namespace) {
-        let target = import_resolution.target_for_namespace(namespace);
+        let target = &import_resolution[namespace].target;
         debug!("check_for_conflicting_import: {}; target exists: {}",
                name,
                target.is_some());
 
-        match target {
+        match *target {
             Some(ref target) if target.shadowable != Shadowable::Always => {
                 let ns_word = match namespace {
                     TypeNS => {
@@ -951,19 +975,20 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                     }
                     ValueNS => "value",
                 };
-                span_err!(self.resolver.session,
-                          import_span,
-                          E0252,
-                          "a {} named `{}` has already been imported in this module",
-                          ns_word,
-                          name);
-                let use_id = import_resolution.id(namespace);
+                let use_id = import_resolution[namespace].id;
                 let item = self.resolver.ast_map.expect_item(use_id);
-                // item is syntax::ast::Item;
-                span_note!(self.resolver.session,
+                let mut err = struct_span_err!(self.resolver.session,
+                                               import_span,
+                                               E0252,
+                                               "a {} named `{}` has already been imported \
+                                                in this module",
+                                               ns_word,
+                                               name);
+                span_note!(&mut err,
                            item.span,
                            "previous import of `{}` here",
                            name);
+                err.emit();
             }
             Some(_) | None => {}
         }
@@ -982,15 +1007,15 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
 
     /// Checks that imported names and items don't have the same name.
     fn check_for_conflicts_between_imports_and_items(&mut self,
-                                                     module: &Module,
-                                                     import_resolution: &ImportResolution,
+                                                     module: Module<'b>,
+                                                     import: &ImportResolutionPerNamespace<'b>,
                                                      import_span: Span,
                                                      name: Name) {
         // First, check for conflicts between imports and `extern crate`s.
         if module.external_module_children
                  .borrow()
                  .contains_key(&name) {
-            match import_resolution.type_target {
+            match import.type_ns.target {
                 Some(ref target) if target.shadowable != Shadowable::Always => {
                     let msg = format!("import `{0}` conflicts with imported crate in this module \
                                        (maybe you meant `use {0}::*`?)",
@@ -1002,8 +1027,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
         }
 
         // Check for item conflicts.
-        let children = module.children.borrow();
-        let name_bindings = match children.get(&name) {
+        let name_bindings = match module.children.borrow().get(&name) {
             None => {
                 // There can't be any conflicts.
                 return;
@@ -1011,23 +1035,25 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
             Some(ref name_bindings) => (*name_bindings).clone(),
         };
 
-        match import_resolution.value_target {
+        match import.value_ns.target {
             Some(ref target) if target.shadowable != Shadowable::Always => {
                 if let Some(ref value) = *name_bindings.value_ns.borrow() {
-                    span_err!(self.resolver.session,
-                              import_span,
-                              E0255,
-                              "import `{}` conflicts with value in this module",
-                              name);
+                    let mut err = struct_span_err!(self.resolver.session,
+                                                   import_span,
+                                                   E0255,
+                                                   "import `{}` conflicts with \
+                                                    value in this module",
+                                                   name);
                     if let Some(span) = value.span {
-                        self.resolver.session.span_note(span, "conflicting value here");
+                        err.span_note(span, "conflicting value here");
                     }
+                    err.emit();
                 }
             }
             Some(_) | None => {}
         }
 
-        match import_resolution.type_target {
+        match import.type_ns.target {
             Some(ref target) if target.shadowable != Shadowable::Always => {
                 if let Some(ref ty) = *name_bindings.type_ns.borrow() {
                     let (what, note) = match ty.module() {
@@ -1037,15 +1063,16 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                             ("trait in this module", "note conflicting trait here"),
                         _ => ("type in this module", "note conflicting type here"),
                     };
-                    span_err!(self.resolver.session,
-                              import_span,
-                              E0256,
-                              "import `{}` conflicts with {}",
-                              name,
-                              what);
+                    let mut err = struct_span_err!(self.resolver.session,
+                                                   import_span,
+                                                   E0256,
+                                                   "import `{}` conflicts with {}",
+                                                   name,
+                                                   what);
                     if let Some(span) = ty.span {
-                        self.resolver.session.span_note(span, note);
+                        err.span_note(span, note);
                     }
+                    err.emit();
                 }
             }
             Some(_) | None => {}
index f5431554a75642547f975b38cd8237908199ef9d..850608588234cf3ee7ccb9cc4587a5d51092cc1c 100644 (file)
@@ -406,11 +406,12 @@ impl<'a> ArchiveBuilder<'a> {
             Ok(prog) => {
                 let o = prog.wait_with_output().unwrap();
                 if !o.status.success() {
-                    sess.err(&format!("{:?} failed with: {}", cmd, o.status));
-                    sess.note(&format!("stdout ---\n{}",
-                                       str::from_utf8(&o.stdout).unwrap()));
-                    sess.note(&format!("stderr ---\n{}",
-                                       str::from_utf8(&o.stderr).unwrap()));
+                    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
index 975323375f18effc1e6bfe24af67b5a78021f4e6..ec1383f1f7b2b19ac8b40dc4db49d3e801096b13 100644 (file)
@@ -182,8 +182,10 @@ pub fn find_crate_name(sess: Option<&Session>,
     "rust_out".to_string()
 }
 
-pub fn build_link_meta(sess: &Session, krate: &hir::Crate,
-                       name: &str) -> LinkMeta {
+pub fn build_link_meta(sess: &Session,
+                       krate: &hir::Crate,
+                       name: &str)
+                       -> LinkMeta {
     let r = LinkMeta {
         crate_name: name.to_owned(),
         crate_hash: Svh::calculate(&sess.opts.cg.metadata, krate),
@@ -817,10 +819,10 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
     ab.build();
 
     if !all_native_libs.is_empty() {
-        sess.note("link against the following native artifacts when linking against \
-                  this static library");
-        sess.note("the order and any duplication can be significant on some platforms, \
-                  and so may need to be preserved");
+        sess.note_without_error("link against the following native artifacts when linking against \
+                                 this static library");
+        sess.note_without_error("the order and any duplication can be significant on some \
+                                 platforms, and so may need to be preserved");
     }
 
     for &(kind, ref lib) in &all_native_libs {
@@ -829,7 +831,7 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
             NativeLibraryKind::NativeUnknown => "library",
             NativeLibraryKind::NativeFramework => "framework",
         };
-        sess.note(&format!("{}: {}", name, *lib));
+        sess.note_without_error(&format!("{}: {}", name, *lib));
     }
 }
 
@@ -902,13 +904,14 @@ fn link_natively(sess: &Session, dylib: bool,
                     })
             }
             if !prog.status.success() {
-                sess.err(&format!("linking with `{}` failed: {}",
-                                 pname,
-                                 prog.status));
-                sess.note(&format!("{:?}", &cmd));
                 let mut output = prog.stderr.clone();
                 output.extend_from_slice(&prog.stdout);
-                sess.note(&*escape_string(&output[..]));
+                sess.struct_err(&format!("linking with `{}` failed: {}",
+                                         pname,
+                                         prog.status))
+                    .note(&format!("{:?}", &cmd))
+                    .note(&*escape_string(&output[..]))
+                    .emit();
                 sess.abort_if_errors();
             }
             info!("linker stderr:\n{}", escape_string(&prog.stderr[..]));
@@ -1054,6 +1057,7 @@ fn link_args(cmd: &mut Linker,
             out_filename: out_filename.to_path_buf(),
             has_rpath: sess.target.target.options.has_rpath,
             is_like_osx: sess.target.target.options.is_like_osx,
+            linker_is_gnu: sess.target.target.options.linker_is_gnu,
             get_install_prefix_lib_path: &mut get_install_prefix_lib_path,
         };
         cmd.args(&rpath::get_rpath_flags(&mut rpath_config));
index 1ee1c9f1912c778f85c441160dbecf831ea1e7e8..f585c65228a825116d0e402eeb7b42f97f74d0c5 100644 (file)
@@ -147,8 +147,8 @@ impl<'a> Linker for GnuLinker<'a> {
 
         // GNU-style linkers support optimization with -O. GNU ld doesn't
         // need a numeric argument, but other linkers do.
-        if self.sess.opts.optimize == config::Default ||
-           self.sess.opts.optimize == config::Aggressive {
+        if self.sess.opts.optimize == config::OptLevel::Default ||
+           self.sess.opts.optimize == config::OptLevel::Aggressive {
             self.cmd.arg("-Wl,-O1");
         }
     }
index ba6ec895a8e12e356d23f3094ad5e99a9cfb9ddc..85419a072503a59ccfeb260813d13352f8c99f5d 100644 (file)
@@ -29,8 +29,9 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
            name_extra: &str,
            output_names: &config::OutputFilenames) {
     if sess.opts.cg.prefer_dynamic {
-        sess.err("cannot prefer dynamic linking when performing LTO");
-        sess.note("only 'staticlib' and 'bin' outputs are supported with LTO");
+        sess.struct_err("cannot prefer dynamic linking when performing LTO")
+            .note("only 'staticlib' and 'bin' outputs are supported with LTO")
+            .emit();
         sess.abort_if_errors();
     }
 
@@ -101,7 +102,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
                 if !llvm::LLVMRustLinkInExternalBitcode(llmod,
                                                         ptr as *const libc::c_char,
                                                         bc_decoded.len() as libc::size_t) {
-                    write::llvm_err(sess.diagnostic().handler(),
+                    write::llvm_err(sess.diagnostic(),
                                     format!("failed to load bc of `{}`",
                                             &name[..]));
                 }
index 24179a0cccd226e30792fb01000a748299382d47..44b161a7575cc545ad5a5ab53c23af73ec7c9675 100644 (file)
@@ -14,7 +14,7 @@ use std::os::windows::prelude::*;
 use std::ptr;
 use libc::{c_void, c_long};
 
-type DWORD = u32;
+pub type DWORD = u32;
 type LPCWSTR = *const u16;
 type LONG = c_long;
 type LPDWORD = *mut DWORD;
@@ -34,7 +34,7 @@ const SYNCHRONIZE: REGSAM = 0x00100000;
 const REG_SZ: DWORD = 1;
 const ERROR_SUCCESS: i32 = 0;
 
-enum __HKEY__ {}
+pub enum __HKEY__ {}
 pub type HKEY = *mut __HKEY__;
 pub type PHKEY = *mut HKEY;
 pub type REGSAM = DWORD;
index c553843f858dc84eb1c87ce016ea5bf422aa8a8d..544df1798eaf93e48dc1a288c75ce74b087661a4 100644 (file)
@@ -20,8 +20,8 @@ use trans::{CrateTranslation, ModuleTranslation};
 use util::common::time;
 use util::common::path2cstr;
 use syntax::codemap;
-use syntax::diagnostic;
-use syntax::diagnostic::{Emitter, Handler, Level};
+use syntax::errors::{self, Handler, Level};
+use syntax::errors::emitter::Emitter;
 
 use std::collections::HashMap;
 use std::ffi::{CStr, CString};
@@ -34,7 +34,7 @@ use std::sync::mpsc::channel;
 use std::thread;
 use libc::{self, c_uint, c_int, c_void};
 
-pub fn llvm_err(handler: &diagnostic::Handler, msg: String) -> ! {
+pub fn llvm_err(handler: &errors::Handler, msg: String) -> ! {
     unsafe {
         let cstr = llvm::LLVMRustGetLastError();
         if cstr == ptr::null() {
@@ -49,7 +49,7 @@ pub fn llvm_err(handler: &diagnostic::Handler, msg: String) -> ! {
 }
 
 pub fn write_output_file(
-        handler: &diagnostic::Handler,
+        handler: &errors::Handler,
         target: llvm::TargetMachineRef,
         pm: llvm::PassManagerRef,
         m: ModuleRef,
@@ -109,9 +109,9 @@ impl SharedEmitter {
 }
 
 impl Emitter for SharedEmitter {
-    fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, codemap::Span)>,
+    fn emit(&mut self, sp: Option<codemap::Span>,
             msg: &str, code: Option<&str>, lvl: Level) {
-        assert!(cmsp.is_none(), "SharedEmitter doesn't support spans");
+        assert!(sp.is_none(), "SharedEmitter doesn't support spans");
 
         self.buffer.lock().unwrap().push(Diagnostic {
             msg: msg.to_string(),
@@ -120,8 +120,7 @@ impl Emitter for SharedEmitter {
         });
     }
 
-    fn custom_emit(&mut self, _cm: &codemap::CodeMap,
-                   _sp: diagnostic::RenderSpan, _msg: &str, _lvl: Level) {
+    fn custom_emit(&mut self, _sp: errors::RenderSpan, _msg: &str, _lvl: Level) {
         panic!("SharedEmitter doesn't support custom_emit");
     }
 }
@@ -145,10 +144,10 @@ fn target_feature(sess: &Session) -> String {
 
 fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel {
     match optimize {
-      config::No => llvm::CodeGenLevelNone,
-      config::Less => llvm::CodeGenLevelLess,
-      config::Default => llvm::CodeGenLevelDefault,
-      config::Aggressive => llvm::CodeGenLevelAggressive,
+      config::OptLevel::No => llvm::CodeGenLevelNone,
+      config::OptLevel::Less => llvm::CodeGenLevelLess,
+      config::OptLevel::Default => llvm::CodeGenLevelDefault,
+      config::OptLevel::Aggressive => llvm::CodeGenLevelAggressive,
     }
 }
 
@@ -226,7 +225,7 @@ pub fn create_target_machine(sess: &Session) -> TargetMachineRef {
     };
 
     if tm.is_null() {
-        llvm_err(sess.diagnostic().handler(),
+        llvm_err(sess.diagnostic(),
                  format!("Could not create LLVM TargetMachine for triple: {}",
                          triple).to_string());
     } else {
@@ -304,13 +303,13 @@ impl ModuleConfig {
         // slp vectorization at O3. Otherwise configure other optimization aspects
         // of this pass manager builder.
         self.vectorize_loop = !sess.opts.cg.no_vectorize_loops &&
-                             (sess.opts.optimize == config::Default ||
-                              sess.opts.optimize == config::Aggressive);
+                             (sess.opts.optimize == config::OptLevel::Default ||
+                              sess.opts.optimize == config::OptLevel::Aggressive);
         self.vectorize_slp = !sess.opts.cg.no_vectorize_slp &&
-                            sess.opts.optimize == config::Aggressive;
+                            sess.opts.optimize == config::OptLevel::Aggressive;
 
-        self.merge_functions = sess.opts.optimize == config::Default ||
-                               sess.opts.optimize == config::Aggressive;
+        self.merge_functions = sess.opts.optimize == config::OptLevel::Default ||
+                               sess.opts.optimize == config::OptLevel::Aggressive;
     }
 }
 
@@ -333,7 +332,7 @@ impl<'a> CodegenContext<'a> {
     fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> {
         CodegenContext {
             lto_ctxt: Some((sess, reachable)),
-            handler: sess.diagnostic().handler(),
+            handler: sess.diagnostic(),
             plugin_passes: sess.plugin_llvm_passes.borrow().clone(),
             remark: sess.opts.cg.remark.clone(),
             worker: 0,
@@ -360,8 +359,9 @@ unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext<'a>,
         }
 
         None => {
-            cgcx.handler.err(msg);
-            cgcx.handler.note("build without -C codegen-units for more exact errors");
+            cgcx.handler.struct_err(msg)
+                        .note("build without -C codegen-units for more exact errors")
+                        .emit();
         }
     }
 }
@@ -398,11 +398,11 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo
 
             if enabled {
                 let loc = llvm::debug_loc_to_string(llcx, opt.debug_loc);
-                cgcx.handler.note(&format!("optimization {} for {} at {}: {}",
-                                           opt.kind.describe(),
-                                           pass_name,
-                                           if loc.is_empty() { "[unknown]" } else { &*loc },
-                                           llvm::twine_to_string(opt.message)));
+                cgcx.handler.note_without_error(&format!("optimization {} for {} at {}: {}",
+                                                opt.kind.describe(),
+                                                pass_name,
+                                                if loc.is_empty() { "[unknown]" } else { &*loc },
+                                                llvm::twine_to_string(opt.message)));
             }
         }
 
@@ -545,10 +545,22 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
 
         if config.emit_asm {
             let path = output_names.with_extension(&format!("{}.s", name_extra));
+
+            // We can't use the same module for asm and binary output, because that triggers
+            // various errors like invalid IR or broken binaries, so we might have to clone the
+            // module to produce the asm output
+            let llmod = if config.emit_obj {
+                llvm::LLVMCloneModule(llmod)
+            } else {
+                llmod
+            };
             with_codegen(tm, llmod, config.no_builtins, |cpm| {
                 write_output_file(cgcx.handler, tm, cpm, llmod, &path,
                                   llvm::AssemblyFileType);
             });
+            if config.emit_obj {
+                llvm::LLVMDisposeModule(llmod);
+            }
         }
 
         if config.emit_obj {
@@ -863,7 +875,7 @@ fn run_work_multithreaded(sess: &Session,
         futures.push(rx);
 
         thread::Builder::new().name(format!("codegen-{}", i)).spawn(move || {
-            let diag_handler = Handler::with_emitter(true, box diag_emitter);
+            let diag_handler = Handler::with_emitter(true, false, box diag_emitter);
 
             // Must construct cgcx inside the proc because it has non-Send
             // fields.
@@ -903,7 +915,7 @@ fn run_work_multithreaded(sess: &Session,
             },
         }
         // Display any new diagnostics.
-        diag_emitter.dump(sess.diagnostic().handler());
+        diag_emitter.dump(sess.diagnostic());
     }
     if panicked {
         sess.fatal("aborting due to worker thread panic");
@@ -920,13 +932,15 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
     match cmd.output() {
         Ok(prog) => {
             if !prog.status.success() {
-                sess.err(&format!("linking with `{}` failed: {}",
-                                 pname,
-                                 prog.status));
-                sess.note(&format!("{:?}", &cmd));
                 let mut note = prog.stderr.clone();
                 note.extend_from_slice(&prog.stdout);
-                sess.note(str::from_utf8(&note[..]).unwrap());
+
+                sess.struct_err(&format!("linking with `{}` failed: {}",
+                                         pname,
+                                         prog.status))
+                    .note(&format!("{:?}", &cmd))
+                    .note(str::from_utf8(&note[..]).unwrap())
+                    .emit();
                 sess.abort_if_errors();
             }
         },
@@ -961,39 +975,7 @@ pub unsafe fn configure_llvm(sess: &Session) {
 
     llvm::LLVMInitializePasses();
 
-    // Only initialize the platforms supported by Rust here, because
-    // using --llvm-root will have multiple platforms that rustllvm
-    // doesn't actually link to and it's pointless to put target info
-    // into the registry that Rust cannot generate machine code for.
-    llvm::LLVMInitializeX86TargetInfo();
-    llvm::LLVMInitializeX86Target();
-    llvm::LLVMInitializeX86TargetMC();
-    llvm::LLVMInitializeX86AsmPrinter();
-    llvm::LLVMInitializeX86AsmParser();
-
-    llvm::LLVMInitializeARMTargetInfo();
-    llvm::LLVMInitializeARMTarget();
-    llvm::LLVMInitializeARMTargetMC();
-    llvm::LLVMInitializeARMAsmPrinter();
-    llvm::LLVMInitializeARMAsmParser();
-
-    llvm::LLVMInitializeAArch64TargetInfo();
-    llvm::LLVMInitializeAArch64Target();
-    llvm::LLVMInitializeAArch64TargetMC();
-    llvm::LLVMInitializeAArch64AsmPrinter();
-    llvm::LLVMInitializeAArch64AsmParser();
-
-    llvm::LLVMInitializeMipsTargetInfo();
-    llvm::LLVMInitializeMipsTarget();
-    llvm::LLVMInitializeMipsTargetMC();
-    llvm::LLVMInitializeMipsAsmPrinter();
-    llvm::LLVMInitializeMipsAsmParser();
-
-    llvm::LLVMInitializePowerPCTargetInfo();
-    llvm::LLVMInitializePowerPCTarget();
-    llvm::LLVMInitializePowerPCTargetMC();
-    llvm::LLVMInitializePowerPCAsmPrinter();
-    llvm::LLVMInitializePowerPCAsmParser();
+    llvm::initialize_available_targets();
 
     llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
                                  llvm_args.as_ptr());
index 83d0106fd4cb334a5216b867232483f42482bb3a..c1ab0284ade5235e226f30237eb8fc30652499eb 100644 (file)
 //!
 //! This API is completely unstable and subject to change.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_trans"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -32,7 +29,6 @@
 #![allow(unused_attributes)]
 #![feature(iter_arith)]
 #![feature(libc)]
-#![feature(path_relative_from)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(rustc_private)]
index 2964d87ec1c0987c16b165a46e2bc1c3f589ca64..c34013a7bbbb1b56d8955e44954e748ec32ba02d 100644 (file)
@@ -34,15 +34,13 @@ use session::Session;
 
 use middle::def;
 use middle::def_id::DefId;
-use middle::ty::{self, Ty};
+use middle::ty;
 
 use std::fs::File;
-use std::path::Path;
 
 use syntax::ast::{self, NodeId};
 use syntax::codemap::*;
 use syntax::parse::token::{self, keywords};
-use syntax::owned_slice::OwnedSlice;
 use syntax::visit::{self, Visitor};
 use syntax::print::pprust::{path_to_string, ty_to_string};
 use syntax::ptr::P;
@@ -274,9 +272,9 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> {
             def::DefSelfTy(..) |
             def::DefLabel(_) |
             def::DefTyParam(..) |
-            def::DefUse(_) |
             def::DefMethod(..) |
-            def::DefPrimTy(_) => {
+            def::DefPrimTy(_) |
+            def::DefErr => {
                 self.sess.span_bug(span,
                                    &format!("lookup_def_kind for unexpected item: {:?}", def));
             }
@@ -573,7 +571,7 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> {
     fn process_trait(&mut self,
                      item: &ast::Item,
                      generics: &ast::Generics,
-                     trait_refs: &OwnedSlice<ast::TyParamBound>,
+                     trait_refs: &ast::TyParamBounds,
                      methods: &[P<ast::TraitItem>]) {
         let qualname = format!("::{}", self.tcx.map.path_to_string(item.id));
         let val = self.span.snippet(item.span);
@@ -684,7 +682,7 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> {
             def::DefMethod(did) => {
                 let ti = self.tcx.impl_or_trait_item(did);
                 if let ty::MethodTraitItem(m) = ti {
-                    if m.explicit_self == ty::StaticExplicitSelfCategory {
+                    if m.explicit_self == ty::ExplicitSelfCategory::Static {
                         self.write_sub_path_trait_truncated(path);
                     }
                 }
index cc5322d7f9f46c04484caee61af584fae6606507..e1343c73acfa90cfbd0db544dd8b4bdbbecc2a51 100644 (file)
@@ -697,8 +697,8 @@ impl<'v> Visitor<'v> for PathCollector {
                     // Even if the ref is mut, you can't change the ref, only
                     // the data pointed at, so showing the initialising expression
                     // is still worthwhile.
-                    ast::BindByRef(_) => ast::MutImmutable,
-                    ast::BindByValue(mt) => mt,
+                    ast::BindingMode::ByRef(_) => ast::MutImmutable,
+                    ast::BindingMode::ByValue(mt) => mt,
                 };
                 // collect path for either visit_local or visit_arm
                 let path = ast_util::ident_to_path(path1.span, path1.node);
@@ -716,6 +716,8 @@ pub fn process_crate<'l, 'tcx>(tcx: &'l ty::ctxt<'tcx>,
                                analysis: &ty::CrateAnalysis,
                                cratename: &str,
                                odir: Option<&Path>) {
+    let _ignore = tcx.dep_graph.in_ignore();
+
     if generated_code(krate.span) {
         return;
     }
index 0e09a15492a891ae3bafb659f4f4d9601196c441..6c1a31738afb960d679513a13b14087c20a46671 100644 (file)
@@ -215,6 +215,7 @@ use trans::expr::{self, Dest};
 use trans::monomorphize;
 use trans::tvec;
 use trans::type_of;
+use trans::Disr;
 use middle::ty::{self, Ty};
 use session::config::NoDebugInfo;
 use util::common::indenter;
@@ -249,7 +250,7 @@ impl<'a> ConstantExpr<'a> {
 enum Opt<'a, 'tcx> {
     ConstantValue(ConstantExpr<'a>, DebugLoc),
     ConstantRange(ConstantExpr<'a>, ConstantExpr<'a>, DebugLoc),
-    Variant(ty::Disr, Rc<adt::Repr<'tcx>>, DefId, DebugLoc),
+    Variant(Disr, Rc<adt::Repr<'tcx>>, DefId, DebugLoc),
     SliceLengthEqual(usize, DebugLoc),
     SliceLengthGreaterOrEqual(/* prefix length */ usize,
                               /* suffix length */ usize,
@@ -670,7 +671,7 @@ fn get_branches<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 match opt_def {
                     Some(def::DefVariant(enum_id, var_id, _)) => {
                         let variant = tcx.lookup_adt_def(enum_id).variant_with_id(var_id);
-                        Variant(variant.disr_val,
+                        Variant(Disr::from(variant.disr_val),
                                 adt::represent_node(bcx, cur.id),
                                 var_id,
                                 debug_loc)
@@ -704,7 +705,7 @@ struct ExtractedBlock<'blk, 'tcx: 'blk> {
 
 fn extract_variant_args<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                     repr: &adt::Repr<'tcx>,
-                                    disr_val: ty::Disr,
+                                    disr_val: Disr,
                                     val: MatchInput)
                                     -> ExtractedBlock<'blk, 'tcx> {
     let _icx = push_ctxt("match::extract_variant_args");
@@ -1189,7 +1190,7 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     };
     let adt_vals = if any_irrefutable_adt_pat(bcx.tcx(), m, col) {
         let repr = adt::represent_type(bcx.ccx(), left_ty);
-        let arg_count = adt::num_args(&*repr, 0);
+        let arg_count = adt::num_args(&*repr, Disr(0));
         let (arg_count, struct_val) = if type_is_sized(bcx.tcx(), left_ty) {
             (arg_count, val.val)
         } else {
@@ -1201,7 +1202,7 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         };
         let mut field_vals: Vec<ValueRef> = (0..arg_count).map(|ix|
             // By definition, these are all sized
-            adt::trans_field_ptr(bcx, &*repr, adt::MaybeSizedValue::sized(struct_val), 0, ix)
+            adt::trans_field_ptr(bcx, &*repr, adt::MaybeSizedValue::sized(struct_val), Disr(0), ix)
         ).collect();
 
         match left_ty.sty {
@@ -1217,7 +1218,7 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                 let meta = Load(bcx, expr::get_meta(bcx, val.val));
                 let struct_val = adt::MaybeSizedValue::unsized_(struct_val, meta);
 
-                let data = adt::trans_field_ptr(bcx, &*repr, struct_val, 0, arg_count);
+                let data = adt::trans_field_ptr(bcx, &*repr, struct_val, Disr(0), arg_count);
                 Store(bcx, data, expr::get_dataptr(bcx, scratch));
                 Store(bcx, meta, expr::get_meta(bcx, scratch));
                 field_vals.push(scratch);
@@ -1760,6 +1761,9 @@ fn mk_binding_alloca<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>,
     let lvalue = Lvalue::new_with_hint(caller_name, bcx, p_id, HintKind::DontZeroJustUse);
     let datum = Datum::new(llval, var_ty, lvalue);
 
+    debug!("mk_binding_alloca cleanup_scope={:?} llval={} var_ty={:?}",
+           cleanup_scope, bcx.ccx().tn().val_to_string(llval), var_ty);
+
     // Subtle: be sure that we *populate* the memory *before*
     // we schedule the cleanup.
     call_lifetime_start(bcx, llval);
@@ -1852,7 +1856,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                     let vinfo = ccx.tcx().lookup_adt_def(enum_id).variant_with_id(var_id);
                     let args = extract_variant_args(bcx,
                                                     &*repr,
-                                                    vinfo.disr_val,
+                                                    Disr::from(vinfo.disr_val),
                                                     val);
                     if let Some(ref sub_pat) = *sub_pats {
                         for (i, &argval) in args.vals.iter().enumerate() {
@@ -1875,7 +1879,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                             let val = adt::MaybeSizedValue::sized(val.val);
                             for (i, elem) in elems.iter().enumerate() {
                                 let fldptr = adt::trans_field_ptr(bcx, &*repr,
-                                                                  val, 0, i);
+                                                                  val, Disr(0), i);
                                 bcx = bind_irrefutable_pat(
                                     bcx,
                                     &**elem,
@@ -1934,7 +1938,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             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, 0, i);
+                let fldptr = adt::trans_field_ptr(bcx, &*repr, val, Disr(0), i);
                 bcx = bind_irrefutable_pat(
                     bcx,
                     &**elem,
@@ -1944,8 +1948,16 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         }
         hir::PatBox(ref inner) => {
             let pat_ty = node_id_type(bcx, inner.id);
-            // Don't load DSTs, instead pass along a fat ptr
-            let val = if type_is_sized(tcx, pat_ty) {
+            // Pass along DSTs as fat pointers.
+            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 hir::PatIdent(hir::BindByRef(_),_,_) = inner.node {
+                    val.val
+                } else {
+                    Load(bcx, val.val)
+                }
+            } else if type_is_sized(tcx, pat_ty) {
                 Load(bcx, val.val)
             } else {
                 val.val
@@ -1955,8 +1967,16 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         }
         hir::PatRegion(ref inner, _) => {
             let pat_ty = node_id_type(bcx, inner.id);
-            // Don't load DSTs, instead pass along a fat ptr
-            let val = if type_is_sized(tcx, pat_ty) {
+            // Pass along DSTs as fat pointers.
+            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 hir::PatIdent(hir::BindByRef(_),_,_) = inner.node {
+                    val.val
+                } else {
+                    Load(bcx, val.val)
+                }
+            } else if type_is_sized(tcx, pat_ty) {
                 Load(bcx, val.val)
             } else {
                 val.val
index f6f1918fd074168ec94ea82befa3bddae3e59b1b..2c11aad8940441638d4b9a1e4c0515d4197fe501 100644 (file)
@@ -42,6 +42,7 @@
 //!   taken to it, implementing them for Rust seems difficult.
 
 pub use self::Repr::*;
+use super::Disr;
 
 use std;
 use std::rc::Rc;
@@ -50,11 +51,11 @@ use llvm::{ValueRef, True, IntEQ, IntNE};
 use back::abi::FAT_PTR_ADDR;
 use middle::subst;
 use middle::ty::{self, Ty};
-use middle::ty::Disr;
 use syntax::ast;
 use syntax::attr;
 use syntax::attr::IntType;
 use trans::_match;
+use trans::base::InitAlloca;
 use trans::build::*;
 use trans::cleanup;
 use trans::cleanup::CleanupMethods;
@@ -307,12 +308,12 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
             if !dtor && cases.iter().all(|c| c.tys.is_empty()) {
                 // All bodies empty -> intlike
-                let discrs: Vec<u64> = cases.iter().map(|c| c.discr).collect();
+                let discrs: Vec<_> = cases.iter().map(|c| Disr::from(c.discr)).collect();
                 let bounds = IntBounds {
-                    ulo: *discrs.iter().min().unwrap(),
-                    uhi: *discrs.iter().max().unwrap(),
-                    slo: discrs.iter().map(|n| *n as i64).min().unwrap(),
-                    shi: discrs.iter().map(|n| *n as i64).max().unwrap()
+                    ulo: discrs.iter().min().unwrap().0,
+                    uhi: discrs.iter().max().unwrap().0,
+                    slo: discrs.iter().map(|n| n.0 as i64).min().unwrap(),
+                    shi: discrs.iter().map(|n| n.0 as i64).max().unwrap()
                 };
                 return mk_cenum(cx, hint, &bounds);
             }
@@ -320,7 +321,7 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             // Since there's at least one
             // non-empty body, explicit discriminants should have
             // been rejected by a checker before this point.
-            if !cases.iter().enumerate().all(|(i,c)| c.discr == (i as Disr)) {
+            if !cases.iter().enumerate().all(|(i,c)| c.discr == Disr::from(i)) {
                 cx.sess().bug(&format!("non-C-like enum {} with specified \
                                         discriminants",
                                        cx.tcx().item_path_str(def.did)));
@@ -346,7 +347,7 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                         match cases[discr].find_ptr(cx) {
                             Some(ref df) if df.len() == 1 && st.fields.len() == 1 => {
                                 return RawNullablePointer {
-                                    nndiscr: discr as Disr,
+                                    nndiscr: Disr::from(discr),
                                     nnty: st.fields[0],
                                     nullfields: cases[1 - discr].tys.clone()
                                 };
@@ -355,7 +356,7 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                 discrfield.push(0);
                                 discrfield.reverse();
                                 return StructWrappedNullablePointer {
-                                    nndiscr: discr as Disr,
+                                    nndiscr: Disr::from(discr),
                                     nonnull: st,
                                     discrfield: discrfield,
                                     nullfields: cases[1 - discr].tys.clone()
@@ -563,7 +564,7 @@ fn get_cases<'tcx>(tcx: &ty::ctxt<'tcx>,
         let field_tys = vi.fields.iter().map(|field| {
             monomorphize::field_ty(tcx, substs, field)
         }).collect();
-        Case { discr: vi.disr_val, tys: field_tys }
+        Case { discr: Disr::from(vi.disr_val), tys: field_tys }
     }).collect()
 }
 
@@ -604,8 +605,8 @@ fn mk_cenum<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                       -> Repr<'tcx> {
     let it = range_to_inttype(cx, hint, bounds);
     match it {
-        attr::SignedInt(_) => CEnum(it, bounds.slo as Disr, bounds.shi as Disr),
-        attr::UnsignedInt(_) => CEnum(it, bounds.ulo, bounds.uhi)
+        attr::SignedInt(_) => CEnum(it, Disr(bounds.slo as u64), Disr(bounds.shi as u64)),
+        attr::UnsignedInt(_) => CEnum(it, Disr(bounds.ulo), Disr(bounds.uhi))
     }
 }
 
@@ -922,11 +923,11 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
         CEnum(ity, min, max) => load_discr(bcx, ity, scrutinee, min, max),
         General(ity, ref cases, _) => {
             let ptr = StructGEP(bcx, scrutinee, 0);
-            load_discr(bcx, ity, ptr, 0, (cases.len() - 1) as Disr)
+            load_discr(bcx, ity, ptr, Disr(0), Disr(cases.len() as u64 - 1))
         }
         Univariant(..) => C_u8(bcx.ccx(), 0),
         RawNullablePointer { nndiscr, nnty, .. } =>  {
-            let cmp = if nndiscr == 0 { IntEQ } else { IntNE };
+            let cmp = if nndiscr == Disr(0) { IntEQ } else { IntNE };
             let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty);
             ICmp(bcx, cmp, Load(bcx, scrutinee), C_null(llptrty), DebugLoc::None)
         }
@@ -944,7 +945,7 @@ fn struct_wrapped_nullable_bitdiscr(bcx: Block, nndiscr: Disr, discrfield: &Disc
                                     scrutinee: ValueRef) -> ValueRef {
     let llptrptr = GEPi(bcx, scrutinee, &discrfield[..]);
     let llptr = Load(bcx, llptrptr);
-    let cmp = if nndiscr == 0 { IntEQ } else { IntNE };
+    let cmp = if nndiscr == Disr(0) { IntEQ } else { IntNE };
     ICmp(bcx, cmp, llptr, C_null(val_ty(llptr)), DebugLoc::None)
 }
 
@@ -955,11 +956,11 @@ fn load_discr(bcx: Block, ity: IntType, ptr: ValueRef, min: Disr, max: Disr)
     assert_eq!(val_ty(ptr), llty.ptr_to());
     let bits = machine::llbitsize_of_real(bcx.ccx(), llty);
     assert!(bits <= 64);
-    let  bits = bits as usize;
-    let mask = (!0u64 >> (64 - bits)) as Disr;
+    let bits = bits as usize;
+    let mask = Disr(!0u64 >> (64 - bits));
     // For a (max) discr of -1, max will be `-1 as usize`, which overflows.
     // However, that is fine here (it would still represent the full range),
-    if (max.wrapping_add(1)) & mask == min & mask {
+    if max.wrapping_add(Disr(1)) & mask == min & mask {
         // i.e., if the range is everything.  The lo==hi case would be
         // rejected by the LLVM verifier (it would mean either an
         // empty set, which is impossible, or the entire range of the
@@ -968,7 +969,7 @@ fn load_discr(bcx: Block, ity: IntType, ptr: ValueRef, min: Disr, max: Disr)
     } else {
         // llvm::ConstantRange can deal with ranges that wrap around,
         // so an overflow on (max + 1) is fine.
-        LoadRangeAssert(bcx, ptr, min, (max.wrapping_add(1)), /* signed: */ True)
+        LoadRangeAssert(bcx, ptr, min.0, max.0.wrapping_add(1), /* signed: */ True)
     }
 }
 
@@ -980,18 +981,18 @@ pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr)
                               -> ValueRef {
     match *r {
         CEnum(ity, _, _) => {
-            C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true)
+            C_integral(ll_inttype(bcx.ccx(), ity), discr.0, true)
         }
         General(ity, _, _) => {
-            C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true)
+            C_integral(ll_inttype(bcx.ccx(), ity), discr.0, true)
         }
         Univariant(..) => {
             bcx.ccx().sess().bug("no cases for univariants or structs")
         }
         RawNullablePointer { .. } |
         StructWrappedNullablePointer { .. } => {
-            assert!(discr == 0 || discr == 1);
-            C_bool(bcx.ccx(), discr != 0)
+            assert!(discr == Disr(0) || discr == Disr(1));
+            C_bool(bcx.ccx(), discr != Disr(0))
         }
     }
 }
@@ -1003,20 +1004,20 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
     match *r {
         CEnum(ity, min, max) => {
             assert_discr_in_range(ity, min, max, discr);
-            Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true),
+            Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr.0, true),
                   val);
         }
         General(ity, ref cases, dtor) => {
             if dtor_active(dtor) {
                 let ptr = trans_field_ptr(bcx, r, MaybeSizedValue::sized(val), discr,
-                                          cases[discr as usize].fields.len() - 2);
+                                          cases[discr.0 as usize].fields.len() - 2);
                 Store(bcx, C_u8(bcx.ccx(), DTOR_NEEDED), ptr);
             }
-            Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true),
+            Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr.0, true),
                   StructGEP(bcx, val, 0));
         }
         Univariant(ref st, dtor) => {
-            assert_eq!(discr, 0);
+            assert_eq!(discr, Disr(0));
             if dtor_active(dtor) {
                 Store(bcx, C_u8(bcx.ccx(), DTOR_NEEDED),
                       StructGEP(bcx, val, st.fields.len() - 1));
@@ -1040,8 +1041,14 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
 
 fn assert_discr_in_range(ity: IntType, min: Disr, max: Disr, discr: Disr) {
     match ity {
-        attr::UnsignedInt(_) => assert!(min <= discr && discr <= max),
-        attr::SignedInt(_) => assert!(min as i64 <= discr as i64 && discr as i64 <= max as i64)
+        attr::UnsignedInt(_) => {
+            assert!(min <= discr);
+            assert!(discr <= max)
+        },
+        attr::SignedInt(_) => {
+            assert!(min.0 as i64 <= discr.0 as i64);
+            assert!(discr.0 as i64 <= max.0 as i64);
+        },
     }
 }
 
@@ -1051,11 +1058,11 @@ pub fn num_args(r: &Repr, discr: Disr) -> usize {
     match *r {
         CEnum(..) => 0,
         Univariant(ref st, dtor) => {
-            assert_eq!(discr, 0);
+            assert_eq!(discr, Disr(0));
             st.fields.len() - (if dtor_active(dtor) { 1 } else { 0 })
         }
         General(_, ref cases, dtor) => {
-            cases[discr as usize].fields.len() - 1 - (if dtor_active(dtor) { 1 } else { 0 })
+            cases[discr.0 as usize].fields.len() - 1 - (if dtor_active(dtor) { 1 } else { 0 })
         }
         RawNullablePointer { nndiscr, ref nullfields, .. } => {
             if discr == nndiscr { 1 } else { nullfields.len() }
@@ -1078,11 +1085,11 @@ pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
             bcx.ccx().sess().bug("element access in C-like enum")
         }
         Univariant(ref st, _dtor) => {
-            assert_eq!(discr, 0);
+            assert_eq!(discr, Disr(0));
             struct_field_ptr(bcx, st, val, ix, false)
         }
         General(_, ref cases, _) => {
-            struct_field_ptr(bcx, &cases[discr as usize], val, ix + 1, true)
+            struct_field_ptr(bcx, &cases[discr.0 as usize], val, ix + 1, true)
         }
         RawNullablePointer { nndiscr, ref nullfields, .. } |
         StructWrappedNullablePointer { nndiscr, ref nullfields, .. } if discr != nndiscr => {
@@ -1165,7 +1172,7 @@ pub fn struct_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, st: &Struct<'tcx>, v
 
     let meta = val.meta;
 
-    // Calculate the unaligned offset of the the unsized field.
+    // Calculate the unaligned offset of the unsized field.
     let mut offset = 0;
     for &ty in &st.fields[0..ix] {
         let llty = type_of::sizing_type_of(ccx, ty);
@@ -1279,7 +1286,12 @@ pub fn trans_drop_flag_ptr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             let custom_cleanup_scope = fcx.push_custom_cleanup_scope();
             let scratch = unpack_datum!(bcx, datum::lvalue_scratch_datum(
                 bcx, tcx.dtor_type(), "drop_flag",
-                cleanup::CustomScope(custom_cleanup_scope), (), |_, bcx, _| bcx
+                InitAlloca::Uninit("drop flag itself has no dtor"),
+                cleanup::CustomScope(custom_cleanup_scope), (), |_, bcx, _| {
+                    debug!("no-op populate call for trans_drop_flag_ptr on dtor_type={:?}",
+                           tcx.dtor_type());
+                    bcx
+                }
             ));
             bcx = fold_variants(bcx, r, val, |variant_cx, st, value| {
                 let ptr = struct_field_ptr(variant_cx, st, MaybeSizedValue::sized(value),
@@ -1320,12 +1332,12 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, discr
         CEnum(ity, min, max) => {
             assert_eq!(vals.len(), 0);
             assert_discr_in_range(ity, min, max, discr);
-            C_integral(ll_inttype(ccx, ity), discr as u64, true)
+            C_integral(ll_inttype(ccx, ity), discr.0, true)
         }
         General(ity, ref cases, _) => {
-            let case = &cases[discr as usize];
+            let case = &cases[discr.0 as usize];
             let (max_sz, _) = union_size_and_align(&cases[..]);
-            let lldiscr = C_integral(ll_inttype(ccx, ity), discr as u64, true);
+            let lldiscr = C_integral(ll_inttype(ccx, ity), discr.0 as u64, true);
             let mut f = vec![lldiscr];
             f.extend_from_slice(vals);
             let mut contents = build_const_struct(ccx, case, &f[..]);
@@ -1333,7 +1345,7 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, discr
             C_struct(ccx, &contents[..], false)
         }
         Univariant(ref st, _dro) => {
-            assert!(discr == 0);
+            assert_eq!(discr, Disr(0));
             let contents = build_const_struct(ccx, st, vals);
             C_struct(ccx, &contents[..], st.packed)
         }
@@ -1438,17 +1450,17 @@ pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef) -> Disr {
     match *r {
         CEnum(ity, _, _) => {
             match ity {
-                attr::SignedInt(..) => const_to_int(val) as Disr,
-                attr::UnsignedInt(..) => const_to_uint(val) as Disr
+                attr::SignedInt(..) => Disr(const_to_int(val) as u64),
+                attr::UnsignedInt(..) => Disr(const_to_uint(val)),
             }
         }
         General(ity, _, _) => {
             match ity {
-                attr::SignedInt(..) => const_to_int(const_get_elt(ccx, val, &[0])) as Disr,
-                attr::UnsignedInt(..) => const_to_uint(const_get_elt(ccx, val, &[0])) as Disr
+                attr::SignedInt(..) => Disr(const_to_int(const_get_elt(ccx, val, &[0])) as u64),
+                attr::UnsignedInt(..) => Disr(const_to_uint(const_get_elt(ccx, val, &[0])))
             }
         }
-        Univariant(..) => 0,
+        Univariant(..) => Disr(0),
         RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => {
             ccx.sess().bug("const discrim access of non c-like enum")
         }
index 1de94bed3ddf723e268fd4c962b170e4fce7ed32..69a8a84229d4740c9d6f0f01d965fa88237a10f2 100644 (file)
@@ -39,27 +39,39 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm)
     let mut ext_constraints = Vec::new();
 
     // Prepare the output operands
-    let outputs = ia.outputs.iter().enumerate().map(|(i, &(ref c, ref out, is_rw))| {
-        constraints.push((*c).clone());
+    let mut outputs = Vec::new();
+    let mut inputs = Vec::new();
+    for (i, out) in ia.outputs.iter().enumerate() {
+        constraints.push(out.constraint.clone());
 
-        let out_datum = unpack_datum!(bcx, expr::trans(bcx, &**out));
-        output_types.push(type_of::type_of(bcx.ccx(), out_datum.ty));
-        let val = out_datum.val;
-        if is_rw {
+        let out_datum = unpack_datum!(bcx, expr::trans(bcx, &*out.expr));
+        if out.is_indirect {
             bcx = callee::trans_arg_datum(bcx,
-                                          expr_ty(bcx, &**out),
+                                          expr_ty(bcx, &*out.expr),
                                           out_datum,
                                           cleanup::CustomScope(temp_scope),
                                           callee::DontAutorefArg,
-                                          &mut ext_inputs);
-            ext_constraints.push(i.to_string());
+                                          &mut inputs);
+            if out.is_rw {
+                ext_inputs.push(*inputs.last().unwrap());
+                ext_constraints.push(i.to_string());
+            }
+        } else {
+            output_types.push(type_of::type_of(bcx.ccx(), out_datum.ty));
+            outputs.push(out_datum.val);
+            if out.is_rw {
+                bcx = callee::trans_arg_datum(bcx,
+                                              expr_ty(bcx, &*out.expr),
+                                              out_datum,
+                                              cleanup::CustomScope(temp_scope),
+                                              callee::DontAutorefArg,
+                                              &mut ext_inputs);
+                ext_constraints.push(i.to_string());
+            }
         }
-        val
-
-    }).collect::<Vec<_>>();
+    }
 
     // Now the input operands
-    let mut inputs = Vec::new();
     for &(ref c, ref input) in &ia.inputs {
         constraints.push((*c).clone());
 
diff --git a/src/librustc_trans/trans/assert_dep_graph.rs b/src/librustc_trans/trans/assert_dep_graph.rs
new file mode 100644 (file)
index 0000000..3d6a6a8
--- /dev/null
@@ -0,0 +1,430 @@
+// 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.
+
+//! This pass is only used for the UNIT TESTS and DEBUGGING NEEDS
+//! around dependency graph construction. It serves two purposes; it
+//! will dump graphs in graphviz form to disk, and it searches for
+//! `#[rustc_if_this_changed]` and `#[rustc_then_this_would_need]`
+//! annotations. These annotations can be used to test whether paths
+//! exist in the graph. We report errors on each
+//! `rustc_if_this_changed` annotation. If a path exists in all
+//! cases, then we would report "all path(s) exist". Otherwise, we
+//! report: "no path to `foo`" for each case where no path exists.
+//! `compile-fail` tests can then be used to check when paths exist or
+//! do not.
+//!
+//! The full form of the `rustc_if_this_changed` annotation is
+//! `#[rustc_if_this_changed(id)]`. The `"id"` is optional and
+//! defaults to `"id"` if omitted.
+//!
+//! Example:
+//!
+//! ```
+//! #[rustc_if_this_changed]
+//! fn foo() { }
+//!
+//! #[rustc_then_this_would_need("trans")] //~ ERROR no path from `foo`
+//! fn bar() { }
+//!
+//! #[rustc_then_this_would_need("trans")] //~ ERROR OK
+//! fn baz() { foo(); }
+//! ```
+
+use graphviz as dot;
+use rustc::dep_graph::{DepGraphQuery, DepNode};
+use rustc::middle::def_id::DefId;
+use rustc::middle::ty;
+use rustc_data_structures::fnv::{FnvHashMap, FnvHashSet};
+use rustc_data_structures::graph::{Direction, INCOMING, OUTGOING, NodeIndex};
+use rustc_front::hir;
+use rustc_front::intravisit::Visitor;
+use graphviz::IntoCow;
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use syntax::ast;
+use syntax::attr::AttrMetaMethods;
+use syntax::codemap::Span;
+use syntax::parse::token::InternedString;
+
+const IF_THIS_CHANGED: &'static str = "rustc_if_this_changed";
+const THEN_THIS_WOULD_NEED: &'static str = "rustc_then_this_would_need";
+const ID: &'static str = "id";
+
+pub fn assert_dep_graph(tcx: &ty::ctxt) {
+    let _ignore = tcx.dep_graph.in_ignore();
+
+    if tcx.sess.opts.dump_dep_graph {
+        dump_graph(tcx);
+    }
+
+    // Find annotations supplied by user (if any).
+    let (if_this_changed, then_this_would_need) = {
+        let mut visitor = IfThisChanged { tcx: tcx,
+                                          if_this_changed: FnvHashMap(),
+                                          then_this_would_need: FnvHashMap() };
+        tcx.map.krate().visit_all_items(&mut visitor);
+        (visitor.if_this_changed, visitor.then_this_would_need)
+    };
+
+    // Check paths.
+    check_paths(tcx, &if_this_changed, &then_this_would_need);
+}
+
+type SourceHashMap = FnvHashMap<InternedString,
+                                FnvHashSet<(Span, DefId, DepNode)>>;
+type TargetHashMap = FnvHashMap<InternedString,
+                                FnvHashSet<(Span, InternedString, ast::NodeId, DepNode)>>;
+
+struct IfThisChanged<'a, 'tcx:'a> {
+    tcx: &'a ty::ctxt<'tcx>,
+    if_this_changed: SourceHashMap,
+    then_this_would_need: TargetHashMap,
+}
+
+impl<'a, 'tcx> IfThisChanged<'a, 'tcx> {
+    fn process_attrs(&mut self, node_id: ast::NodeId, def_id: DefId) {
+        for attr in self.tcx.get_attrs(def_id).iter() {
+            if attr.check_name(IF_THIS_CHANGED) {
+                let mut id = None;
+                for meta_item in attr.meta_item_list().unwrap_or_default() {
+                    match meta_item.node {
+                        ast::MetaWord(ref s) if id.is_none() => id = Some(s.clone()),
+                        _ => {
+                            self.tcx.sess.span_err(
+                                meta_item.span,
+                                &format!("unexpected meta-item {:?}", meta_item.node));
+                        }
+                    }
+                }
+                let id = id.unwrap_or(InternedString::new(ID));
+                self.if_this_changed.entry(id)
+                                    .or_insert(FnvHashSet())
+                                    .insert((attr.span, def_id, DepNode::Hir(def_id)));
+            } else if attr.check_name(THEN_THIS_WOULD_NEED) {
+                let mut dep_node_interned = None;
+                let mut id = None;
+                for meta_item in attr.meta_item_list().unwrap_or_default() {
+                    match meta_item.node {
+                        ast::MetaWord(ref s) if dep_node_interned.is_none() =>
+                            dep_node_interned = Some(s.clone()),
+                        ast::MetaWord(ref s) if id.is_none() =>
+                            id = Some(s.clone()),
+                        _ => {
+                            self.tcx.sess.span_err(
+                                meta_item.span,
+                                &format!("unexpected meta-item {:?}", meta_item.node));
+                        }
+                    }
+                }
+                let dep_node_str = dep_node_interned.as_ref().map(|s| &**s);
+                macro_rules! match_depnode_name {
+                    ($input:expr, $def_id:expr, match { $($variant:ident,)* } else $y:expr) => {
+                        match $input {
+                            $(Some(stringify!($variant)) => DepNode::$variant($def_id),)*
+                            _ => $y
+                        }
+                    }
+                }
+                let dep_node = match_depnode_name! {
+                    dep_node_str, def_id, match {
+                        CollectItem,
+                        BorrowCheck,
+                        TransCrateItem,
+                        TypeckItemType,
+                        TypeckItemBody,
+                        ImplOrTraitItems,
+                        ItemSignature,
+                        FieldTy,
+                        TraitItemDefIds,
+                        InherentImpls,
+                        ImplItems,
+                        TraitImpls,
+                        ReprHints,
+                    } else {
+                        self.tcx.sess.span_fatal(
+                            attr.span,
+                            &format!("unrecognized DepNode variant {:?}", dep_node_str));
+                    }
+                };
+                let id = id.unwrap_or(InternedString::new(ID));
+                self.then_this_would_need
+                    .entry(id)
+                    .or_insert(FnvHashSet())
+                    .insert((attr.span, dep_node_interned.clone().unwrap(), node_id, dep_node));
+            }
+        }
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for IfThisChanged<'a, 'tcx> {
+    fn visit_item(&mut self, item: &'tcx hir::Item) {
+        let def_id = self.tcx.map.local_def_id(item.id);
+        self.process_attrs(item.id, def_id);
+    }
+}
+
+fn check_paths(tcx: &ty::ctxt,
+               if_this_changed: &SourceHashMap,
+               then_this_would_need: &TargetHashMap)
+{
+    // Return early here so as not to construct the query, which is not cheap.
+    if if_this_changed.is_empty() {
+        return;
+    }
+    let query = tcx.dep_graph.query();
+    for (id, sources) in if_this_changed {
+        let targets = match then_this_would_need.get(id) {
+            Some(targets) => targets,
+            None => {
+                for &(source_span, _, _) in sources.iter().take(1) {
+                    tcx.sess.span_err(
+                        source_span,
+                        &format!("no targets for id `{}`", id));
+                }
+                continue;
+            }
+        };
+
+        for &(_, source_def_id, source_dep_node) in sources {
+            let dependents = query.dependents(source_dep_node);
+            for &(target_span, ref target_pass, _, ref target_dep_node) in targets {
+                if !dependents.contains(&target_dep_node) {
+                    tcx.sess.span_err(
+                        target_span,
+                        &format!("no path from `{}` to `{}`",
+                                 tcx.item_path_str(source_def_id),
+                                 target_pass));
+                } else {
+                    tcx.sess.span_err(
+                        target_span,
+                        &format!("OK"));
+                }
+            }
+        }
+    }
+}
+
+fn dump_graph(tcx: &ty::ctxt) {
+    let path: String = env::var("RUST_DEP_GRAPH").unwrap_or_else(|_| format!("dep_graph"));
+    let query = tcx.dep_graph.query();
+
+    let nodes = match env::var("RUST_DEP_GRAPH_FILTER") {
+        Ok(string) => {
+            // Expect one of: "-> target", "source -> target", or "source ->".
+            let parts: Vec<_> = string.split("->").collect();
+            if parts.len() > 2 {
+                panic!("Invalid RUST_DEP_GRAPH_FILTER: expected '[source] -> [target]'");
+            }
+            let sources = node_set(&query, &parts[0]);
+            let targets = node_set(&query, &parts[1]);
+            filter_nodes(&query, &sources, &targets)
+        }
+        Err(_) => {
+            query.nodes()
+                 .into_iter()
+                 .collect()
+        }
+    };
+    let edges = filter_edges(&query, &nodes);
+
+    { // 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 {
+            write!(file, "{:?} -> {:?}\n", source, target).unwrap();
+        }
+    }
+
+    { // dump a .dot file in graphviz format:
+        let dot_path = format!("{}.dot", path);
+        let mut v = Vec::new();
+        dot::render(&GraphvizDepGraph(nodes, edges), &mut v).unwrap();
+        File::create(&dot_path).and_then(|mut f| f.write_all(&v)).unwrap();
+    }
+}
+
+pub struct GraphvizDepGraph(FnvHashSet<DepNode>, Vec<(DepNode, DepNode)>);
+
+impl<'a, 'tcx> dot::GraphWalk<'a, DepNode, (DepNode, DepNode)> for GraphvizDepGraph {
+    fn nodes(&self) -> dot::Nodes<DepNode> {
+        let nodes: Vec<_> = self.0.iter().cloned().collect();
+        nodes.into_cow()
+    }
+    fn edges(&self) -> dot::Edges<(DepNode, DepNode)> {
+        self.1[..].into_cow()
+    }
+    fn source(&self, edge: &(DepNode, DepNode)) -> DepNode {
+        edge.0
+    }
+    fn target(&self, edge: &(DepNode, DepNode)) -> DepNode {
+        edge.1
+    }
+}
+
+impl<'a, 'tcx> dot::Labeller<'a, DepNode, (DepNode, DepNode)> for GraphvizDepGraph {
+    fn graph_id(&self) -> dot::Id {
+        dot::Id::new("DependencyGraph").unwrap()
+    }
+    fn node_id(&self, n: &DepNode) -> dot::Id {
+        let s: String =
+            format!("{:?}", n).chars()
+                              .map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' })
+                              .collect();
+        debug!("n={:?} s={:?}", n, s);
+        dot::Id::new(s).unwrap()
+    }
+    fn node_label(&self, n: &DepNode) -> dot::LabelText {
+        dot::LabelText::label(format!("{:?}", n))
+    }
+}
+
+// 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, filter: &str) -> Option<FnvHashSet<DepNode>> {
+    debug!("node_set(filter={:?})", filter);
+
+    if filter.trim().is_empty() {
+        return None;
+    }
+
+    let filters: Vec<&str> = filter.split("&").map(|s| s.trim()).collect();
+
+    debug!("node_set: filters={:?}", filters);
+
+    Some(query.nodes()
+         .into_iter()
+         .filter(|n| {
+             let s = format!("{:?}", n);
+             filters.iter().all(|f| s.contains(f))
+         })
+        .collect())
+}
+
+fn filter_nodes(query: &DepGraphQuery,
+                sources: &Option<FnvHashSet<DepNode>>,
+                targets: &Option<FnvHashSet<DepNode>>)
+                -> FnvHashSet<DepNode>
+{
+    if let &Some(ref sources) = sources {
+        if let &Some(ref targets) = targets {
+            walk_between(query, sources, targets)
+        } else {
+            walk_nodes(query, sources, OUTGOING)
+        }
+    } else if let &Some(ref targets) = targets {
+        walk_nodes(query, targets, INCOMING)
+    } else {
+        query.nodes().into_iter().collect()
+    }
+}
+
+fn walk_nodes(query: &DepGraphQuery,
+              starts: &FnvHashSet<DepNode>,
+              direction: Direction)
+              -> FnvHashSet<DepNode>
+{
+    let mut set = FnvHashSet();
+    for start in starts {
+        debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING);
+        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) {
+                        stack.push(neighbor_index);
+                    }
+                }
+            }
+        }
+    }
+    set
+}
+
+fn walk_between(query: &DepGraphQuery,
+                sources: &FnvHashSet<DepNode>,
+                targets: &FnvHashSet<DepNode>)
+                -> FnvHashSet<DepNode>
+{
+    // 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
+    // have to be careful about cycles etc.  Luckily efficiency is not
+    // a big concern!
+
+    #[derive(Copy, Clone, PartialEq)]
+    enum State { Undecided, Deciding, Included, Excluded }
+
+    let mut node_states = vec![State::Undecided; query.graph.len_nodes()];
+
+    for &target in targets {
+        node_states[query.indices[&target].0] = State::Included;
+    }
+
+    for source in sources.iter().map(|n| query.indices[n]) {
+        recurse(query, &mut node_states, source);
+    }
+
+    return query.nodes()
+                .into_iter()
+                .filter(|n| {
+                    let index = query.indices[n];
+                    node_states[index.0] == State::Included
+                })
+                .collect();
+
+    fn recurse(query: &DepGraphQuery,
+               node_states: &mut [State],
+               node: NodeIndex)
+               -> bool
+    {
+        match node_states[node.0] {
+            // known to reach a target
+            State::Included => return true,
+
+            // known not to reach a target
+            State::Excluded => return false,
+
+            // backedge, not yet known, say false
+            State::Deciding => return false,
+
+            State::Undecided => { }
+        }
+
+        node_states[node.0] = State::Deciding;
+
+        for neighbor_index in query.graph.successor_nodes(node) {
+            if recurse(query, node_states, neighbor_index) {
+                node_states[node.0] = State::Included;
+            }
+        }
+
+        // if we didn't find a path to target, then set to excluded
+        if node_states[node.0] == State::Deciding {
+            node_states[node.0] = State::Excluded;
+            false
+        } else {
+            assert!(node_states[node.0] == State::Included);
+            true
+        }
+    }
+}
+
+fn filter_edges(query: &DepGraphQuery,
+                nodes: &FnvHashSet<DepNode>)
+                -> Vec<(DepNode, DepNode)>
+{
+    query.edges()
+         .into_iter()
+         .filter(|&(source, target)| nodes.contains(&source) && nodes.contains(&target))
+         .collect()
+}
index 971eb6bc5743ad3aa4de499f49e653e5f6b572f7..4c619f895de56a987bf8d9a6e8829ae93503e2be 100644 (file)
@@ -42,13 +42,16 @@ use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
 use middle::weak_lang_items;
 use middle::pat_util::simple_name;
 use middle::subst::Substs;
-use middle::ty::{self, Ty, HasTypeFlags};
+use middle::ty::{self, Ty, TypeFoldable};
+use rustc::dep_graph::DepNode;
 use rustc::front::map as hir_map;
+use rustc::util::common::time;
 use rustc_mir::mir_map::MirMap;
 use session::config::{self, NoDebugInfo, FullDebugInfo};
 use session::Session;
 use trans::_match;
 use trans::adt;
+use trans::assert_dep_graph;
 use trans::attributes;
 use trans::build::*;
 use trans::builder::{Builder, noname};
@@ -82,6 +85,7 @@ use trans::type_::Type;
 use trans::type_of;
 use trans::type_of::*;
 use trans::value::Value;
+use trans::Disr;
 use util::common::indenter;
 use util::sha2::Sha256;
 use util::nodemap::{NodeMap, NodeSet};
@@ -486,7 +490,7 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
         for (i, field) in variant.fields.iter().enumerate() {
             let arg = monomorphize::field_ty(tcx, substs, field);
             cx = f(cx,
-                   adt::trans_field_ptr(cx, repr, av, variant.disr_val, i),
+                   adt::trans_field_ptr(cx, repr, av, Disr::from(variant.disr_val), i),
                    arg);
         }
         return cx;
@@ -506,7 +510,7 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
             let repr = adt::represent_type(cx.ccx(), t);
             let VariantInfo { fields, discr } = VariantInfo::from_ty(cx.tcx(), t, None);
             for (i, &Field(_, field_ty)) in fields.iter().enumerate() {
-                let llfld_a = adt::trans_field_ptr(cx, &*repr, value, discr, i);
+                let llfld_a = adt::trans_field_ptr(cx, &*repr, value, Disr::from(discr), i);
 
                 let val = if common::type_is_sized(cx.tcx(), field_ty) {
                     llfld_a
@@ -522,7 +526,7 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
         ty::TyClosure(_, ref substs) => {
             let repr = adt::represent_type(cx.ccx(), t);
             for (i, upvar_ty) in substs.upvar_tys.iter().enumerate() {
-                let llupvar = adt::trans_field_ptr(cx, &*repr, value, 0, i);
+                let llupvar = adt::trans_field_ptr(cx, &*repr, value, Disr(0), i);
                 cx = f(cx, llupvar, upvar_ty);
             }
         }
@@ -538,7 +542,7 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
         ty::TyTuple(ref args) => {
             let repr = adt::represent_type(cx.ccx(), t);
             for (i, arg) in args.iter().enumerate() {
-                let llfld_a = adt::trans_field_ptr(cx, &*repr, value, 0, i);
+                let llfld_a = adt::trans_field_ptr(cx, &*repr, value, Disr(0), i);
                 cx = f(cx, llfld_a, *arg);
             }
         }
@@ -585,7 +589,7 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
                         let variant_cx = fcx.new_temp_block(&format!("enum-iter-variant-{}",
                                                                      &variant.disr_val
                                                                              .to_string()));
-                        let case_val = adt::trans_case(cx, &*repr, variant.disr_val);
+                        let case_val = adt::trans_case(cx, &*repr, Disr::from(variant.disr_val));
                         AddCase(llswitch, case_val, variant_cx.llbb);
                         let variant_cx = iter_variant(variant_cx,
                                                       &*repr,
@@ -717,8 +721,8 @@ pub fn coerce_unsized_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                     continue;
                 }
 
-                let src_f = adt::trans_field_ptr(bcx, &src_repr, src, 0, i);
-                let dst_f = adt::trans_field_ptr(bcx, &dst_repr, dst, 0, i);
+                let src_f = adt::trans_field_ptr(bcx, &src_repr, src, Disr(0), i);
+                let dst_f = adt::trans_field_ptr(bcx, &dst_repr, dst, Disr(0), i);
                 if src_fty == dst_fty {
                     memcpy_ty(bcx, dst_f, src_f, src_fty);
                 } else {
@@ -957,22 +961,28 @@ pub fn wants_msvc_seh(sess: &Session) -> bool {
     sess.target.target.options.is_like_msvc && sess.target.target.arch == "x86"
 }
 
-pub fn need_invoke(bcx: Block) -> bool {
+pub fn avoid_invoke(bcx: Block) -> bool {
     // FIXME(#25869) currently SEH-based unwinding is pretty buggy in LLVM and
     //               is being overhauled as this is being written. Until that
     //               time such that upstream LLVM's implementation is more solid
     //               and we start binding it we need to skip invokes for any
     //               target which wants SEH-based unwinding.
     if bcx.sess().no_landing_pads() || wants_msvc_seh(bcx.sess()) {
-        return false;
+        true
+    } else if bcx.is_lpad {
+        // Avoid using invoke if we are already inside a landing pad.
+        true
+    } else {
+        false
     }
+}
 
-    // Avoid using invoke if we are already inside a landing pad.
-    if bcx.is_lpad {
-        return false;
+pub fn need_invoke(bcx: Block) -> bool {
+    if avoid_invoke(bcx) {
+        false
+    } else {
+        bcx.fcx.needs_invoke()
     }
-
-    bcx.fcx.needs_invoke()
 }
 
 pub fn load_if_immediate<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, t: Ty<'tcx>) -> ValueRef {
@@ -1138,48 +1148,63 @@ pub fn with_cond<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, val: ValueRef, f: F) ->
     next_cx
 }
 
-pub fn call_lifetime_start(cx: Block, ptr: ValueRef) {
-    if cx.sess().opts.optimize == config::No {
+enum Lifetime { Start, End }
+
+// If LLVM lifetime intrinsic support is enabled (i.e. optimizations
+// on), and `ptr` is nonzero-sized, then extracts the size of `ptr`
+// and the intrinsic for `lt` and passes them to `emit`, which is in
+// charge of generating code to call the passed intrinsic on whatever
+// block of generated code is targetted for the intrinsic.
+//
+// If LLVM lifetime intrinsic support is disabled (i.e.  optimizations
+// off) or `ptr` is zero-sized, then no-op (does not call `emit`).
+fn core_lifetime_emit<'blk, 'tcx, F>(ccx: &'blk CrateContext<'blk, 'tcx>,
+                                     ptr: ValueRef,
+                                     lt: Lifetime,
+                                     emit: F)
+    where F: FnOnce(&'blk CrateContext<'blk, 'tcx>, machine::llsize, ValueRef)
+{
+    if ccx.sess().opts.optimize == config::OptLevel::No {
         return;
     }
 
-    let _icx = push_ctxt("lifetime_start");
-    let ccx = cx.ccx();
+    let _icx = push_ctxt(match lt {
+        Lifetime::Start => "lifetime_start",
+        Lifetime::End => "lifetime_end"
+    });
 
     let size = machine::llsize_of_alloc(ccx, val_ty(ptr).element_type());
     if size == 0 {
         return;
     }
 
-    let ptr = PointerCast(cx, ptr, Type::i8p(ccx));
-    let lifetime_start = ccx.get_intrinsic(&"llvm.lifetime.start");
-    Call(cx,
-         lifetime_start,
-         &[C_u64(ccx, size), ptr],
-         None,
-         DebugLoc::None);
+    let lifetime_intrinsic = ccx.get_intrinsic(match lt {
+        Lifetime::Start => "llvm.lifetime.start",
+        Lifetime::End => "llvm.lifetime.end"
+    });
+    emit(ccx, size, lifetime_intrinsic)
 }
 
-pub fn call_lifetime_end(cx: Block, ptr: ValueRef) {
-    if cx.sess().opts.optimize == config::No {
-        return;
-    }
-
-    let _icx = push_ctxt("lifetime_end");
-    let ccx = cx.ccx();
-
-    let size = machine::llsize_of_alloc(ccx, val_ty(ptr).element_type());
-    if size == 0 {
-        return;
-    }
+pub fn call_lifetime_start(cx: Block, ptr: ValueRef) {
+    core_lifetime_emit(cx.ccx(), ptr, Lifetime::Start, |ccx, size, lifetime_start| {
+        let ptr = PointerCast(cx, ptr, Type::i8p(ccx));
+        Call(cx,
+             lifetime_start,
+             &[C_u64(ccx, size), ptr],
+             None,
+             DebugLoc::None);
+    })
+}
 
-    let ptr = PointerCast(cx, ptr, Type::i8p(ccx));
-    let lifetime_end = ccx.get_intrinsic(&"llvm.lifetime.end");
-    Call(cx,
-         lifetime_end,
-         &[C_u64(ccx, size), ptr],
-         None,
-         DebugLoc::None);
+pub fn call_lifetime_end(cx: Block, ptr: ValueRef) {
+    core_lifetime_emit(cx.ccx(), ptr, Lifetime::End, |ccx, size, lifetime_end| {
+        let ptr = PointerCast(cx, ptr, Type::i8p(ccx));
+        Call(cx,
+             lifetime_end,
+             &[C_u64(ccx, size), ptr],
+             None,
+             DebugLoc::None);
+    })
 }
 
 // Generates code for resumption of unwind at the end of a landing pad.
@@ -1276,12 +1301,81 @@ fn memfill<'a, 'tcx>(b: &Builder<'a, 'tcx>, llptr: ValueRef, ty: Ty<'tcx>, byte:
            None);
 }
 
-pub fn alloc_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, name: &str) -> ValueRef {
+/// In general, when we create an scratch value in an alloca, the
+/// creator may not know if the block (that initializes the scratch
+/// with the desired value) actually dominates the cleanup associated
+/// with the scratch value.
+///
+/// To deal with this, when we do an alloca (at the *start* of whole
+/// function body), we optionally can also set the associated
+/// dropped-flag state of the alloca to "dropped."
+#[derive(Copy, Clone, Debug)]
+pub enum InitAlloca {
+    /// Indicates that the state should have its associated drop flag
+    /// set to "dropped" at the point of allocation.
+    Dropped,
+    /// Indicates the value of the associated drop flag is irrelevant.
+    /// The embedded string literal is a programmer provided argument
+    /// for why. This is a safeguard forcing compiler devs to
+    /// document; it might be a good idea to also emit this as a
+    /// comment with the alloca itself when emitting LLVM output.ll.
+    Uninit(&'static str),
+}
+
+
+pub fn alloc_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                            t: Ty<'tcx>,
+                            name: &str) -> ValueRef {
+    // pnkfelix: I do not know why alloc_ty meets the assumptions for
+    // passing Uninit, but it was never needed (even back when we had
+    // the original boolean `zero` flag on `lvalue_scratch_datum`).
+    alloc_ty_init(bcx, t, InitAlloca::Uninit("all alloc_ty are uninit"), name)
+}
+
+/// This variant of `fn alloc_ty` does not necessarily assume that the
+/// alloca should be created with no initial value. Instead the caller
+/// controls that assumption via the `init` flag.
+///
+/// Note that if the alloca *is* initialized via `init`, then we will
+/// also inject an `llvm.lifetime.start` before that initialization
+/// occurs, and thus callers should not call_lifetime_start
+/// themselves.  But if `init` says "uninitialized", then callers are
+/// in charge of choosing where to call_lifetime_start and
+/// subsequently populate the alloca.
+///
+/// (See related discussion on PR #30823.)
+pub fn alloc_ty_init<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                             t: Ty<'tcx>,
+                             init: InitAlloca,
+                             name: &str) -> ValueRef {
     let _icx = push_ctxt("alloc_ty");
     let ccx = bcx.ccx();
     let ty = type_of::type_of(ccx, t);
     assert!(!t.has_param_types());
-    alloca(bcx, ty, name)
+    match init {
+        InitAlloca::Dropped => alloca_dropped(bcx, t, name),
+        InitAlloca::Uninit(_) => alloca(bcx, ty, name),
+    }
+}
+
+pub fn alloca_dropped<'blk, 'tcx>(cx: Block<'blk, 'tcx>, ty: Ty<'tcx>, name: &str) -> ValueRef {
+    let _icx = push_ctxt("alloca_dropped");
+    let llty = type_of::type_of(cx.ccx(), ty);
+    if cx.unreachable.get() {
+        unsafe { return llvm::LLVMGetUndef(llty.ptr_to().to_ref()); }
+    }
+    let p = alloca(cx, llty, name);
+    let b = cx.fcx.ccx.builder();
+    b.position_before(cx.fcx.alloca_insert_pt.get().unwrap());
+
+    // This is just like `call_lifetime_start` (but latter expects a
+    // Block, which we do not have for `alloca_insert_pt`).
+    core_lifetime_emit(cx.ccx(), p, Lifetime::Start, |ccx, size, lifetime_start| {
+        let ptr = b.pointercast(p, Type::i8p(ccx));
+        b.call(lifetime_start, &[C_u64(ccx, size), ptr], None);
+    });
+    memfill(&b, p, ty, adt::DTOR_DONE);
+    p
 }
 
 pub fn alloca(cx: Block, ty: Type, name: &str) -> ValueRef {
@@ -1556,6 +1650,7 @@ pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>,
     // Create the drop-flag hints for every unfragmented path in the function.
     let tcx = fcx.ccx.tcx();
     let fn_did = tcx.map.local_def_id(fcx.id);
+    let tables = tcx.tables.borrow();
     let mut hints = fcx.lldropflag_hints.borrow_mut();
     let fragment_infos = tcx.fragment_infos.borrow();
 
@@ -1579,12 +1674,22 @@ pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>,
             let (var, datum) = match info {
                 ty::FragmentInfo::Moved { var, .. } |
                 ty::FragmentInfo::Assigned { var, .. } => {
-                    let datum = seen.get(&var).cloned().unwrap_or_else(|| {
-                        let datum = make_datum(var);
-                        seen.insert(var, datum.clone());
-                        datum
+                    let opt_datum = seen.get(&var).cloned().unwrap_or_else(|| {
+                        let ty = tables.node_types[&var];
+                        if fcx.type_needs_drop(ty) {
+                            let datum = make_datum(var);
+                            seen.insert(var, Some(datum.clone()));
+                            Some(datum)
+                        } else {
+                            // No drop call needed, so we don't need a dropflag hint
+                            None
+                        }
                     });
-                    (var, datum)
+                    if let Some(datum) = opt_datum {
+                        (var, datum)
+                    } else {
+                        continue
+                    }
                 }
             };
             match info {
@@ -1630,6 +1735,8 @@ pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>,
     let fcx = bcx.fcx;
     let arg_scope_id = cleanup::CustomScope(arg_scope);
 
+    debug!("create_datums_for_fn_args");
+
     // Return an array wrapping the ValueRefs that we get from `get_param` for
     // each argument into datums.
     //
@@ -1641,6 +1748,7 @@ pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>,
     // This alloca should be optimized away by LLVM's mem-to-reg pass in
     // the event it's not truly needed.
     let mut idx = fcx.arg_offset() as c_uint;
+    let uninit_reason = InitAlloca::Uninit("fn_arg populate dominates dtor");
     for (i, &arg_ty) in arg_tys.iter().enumerate() {
         let arg_datum = if !has_tupled_arg || i < arg_tys.len() - 1 {
             if type_of::arg_is_indirect(bcx.ccx(), arg_ty) &&
@@ -1660,9 +1768,12 @@ pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>,
                 let data = get_param(fcx.llfn, idx);
                 let extra = get_param(fcx.llfn, idx + 1);
                 idx += 2;
-                unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx, arg_ty, "",
+                unpack_datum!(bcx, datum::lvalue_scratch_datum(bcx, arg_ty, "", uninit_reason,
                                                         arg_scope_id, (data, extra),
                                                         |(data, extra), bcx, dst| {
+                    debug!("populate call for create_datum_for_fn_args \
+                            early fat arg, on arg[{}] ty={:?}", i, arg_ty);
+
                     Store(bcx, data, expr::get_dataptr(bcx, dst));
                     Store(bcx, extra, expr::get_meta(bcx, dst));
                     bcx
@@ -1675,9 +1786,16 @@ pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>,
                               datum::lvalue_scratch_datum(bcx,
                                                           arg_ty,
                                                           "",
+                                                          uninit_reason,
                                                           arg_scope_id,
                                                           tmp,
-                                                          |tmp, bcx, dst| tmp.store_to(bcx, dst)))
+                                                          |tmp, bcx, dst| {
+
+                        debug!("populate call for create_datum_for_fn_args \
+                                early thin arg, on arg[{}] ty={:?}", i, arg_ty);
+
+                                                              tmp.store_to(bcx, dst)
+                                                          }))
             }
         } else {
             // FIXME(pcwalton): Reduce the amount of code bloat this is responsible for.
@@ -1687,11 +1805,14 @@ pub fn create_datums_for_fn_args<'a, 'tcx>(mut bcx: Block<'a, 'tcx>,
                                   datum::lvalue_scratch_datum(bcx,
                                                               arg_ty,
                                                               "tupled_args",
+                                                              uninit_reason,
                                                               arg_scope_id,
                                                               (),
                                                               |(),
                                                                mut bcx,
-                                                               llval| {
+                                                              llval| {
+                        debug!("populate call for create_datum_for_fn_args \
+                                tupled_args, on arg[{}] ty={:?}", i, arg_ty);
                         for (j, &tupled_arg_ty) in
                                     tupled_arg_tys.iter().enumerate() {
                             let lldest = StructGEP(bcx, llval, j);
@@ -1991,7 +2112,7 @@ pub fn trans_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
 pub fn trans_enum_variant<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                     ctor_id: ast::NodeId,
-                                    disr: ty::Disr,
+                                    disr: Disr,
                                     param_substs: &'tcx Substs<'tcx>,
                                     llfndecl: ValueRef) {
     let _icx = push_ctxt("trans_enum_variant");
@@ -2001,7 +2122,7 @@ pub fn trans_enum_variant<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
 pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                                                  ctor_ty: Ty<'tcx>,
-                                                 disr: ty::Disr,
+                                                 disr: Disr,
                                                  args: callee::CallArgs,
                                                  dest: expr::Dest,
                                                  debug_loc: DebugLoc)
@@ -2077,12 +2198,12 @@ pub fn trans_tuple_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                     llfndecl: ValueRef) {
     let _icx = push_ctxt("trans_tuple_struct");
 
-    trans_enum_variant_or_tuple_like_struct(ccx, ctor_id, 0, param_substs, llfndecl);
+    trans_enum_variant_or_tuple_like_struct(ccx, ctor_id, Disr(0), param_substs, llfndecl);
 }
 
 fn trans_enum_variant_or_tuple_like_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                                      ctor_id: ast::NodeId,
-                                                     disr: ty::Disr,
+                                                     disr: Disr,
                                                      param_substs: &'tcx Substs<'tcx>,
                                                      llfndecl: ValueRef) {
     let ctor_ty = ccx.tcx().node_id_to_type(ctor_id);
@@ -2113,7 +2234,7 @@ fn trans_enum_variant_or_tuple_like_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx
         let repr = adt::represent_type(ccx, result_ty.unwrap());
         let mut llarg_idx = fcx.arg_offset() as c_uint;
         for (i, arg_ty) in arg_tys.into_iter().enumerate() {
-            let lldestptr = adt::trans_field_ptr(bcx, &*repr, dest_val, disr, i);
+            let lldestptr = adt::trans_field_ptr(bcx, &*repr, dest_val, Disr::from(disr), i);
             if common::type_is_fat_ptr(bcx.tcx(), arg_ty) {
                 Store(bcx,
                       get_param(fcx.llfn, llarg_idx),
@@ -2181,17 +2302,20 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &hir::EnumDef, sp: Span,
             }
     );
 
+    // FIXME(#30505) Should use logging for this.
     if print_info {
         let llty = type_of::sizing_type_of(ccx, ty);
 
         let sess = &ccx.tcx().sess;
-        sess.span_note(sp, &*format!("total size: {} bytes", llsize_of_real(ccx, llty)));
+        sess.span_note_without_error(sp,
+                                     &*format!("total size: {} bytes", llsize_of_real(ccx, llty)));
         match *avar {
             adt::General(..) => {
                 for (i, var) in enum_def.variants.iter().enumerate() {
                     ccx.tcx()
                        .sess
-                       .span_note(var.span, &*format!("variant data: {} bytes", sizes[i]));
+                       .span_note_without_error(var.span,
+                                                &*format!("variant data: {} bytes", sizes[i]));
                 }
             }
             _ => {}
@@ -2203,17 +2327,17 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &hir::EnumDef, sp: Span,
     if !is_allow && largest > slargest * 3 && slargest > 0 {
         // Use lint::raw_emit_lint rather than sess.add_lint because the lint-printing
         // pass for the latter already ran.
-        lint::raw_emit_lint(&ccx.tcx().sess,
-                            &ccx.tcx().sess.lint_store.borrow(),
-                            lint::builtin::VARIANT_SIZE_DIFFERENCES,
-                            *lvlsrc.unwrap(),
-                            Some(sp),
-                            &format!("enum variant is more than three times larger ({} bytes) \
-                                      than the next largest (ignoring padding)",
-                                     largest));
-
-        ccx.sess().span_note(enum_def.variants[largest_index].span,
-                             "this variant is the largest");
+        lint::raw_struct_lint(&ccx.tcx().sess,
+                              &ccx.tcx().sess.lint_store.borrow(),
+                              lint::builtin::VARIANT_SIZE_DIFFERENCES,
+                              *lvlsrc.unwrap(),
+                              Some(sp),
+                              &format!("enum variant is more than three times larger ({} bytes) \
+                                        than the next largest (ignoring padding)",
+                                       largest))
+            .span_note(enum_def.variants[largest_index].span,
+                       "this variant is the largest")
+            .emit();
     }
 }
 
@@ -2490,9 +2614,10 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) {
         let llfty = Type::func(&[ccx.int_type(), Type::i8p(ccx).ptr_to()], &ccx.int_type());
 
         let llfn = declare::define_cfn(ccx, "main", llfty, ccx.tcx().mk_nil()).unwrap_or_else(|| {
-            ccx.sess().span_err(sp, "entry symbol `main` defined multiple times");
             // FIXME: We should be smart and show a better diagnostic here.
-            ccx.sess().help("did you use #[no_mangle] on `fn main`? Use #[start] instead");
+            ccx.sess().struct_span_err(sp, "entry symbol `main` defined multiple times")
+                      .help("did you use #[no_mangle] on `fn main`? Use #[start] instead")
+                      .emit();
             ccx.sess().abort_if_errors();
             panic!();
         });
@@ -2761,7 +2886,11 @@ fn register_method(ccx: &CrateContext,
     }
 }
 
-pub fn write_metadata(cx: &SharedCrateContext, krate: &hir::Crate, reachable: &NodeSet) -> Vec<u8> {
+pub fn write_metadata<'a, 'tcx>(cx: &SharedCrateContext<'a, 'tcx>,
+                                krate: &hir::Crate,
+                                reachable: &NodeSet,
+                                mir_map: &MirMap<'tcx>)
+                                -> Vec<u8> {
     use flate;
 
     let any_library = cx.sess()
@@ -2774,9 +2903,13 @@ pub fn write_metadata(cx: &SharedCrateContext, krate: &hir::Crate, reachable: &N
     }
 
     let cstore = &cx.tcx().sess.cstore;
-    let metadata = cstore.encode_metadata(
-        cx.tcx(), cx.export_map(), cx.item_symbols(), cx.link_meta(), reachable,
-        krate);
+    let metadata = cstore.encode_metadata(cx.tcx(),
+                                          cx.export_map(),
+                                          cx.item_symbols(),
+                                          cx.link_meta(),
+                                          reachable,
+                                          mir_map,
+                                          krate);
     let mut compressed = cstore.metadata_encoding_version().to_vec();
     compressed.extend_from_slice(&flate::deflate_bytes(&metadata));
 
@@ -2966,9 +3099,16 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>,
                          mir_map: &MirMap<'tcx>,
                          analysis: ty::CrateAnalysis)
                          -> CrateTranslation {
-    let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis;
+    let _task = tcx.dep_graph.in_task(DepNode::TransCrate);
+
+    // Be careful with this krate: obviously it gives access to the
+    // entire contents of the krate. So if you push any subtasks of
+    // `TransCrate`, you need to be careful to register "reads" of the
+    // particular items that will be processed.
     let krate = tcx.map.krate();
 
+    let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis;
+
     let check_overflow = if let Some(v) = tcx.sess.opts.debugging_opts.force_overflow_checks {
         v
     } else {
@@ -3046,7 +3186,9 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>,
     let reachable_symbol_ids = filter_reachable_ids(&shared_ccx);
 
     // Translate the metadata.
-    let metadata = write_metadata(&shared_ccx, krate, &reachable_symbol_ids);
+    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();
@@ -3120,6 +3262,8 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>,
     };
     let no_builtins = attr::contains_name(&krate.attrs, "no_builtins");
 
+    assert_dep_graph::assert_dep_graph(tcx);
+
     CrateTranslation {
         modules: modules,
         metadata_module: metadata_module,
@@ -3172,7 +3316,16 @@ impl<'a, 'tcx, 'v> Visitor<'v> for TransItemsWithinModVisitor<'a, 'tcx> {
                 // skip modules, they will be uncovered by the TransModVisitor
             }
             _ => {
-                trans_item(self.ccx, i);
+                let def_id = self.ccx.tcx().map.local_def_id(i.id);
+                let tcx = self.ccx.tcx();
+
+                // Create a subtask for trans'ing a particular item. We are
+                // giving `trans_item` access to this item, so also record a read.
+                tcx.dep_graph.with_task(DepNode::TransCrateItem(def_id), || {
+                    tcx.dep_graph.read(DepNode::Hir(def_id));
+                    trans_item(self.ccx, i);
+                });
+
                 intravisit::walk_item(self, i);
             }
         }
index 0ff5264c00f0f8346ebd9d3180affdf7787c9216..4bfbb8b69f0acee7fd17163e36b9c3c7baddd6ad 100644 (file)
@@ -19,6 +19,7 @@ use trans::cabi_x86_win64;
 use trans::cabi_arm;
 use trans::cabi_aarch64;
 use trans::cabi_powerpc;
+use trans::cabi_powerpc64;
 use trans::cabi_mips;
 use trans::type_::Type;
 
@@ -127,6 +128,7 @@ pub fn compute_abi_info(ccx: &CrateContext,
         },
         "mips" => cabi_mips::compute_abi_info(ccx, atys, rty, ret_def),
         "powerpc" => cabi_powerpc::compute_abi_info(ccx, atys, rty, ret_def),
+        "powerpc64" | "powerpc64le" => cabi_powerpc64::compute_abi_info(ccx, atys, rty, ret_def),
         a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a)
                               ),
     }
diff --git a/src/librustc_trans/trans/cabi_powerpc64.rs b/src/librustc_trans/trans/cabi_powerpc64.rs
new file mode 100644 (file)
index 0000000..cba7300
--- /dev/null
@@ -0,0 +1,259 @@
+// Copyright 2014-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: The PowerPC64 ABI needs to zero or sign extend function
+// call parameters, but compute_abi_info() is passed LLVM types
+// which have no sign information.
+//
+// Alignment of 128 bit types is not currently handled, this will
+// need to be fixed when PowerPC vector support is added.
+
+use llvm::{Integer, Pointer, Float, Double, Struct, Array, Attribute};
+use trans::cabi::{FnType, ArgType};
+use trans::context::CrateContext;
+use trans::type_::Type;
+
+use std::cmp;
+
+fn align_up_to(off: usize, a: usize) -> usize {
+    return (off + a - 1) / a * a;
+}
+
+fn align(off: usize, ty: Type) -> usize {
+    let a = ty_align(ty);
+    return align_up_to(off, a);
+}
+
+fn ty_align(ty: Type) -> usize {
+    match ty.kind() {
+        Integer => ((ty.int_width() as usize) + 7) / 8,
+        Pointer => 8,
+        Float => 4,
+        Double => 8,
+        Struct => {
+            if ty.is_packed() {
+                1
+            } else {
+                let str_tys = ty.field_types();
+                str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t)))
+            }
+        }
+        Array => {
+            let elt = ty.element_type();
+            ty_align(elt)
+        }
+        _ => panic!("ty_align: unhandled type")
+    }
+}
+
+fn ty_size(ty: Type) -> usize {
+    match ty.kind() {
+        Integer => ((ty.int_width() as usize) + 7) / 8,
+        Pointer => 8,
+        Float => 4,
+        Double => 8,
+        Struct => {
+            if ty.is_packed() {
+                let str_tys = ty.field_types();
+                str_tys.iter().fold(0, |s, t| s + ty_size(*t))
+            } else {
+                let str_tys = ty.field_types();
+                let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t));
+                align(size, ty)
+            }
+        }
+        Array => {
+            let len = ty.array_length();
+            let elt = ty.element_type();
+            let eltsz = ty_size(elt);
+            len * eltsz
+        }
+        _ => panic!("ty_size: unhandled type")
+    }
+}
+
+fn is_homogenous_aggregate_ty(ty: Type) -> Option<(Type, u64)> {
+    fn check_array(ty: Type) -> Option<(Type, u64)> {
+        let len = ty.array_length() as u64;
+        if len == 0 {
+            return None
+        }
+        let elt = ty.element_type();
+
+        // if our element is an HFA/HVA, so are we; multiply members by our len
+        is_homogenous_aggregate_ty(elt).map(|(base_ty, members)| (base_ty, len * members))
+    }
+
+    fn check_struct(ty: Type) -> Option<(Type, u64)> {
+        let str_tys = ty.field_types();
+        if str_tys.len() == 0 {
+            return None
+        }
+
+        let mut prev_base_ty = None;
+        let mut members = 0;
+        for opt_homog_agg in str_tys.iter().map(|t| is_homogenous_aggregate_ty(*t)) {
+            match (prev_base_ty, opt_homog_agg) {
+                // field isn't itself an HFA, so we aren't either
+                (_, None) => return None,
+
+                // first field - store its type and number of members
+                (None, Some((field_ty, field_members))) => {
+                    prev_base_ty = Some(field_ty);
+                    members = field_members;
+                },
+
+                // 2nd or later field - give up if it's a different type; otherwise incr. members
+                (Some(prev_ty), Some((field_ty, field_members))) => {
+                    if prev_ty != field_ty {
+                        return None;
+                    }
+                    members += field_members;
+                }
+            }
+        }
+
+        // Because of previous checks, we know prev_base_ty is Some(...) because
+        //   1. str_tys has at least one element; and
+        //   2. prev_base_ty was filled in (or we would've returned early)
+        let (base_ty, members) = (prev_base_ty.unwrap(), members);
+
+        // Ensure there is no padding.
+        if ty_size(ty) == ty_size(base_ty) * (members as usize) {
+            Some((base_ty, members))
+        } else {
+            None
+        }
+    }
+
+    let homog_agg = match ty.kind() {
+        Float  => Some((ty, 1)),
+        Double => Some((ty, 1)),
+        Array  => check_array(ty),
+        Struct => check_struct(ty),
+        _ => None
+    };
+
+    // Ensure we have at most eight uniquely addressable members
+    homog_agg.and_then(|(base_ty, members)| {
+        if members > 0 && members <= 8 {
+            Some((base_ty, members))
+        } else {
+            None
+        }
+    })
+}
+
+fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType {
+    if is_reg_ty(ty) {
+        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
+        return ArgType::direct(ty, None, None, attr);
+    }
+
+    // The PowerPC64 big endian ABI doesn't return aggregates in registers
+    if ccx.sess().target.target.arch == "powerpc64" {
+        return ArgType::indirect(ty, Some(Attribute::StructRet))
+    }
+
+    if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ty) {
+        let llty = Type::array(&base_ty, members);
+        return ArgType::direct(ty, Some(llty), None, None);
+    }
+    let size = ty_size(ty);
+    if size <= 16 {
+        let llty = if size <= 1 {
+            Type::i8(ccx)
+        } else if size <= 2 {
+            Type::i16(ccx)
+        } else if size <= 4 {
+            Type::i32(ccx)
+        } else if size <= 8 {
+            Type::i64(ccx)
+        } else {
+            Type::array(&Type::i64(ccx), ((size + 7 ) / 8 ) as u64)
+        };
+        return ArgType::direct(ty, Some(llty), None, None);
+    }
+
+    ArgType::indirect(ty, Some(Attribute::StructRet))
+}
+
+fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType {
+    if is_reg_ty(ty) {
+        let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExt) } else { None };
+        return ArgType::direct(ty, None, None, attr);
+    }
+    if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ty) {
+        let llty = Type::array(&base_ty, members);
+        return ArgType::direct(ty, Some(llty), None, None);
+    }
+
+    ArgType::direct(
+        ty,
+        Some(struct_ty(ccx, ty)),
+        None,
+        None
+    )
+}
+
+fn is_reg_ty(ty: Type) -> bool {
+    match ty.kind() {
+        Integer
+        | Pointer
+        | Float
+        | Double => true,
+        _ => false
+    }
+}
+
+fn coerce_to_long(ccx: &CrateContext, size: usize) -> Vec<Type> {
+    let long_ty = Type::i64(ccx);
+    let mut args = Vec::new();
+
+    let mut n = size / 64;
+    while n > 0 {
+        args.push(long_ty);
+        n -= 1;
+    }
+
+    let r = size % 64;
+    if r > 0 {
+        args.push(Type::ix(ccx, r as u64));
+    }
+
+    args
+}
+
+fn struct_ty(ccx: &CrateContext, ty: Type) -> Type {
+    let size = ty_size(ty) * 8;
+    Type::struct_(ccx, &coerce_to_long(ccx, size), false)
+}
+
+pub fn compute_abi_info(ccx: &CrateContext,
+                        atys: &[Type],
+                        rty: Type,
+                        ret_def: bool) -> FnType {
+    let ret_ty = if ret_def {
+        classify_ret_ty(ccx, rty)
+    } else {
+        ArgType::direct(Type::void(ccx), None, None, None)
+    };
+
+    let mut arg_tys = Vec::new();
+    for &aty in atys {
+        let ty = classify_arg_ty(ccx, aty);
+        arg_tys.push(ty);
+    };
+
+    return FnType {
+        arg_tys: arg_tys,
+        ret_ty: ret_ty,
+    };
+}
index 4aa7c0ff587f04d53aff920481f11a98ebab8879..c7ec1c0955146ca82182a4593023bba29d16fd51 100644 (file)
@@ -20,7 +20,6 @@ pub use self::CallArgs::*;
 
 use arena::TypedArena;
 use back::link;
-use session;
 use llvm::{self, ValueRef, get_params};
 use middle::cstore::LOCAL_CRATE;
 use middle::def;
@@ -51,12 +50,14 @@ use trans::meth;
 use trans::monomorphize;
 use trans::type_::Type;
 use trans::type_of;
-use middle::ty::{self, Ty, HasTypeFlags, RegionEscape};
+use trans::Disr;
+use middle::ty::{self, Ty, TypeFoldable};
 use middle::ty::MethodCall;
 use rustc_front::hir;
 
 use syntax::abi as synabi;
 use syntax::ast;
+use syntax::errors;
 use syntax::ptr::P;
 
 #[derive(Copy, Clone)]
@@ -68,7 +69,7 @@ pub struct MethodData {
 pub enum CalleeData<'tcx> {
     // Constructor for enum variant/tuple-like-struct
     // i.e. Some, Ok
-    NamedTupleConstructor(ty::Disr),
+    NamedTupleConstructor(Disr),
 
     // Represents a (possibly monomorphized) top-level fn item or method
     // item. Note that this is just the fn-ptr and is not a Rust closure
@@ -151,7 +152,7 @@ fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &hir::Expr)
             } => {
                 Callee {
                     bcx: bcx,
-                    data: NamedTupleConstructor(0),
+                    data: NamedTupleConstructor(Disr(0)),
                     ty: expr_ty
                 }
             }
@@ -195,14 +196,14 @@ fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &hir::Expr)
 
                 Callee {
                     bcx: bcx,
-                    data: NamedTupleConstructor(vinfo.disr_val),
+                    data: NamedTupleConstructor(Disr::from(vinfo.disr_val)),
                     ty: expr_ty
                 }
             }
             def::DefStruct(_) => {
                 Callee {
                     bcx: bcx,
-                    data: NamedTupleConstructor(0),
+                    data: NamedTupleConstructor(Disr(0)),
                     ty: expr_ty
                 }
             }
@@ -215,8 +216,8 @@ fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &hir::Expr)
             }
             def::DefMod(..) | def::DefForeignMod(..) | def::DefTrait(..) |
             def::DefTy(..) | def::DefPrimTy(..) | def::DefAssociatedTy(..) |
-            def::DefUse(..) | def::DefLabel(..) | def::DefTyParam(..) |
-            def::DefSelfTy(..) => {
+            def::DefLabel(..) | def::DefTyParam(..) |
+            def::DefSelfTy(..) | def::DefErr => {
                 bcx.tcx().sess.span_bug(
                     ref_expr.span,
                     &format!("cannot translate def {:?} \
@@ -412,8 +413,8 @@ pub fn trans_fn_ref_with_substs<'a, 'tcx>(
             Some(n) => n,
             None => { return false; }
         };
-        let map_node = session::expect(
-            &tcx.sess,
+        let map_node = errors::expect(
+            &tcx.sess.diagnostic(),
             tcx.map.find(node_id),
             || "local item should be in ast map".to_string());
 
@@ -863,7 +864,7 @@ fn trans_args_under_call_abi<'blk, 'tcx>(
                     bcx,
                     field_type,
                     |srcval| {
-                        adt::trans_field_ptr(bcx, repr_ptr, srcval, 0, i)
+                        adt::trans_field_ptr(bcx, repr_ptr, srcval, Disr(0), i)
                     }).to_expr_datum();
                 bcx = trans_arg_datum(bcx,
                                       field_type,
index c125ba30a51e8ca685c4a608f1d41173ca1d9d43..5bdfc099f0880eedc40bf0252fcea8d7770899e4 100644 (file)
@@ -20,17 +20,19 @@ use trans::build::*;
 use trans::callee::{self, ArgVals, Callee, TraitItem, MethodData};
 use trans::cleanup::{CleanupMethods, CustomScope, ScopeId};
 use trans::common::*;
-use trans::datum::{self, Datum, rvalue_scratch_datum, Rvalue, ByValue};
+use trans::datum::{self, Datum, rvalue_scratch_datum, Rvalue};
 use trans::debuginfo::{self, DebugLoc};
 use trans::declare;
 use trans::expr;
 use trans::monomorphize::{MonoId};
 use trans::type_of::*;
+use trans::Disr;
 use middle::ty;
 use session::config::FullDebugInfo;
 
 use syntax::abi::RustCall;
 use syntax::ast;
+use syntax::attr::{ThinAttributes, ThinAttributesExt};
 
 use rustc_front::hir;
 
@@ -176,7 +178,8 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
                                     body: &hir::Block,
                                     id: ast::NodeId,
                                     closure_def_id: DefId, // (*)
-                                    closure_substs: &'tcx ty::ClosureSubsts<'tcx>)
+                                    closure_substs: &'tcx ty::ClosureSubsts<'tcx>,
+                                    closure_expr_attrs: &ThinAttributes)
                                     -> Option<Block<'a, 'tcx>>
 {
     // (*) Note that in the case of inlined functions, the `closure_def_id` will be the
@@ -218,7 +221,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
                   llfn,
                   param_substs,
                   id,
-                  &[],
+                  closure_expr_attrs.as_attr_slice(),
                   sig.output,
                   function_type.abi,
                   ClosureEnv::Closure(closure_def_id, &freevars));
@@ -240,7 +243,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
     for (i, freevar) in freevars.iter().enumerate() {
         let datum = expr::trans_local_var(bcx, freevar.def);
         let upvar_slot_dest = adt::trans_field_ptr(
-            bcx, &*repr, adt::MaybeSizedValue::sized(dest_addr), 0, i);
+            bcx, &*repr, adt::MaybeSizedValue::sized(dest_addr), Disr(0), i);
         let upvar_id = ty::UpvarId { var_id: freevar.def.var_id(),
                                      closure_expr_id: id };
         match tcx.upvar_capture(upvar_id).unwrap() {
@@ -252,7 +255,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
             }
         }
     }
-    adt::trans_set_discr(bcx, &*repr, dest_addr, 0);
+    adt::trans_set_discr(bcx, &*repr, dest_addr, Disr(0));
 
     Some(bcx)
 }
index fa500ab93552c97a031fa725db158754ed4d8522..b73e5ff3e038eb493f1d88f958a16bd533726648 100644 (file)
@@ -37,7 +37,7 @@ use trans::monomorphize;
 use trans::type_::Type;
 use trans::type_of;
 use middle::traits;
-use middle::ty::{self, HasTypeFlags, Ty};
+use middle::ty::{self, Ty};
 use middle::ty::fold::{TypeFolder, TypeFoldable};
 use rustc_front::hir;
 use rustc::mir::repr::Mir;
@@ -73,45 +73,6 @@ pub fn type_is_fat_ptr<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
     }
 }
 
-/// If `type_needs_drop` returns true, then `ty` is definitely
-/// non-copy and *might* have a destructor attached; if it returns
-/// false, then `ty` definitely has no destructor (i.e. no drop glue).
-///
-/// (Note that this implies that if `ty` has a destructor attached,
-/// then `type_needs_drop` will definitely return `true` for `ty`.)
-pub fn type_needs_drop<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
-    type_needs_drop_given_env(cx, ty, &cx.empty_parameter_environment())
-}
-
-/// Core implementation of type_needs_drop, potentially making use of
-/// and/or updating caches held in the `param_env`.
-fn type_needs_drop_given_env<'a,'tcx>(cx: &ty::ctxt<'tcx>,
-                                      ty: Ty<'tcx>,
-                                      param_env: &ty::ParameterEnvironment<'a,'tcx>) -> bool {
-    // Issue #22536: We first query type_moves_by_default.  It sees a
-    // normalized version of the type, and therefore will definitely
-    // know whether the type implements Copy (and thus needs no
-    // cleanup/drop/zeroing) ...
-    let implements_copy = !ty.moves_by_default(param_env, DUMMY_SP);
-
-    if implements_copy { return false; }
-
-    // ... (issue #22536 continued) but as an optimization, still use
-    // prior logic of asking if the `needs_drop` bit is set; we need
-    // not zero non-Copy types if they have no destructor.
-
-    // FIXME(#22815): Note that calling `ty::type_contents` is a
-    // conservative heuristic; it may report that `needs_drop` is set
-    // when actual type does not actually have a destructor associated
-    // with it. But since `ty` absolutely did not have the `Copy`
-    // bound attached (see above), it is sound to treat it as having a
-    // destructor (e.g. zero its memory on move).
-
-    let contents = ty.type_contents(cx);
-    debug!("type_needs_drop ty={:?} contents={:?}", ty, contents);
-    contents.needs_drop(cx)
-}
-
 fn type_is_newtype_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
     match ty.sty {
         ty::TyStruct(def, substs) => {
@@ -158,11 +119,9 @@ pub fn type_is_zero_size<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -
 }
 
 /// Identifies types which we declare to be equivalent to `void` in C for the purpose of function
-/// return types. These are `()`, bot, and uninhabited enums. Note that all such types are also
-/// zero-size, but not all zero-size types use a `void` return type (in order to aid with C ABI
-/// compatibility).
-pub fn return_type_is_void(ccx: &CrateContext, ty: Ty) -> bool {
-    ty.is_nil() || ty.is_empty(ccx.tcx())
+/// return types. These are `()`, bot, uninhabited enums and all other zero-sized types.
+pub fn return_type_is_void<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
+    ty.is_nil() || ty.is_empty(ccx.tcx()) || type_is_zero_size(ccx, ty)
 }
 
 /// Generates a unique symbol based off the name given. This is used to create
@@ -200,6 +159,8 @@ pub fn gensym_name(name: &str) -> ast::Name {
 *
 */
 
+use trans::Disr;
+
 #[derive(Copy, Clone)]
 pub struct NodeIdAndSpan {
     pub id: ast::NodeId,
@@ -216,7 +177,7 @@ pub struct Field<'tcx>(pub ast::Name, pub Ty<'tcx>);
 
 /// The concrete version of ty::VariantDef
 pub struct VariantInfo<'tcx> {
-    pub discr: ty::Disr,
+    pub discr: Disr,
     pub fields: Vec<Field<'tcx>>
 }
 
@@ -234,7 +195,7 @@ impl<'tcx> VariantInfo<'tcx> {
                 };
 
                 VariantInfo {
-                    discr: variant.disr_val,
+                    discr: Disr::from(variant.disr_val),
                     fields: variant.fields.iter().map(|f| {
                         Field(f.name, monomorphize::field_ty(tcx, substs, f))
                     }).collect()
@@ -243,7 +204,7 @@ impl<'tcx> VariantInfo<'tcx> {
 
             ty::TyTuple(ref v) => {
                 VariantInfo {
-                    discr: 0,
+                    discr: Disr(0),
                     fields: v.iter().enumerate().map(|(i, &t)| {
                         Field(token::intern(&i.to_string()), t)
                     }).collect()
@@ -508,7 +469,7 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
     }
 
     pub fn monomorphize<T>(&self, value: &T) -> T
-        where T : TypeFoldable<'tcx> + HasTypeFlags
+        where T : TypeFoldable<'tcx>
     {
         monomorphize::apply_param_substs(self.ccx.tcx(),
                                          self.param_substs,
@@ -518,7 +479,7 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
     /// This is the same as `common::type_needs_drop`, except that it
     /// may use or update caches within this `FunctionContext`.
     pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool {
-        type_needs_drop_given_env(self.ccx.tcx(), ty, &self.param_env)
+        self.ccx.tcx().type_needs_drop_given_env(ty, &self.param_env)
     }
 
     pub fn eh_personality(&self) -> ValueRef {
@@ -689,7 +650,7 @@ impl<'blk, 'tcx> BlockS<'blk, 'tcx> {
     }
 
     pub fn monomorphize<T>(&self, value: &T) -> T
-        where T : TypeFoldable<'tcx> + HasTypeFlags
+        where T : TypeFoldable<'tcx>
     {
         monomorphize::apply_param_substs(self.tcx(),
                                          self.fcx.param_substs,
index 6f40283064bd05011a3bfb4c659649f963d8f8da..0fc879707331df7b2bc62e8f1d4495881970e9c3 100644 (file)
@@ -37,6 +37,7 @@ use trans::declare;
 use trans::monomorphize;
 use trans::type_::Type;
 use trans::type_of;
+use trans::Disr;
 use middle::subst::Substs;
 use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer};
 use middle::ty::adjustment::AdjustUnsafeFnPointer;
@@ -217,7 +218,8 @@ fn const_fn_call<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
 pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 def_id: DefId,
-                                ref_expr: &hir::Expr)
+                                ref_expr: &hir::Expr,
+                                param_substs: &'tcx Substs<'tcx>)
                                 -> &'tcx hir::Expr {
     let def_id = inline::maybe_instantiate_inline(ccx, def_id);
 
@@ -226,7 +228,7 @@ pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                             "cross crate constant could not be inlined");
     }
 
-    match const_eval::lookup_const_by_id(ccx.tcx(), def_id, Some(ref_expr.id)) {
+    match const_eval::lookup_const_by_id(ccx.tcx(), def_id, Some(ref_expr.id), Some(param_substs)) {
         Some(ref expr) => expr,
         None => {
             ccx.sess().span_bug(ref_expr.span, "constant item not found")
@@ -264,10 +266,12 @@ pub enum TrueConst {
 
 use self::ConstEvalFailure::*;
 
-fn get_const_val(ccx: &CrateContext,
-                 def_id: DefId,
-                 ref_expr: &hir::Expr) -> Result<ValueRef, ConstEvalFailure> {
-    let expr = get_const_expr(ccx, def_id, ref_expr);
+fn get_const_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                           def_id: DefId,
+                           ref_expr: &hir::Expr,
+                           param_substs: &'tcx Substs<'tcx>)
+                           -> Result<ValueRef, ConstEvalFailure> {
+    let expr = get_const_expr(ccx, def_id, ref_expr, param_substs);
     let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
     match get_const_expr_as_global(ccx, expr, check_const::ConstQualif::empty(),
                                    empty_substs, TrueConst::Yes) {
@@ -287,27 +291,26 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                           -> Result<ValueRef, ConstEvalFailure> {
     debug!("get_const_expr_as_global: {:?}", expr.id);
     // Special-case constants to cache a common global for all uses.
-    match expr.node {
-        hir::ExprPath(..) => {
-            let def = ccx.tcx().def_map.borrow().get(&expr.id).unwrap().full_def();
-            match def {
-                def::DefConst(def_id) | def::DefAssociatedConst(def_id) => {
-                    if !ccx.tcx().tables.borrow().adjustments.contains_key(&expr.id) {
-                        debug!("get_const_expr_as_global ({:?}): found const {:?}",
-                               expr.id, def_id);
-                        return get_const_val(ccx, def_id, expr);
-                    }
+    if let hir::ExprPath(..) = expr.node {
+        // `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 {
+            def::DefConst(def_id) | def::DefAssociatedConst(def_id) => {
+                if !ccx.tcx().tables.borrow().adjustments.contains_key(&expr.id) {
+                    debug!("get_const_expr_as_global ({:?}): found const {:?}",
+                           expr.id, def_id);
+                    return get_const_val(ccx, def_id, expr, param_substs);
                 }
-                _ => {}
-            }
+            },
+            _ => {},
         }
-        _ => {}
     }
 
     let key = (expr.id, param_substs);
-    match ccx.const_values().borrow().get(&key) {
-        Some(&val) => return Ok(val),
-        None => {}
+    if let Some(&val) = ccx.const_values().borrow().get(&key) {
+        return Ok(val);
     }
     let ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs,
                                               &ccx.tcx().expr_ty(expr));
@@ -316,10 +319,7 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         // references, even when only the latter are correct.
         try!(const_expr_unadjusted(ccx, expr, ty, param_substs, None, trueconst))
     } else {
-        match const_expr(ccx, expr, param_substs, None, trueconst) {
-            Err(err) => return Err(err),
-            Ok((ok, _)) => ok,
-        }
+        try!(const_expr(ccx, expr, param_substs, None, trueconst)).0
     };
 
     // boolean SSA values are i1, but they have to be stored in i8 slots,
@@ -577,9 +577,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     };
     let _icx = push_ctxt("const_expr");
     Ok(match e.node {
-        hir::ExprLit(ref lit) => {
-            const_lit(cx, e, &**lit)
-        },
+        hir::ExprLit(ref lit) => const_lit(cx, e, &**lit),
         hir::ExprBinary(b, ref e1, ref e2) => {
             /* Neither type is bottom, and we expect them to be unified
              * already, so the following is safe. */
@@ -743,7 +741,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 (CastTy::Int(IntTy::CEnum), CastTy::Int(_)) => {
                     let repr = adt::represent_type(cx, t_expr);
                     let discr = adt::const_get_discrim(cx, &*repr, v);
-                    let iv = C_integral(cx.int_type(), discr, false);
+                    let iv = C_integral(cx.int_type(), discr.0, false);
                     let s = adt::is_discr_signed(&*repr) as Bool;
                     llvm::LLVMConstIntCast(iv, llty.to_ref(), s)
                 },
@@ -810,7 +808,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         hir::ExprTup(ref es) => {
             let repr = adt::represent_type(cx, ety);
             let vals = try!(map_list(&es[..]));
-            adt::trans_const(cx, &*repr, 0, &vals[..])
+            adt::trans_const(cx, &*repr, Disr(0), &vals[..])
         },
         hir::ExprStruct(_, ref fs, ref base_opt) => {
             let repr = adt::represent_type(cx, ety);
@@ -894,14 +892,14 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val
                 }
                 def::DefConst(def_id) | def::DefAssociatedConst(def_id) => {
-                    const_deref_ptr(cx, try!(get_const_val(cx, def_id, e)))
+                    const_deref_ptr(cx, try!(get_const_val(cx, def_id, e, param_substs)))
                 }
                 def::DefVariant(enum_did, variant_did, _) => {
                     let vinfo = cx.tcx().lookup_adt_def(enum_did).variant_with_id(variant_did);
                     match vinfo.kind() {
                         ty::VariantKind::Unit => {
                             let repr = adt::represent_type(cx, ety);
-                            adt::trans_const(cx, &*repr, vinfo.disr_val, &[])
+                            adt::trans_const(cx, &*repr, Disr::from(vinfo.disr_val), &[])
                         }
                         ty::VariantKind::Tuple => {
                             expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val
@@ -955,7 +953,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                         C_vector(&arg_vals[..])
                     } else {
                         let repr = adt::represent_type(cx, ety);
-                        adt::trans_const(cx, &*repr, 0, &arg_vals[..])
+                        adt::trans_const(cx, &*repr, Disr(0), &arg_vals[..])
                     }
                 }
                 def::DefVariant(enum_did, variant_did, _) => {
@@ -963,7 +961,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     let vinfo = cx.tcx().lookup_adt_def(enum_did).variant_with_id(variant_did);
                     adt::trans_const(cx,
                                      &*repr,
-                                     vinfo.disr_val,
+                                     Disr::from(vinfo.disr_val),
                                      &arg_vals[..])
                 }
                 _ => cx.sess().span_bug(e.span, "expected a struct, variant, or const fn def"),
@@ -976,6 +974,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             try!(const_fn_call(cx, MethodCallKey(method_call),
                                method_did, &arg_vals, param_substs, trueconst))
         },
+        hir::ExprType(ref e, _) => try!(const_expr(cx, &**e, param_substs, fn_args, trueconst)).0,
         hir::ExprBlock(ref block) => {
             match block.expr {
                 Some(ref expr) => try!(const_expr(
@@ -991,8 +990,13 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         hir::ExprClosure(_, ref decl, ref body) => {
             match ety.sty {
                 ty::TyClosure(def_id, ref substs) => {
-                    closure::trans_closure_expr(closure::Dest::Ignore(cx), decl,
-                                                body, e.id, def_id, substs);
+                    closure::trans_closure_expr(closure::Dest::Ignore(cx),
+                                                decl,
+                                                body,
+                                                e.id,
+                                                def_id,
+                                                substs,
+                                                &e.attrs);
                 }
                 _ =>
                     cx.sess().span_bug(
@@ -1010,7 +1014,7 @@ pub fn trans_static(ccx: &CrateContext,
                     m: hir::Mutability,
                     expr: &hir::Expr,
                     id: ast::NodeId,
-                    attrs: &Vec<ast::Attribute>)
+                    attrs: &[ast::Attribute])
                     -> Result<ValueRef, ConstEvalErr> {
     unsafe {
         let _icx = push_ctxt("trans_static");
index 418ff4c8337e7f27dd48da0a72d0ef98c34400fd..32f263746d31e04158742f0abcf12395ad1d44c4 100644 (file)
@@ -288,20 +288,31 @@ pub fn immediate_rvalue_bcx<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     return DatumBlock::new(bcx, immediate_rvalue(val, ty))
 }
 
-
 /// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to
 /// it. The memory will be dropped upon exit from `scope`. The callback `populate` should
 /// initialize the memory.
+///
+/// The flag `zero` indicates how the temporary space itself should be
+/// initialized at the outset of the function; the only time that
+/// `InitAlloca::Uninit` is a valid value for `zero` is when the
+/// caller can prove that either (1.) the code injected by `populate`
+/// onto `bcx` always dominates the end of `scope`, or (2.) the data
+/// being allocated has no associated destructor.
 pub fn lvalue_scratch_datum<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>,
                                               ty: Ty<'tcx>,
                                               name: &str,
+                                              zero: InitAlloca,
                                               scope: cleanup::ScopeId,
                                               arg: A,
                                               populate: F)
                                               -> DatumBlock<'blk, 'tcx, Lvalue> where
     F: FnOnce(A, Block<'blk, 'tcx>, ValueRef) -> Block<'blk, 'tcx>,
 {
-    let scratch = alloc_ty(bcx, ty, name);
+    // Very subtle: potentially initialize the scratch memory at point where it is alloca'ed.
+    // (See discussion at Issue 30530.)
+    let scratch = alloc_ty_init(bcx, ty, zero, name);
+    debug!("lvalue_scratch_datum scope={:?} scratch={} ty={:?}",
+           scope, bcx.ccx().tn().val_to_string(scratch), ty);
 
     // Subtle. Populate the scratch memory *before* scheduling cleanup.
     let bcx = populate(arg, bcx, scratch);
@@ -340,6 +351,8 @@ fn add_rvalue_clean<'a, 'tcx>(mode: RvalueMode,
                               scope: cleanup::ScopeId,
                               val: ValueRef,
                               ty: Ty<'tcx>) {
+    debug!("add_rvalue_clean scope={:?} val={} ty={:?}",
+           scope, fcx.ccx.tn().val_to_string(val), ty);
     match mode {
         ByValue => { fcx.schedule_drop_immediate(scope, val, ty); }
         ByRef => {
@@ -496,9 +509,13 @@ impl<'tcx> Datum<'tcx, Rvalue> {
 
             ByValue => {
                 lvalue_scratch_datum(
-                    bcx, self.ty, name, scope, self,
+                    bcx, self.ty, name, InitAlloca::Dropped, scope, self,
                     |this, bcx, llval| {
-                        call_lifetime_start(bcx, llval);
+                        debug!("populate call for Datum::to_lvalue_datum_in_scope \
+                                self.ty={:?}", this.ty);
+                        // do not call_lifetime_start here; the
+                        // `InitAlloc::Dropped` will start scratch
+                        // value's lifetime at open of function body.
                         let bcx = this.store_to(bcx, llval);
                         bcx.fcx.schedule_lifetime_end(scope, llval);
                         bcx
index 7d88e579e5a58a9e19b8b5bfd52b9c486c71bcb8..237d31c47783d91059fbf2635af13f99eec1c69d 100644 (file)
@@ -320,6 +320,7 @@ fn walk_expr(cx: &CrateContext,
         hir::ExprPath(..) => {}
 
         hir::ExprCast(ref sub_exp, _)     |
+        hir::ExprType(ref sub_exp, _) |
         hir::ExprAddrOf(_, ref sub_exp)  |
         hir::ExprField(ref sub_exp, _) |
         hir::ExprTupField(ref sub_exp, _) =>
@@ -480,8 +481,8 @@ fn walk_expr(cx: &CrateContext,
                 walk_expr(cx, &**exp, scope_stack, scope_map);
             }
 
-            for &(_, ref exp, _) in outputs {
-                walk_expr(cx, &**exp, scope_stack, scope_map);
+            for out in outputs {
+                walk_expr(cx, &*out.expr, scope_stack, scope_map);
             }
         }
     }
index 03b58fb2c474f777415d8b5095b03ccfe7f66df8..4e3fadd0fa911baa4cdac9950a4c0c8615f9ae94 100644 (file)
@@ -11,7 +11,6 @@
 // .debug_gdb_scripts binary section.
 
 use llvm;
-use llvm::ValueRef;
 
 use trans::common::{C_bytes, CrateContext, C_i32};
 use trans::declare;
index 9eed014ac73a4c89165a4e0ded42a66629f8c343..128d0601167f30a621534eb8e1d41c4b91add0c0 100644 (file)
@@ -46,7 +46,7 @@ use std::rc::Rc;
 use syntax;
 use syntax::util::interner::Interner;
 use syntax::codemap::Span;
-use syntax::{ast, ast_util, codemap};
+use syntax::{ast, codemap};
 use syntax::parse::token;
 
 
@@ -936,13 +936,13 @@ fn basic_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         ty::TyBool => ("bool", DW_ATE_boolean),
         ty::TyChar => ("char", DW_ATE_unsigned_char),
         ty::TyInt(int_ty) => {
-            (ast_util::int_ty_to_string(int_ty), DW_ATE_signed)
+            (int_ty.ty_to_string(), DW_ATE_signed)
         },
         ty::TyUint(uint_ty) => {
-            (ast_util::uint_ty_to_string(uint_ty), DW_ATE_unsigned)
+            (uint_ty.ty_to_string(), DW_ATE_unsigned)
         },
         ty::TyFloat(float_ty) => {
-            (ast_util::float_ty_to_string(float_ty), DW_ATE_float)
+            (float_ty.ty_to_string(), DW_ATE_float)
         },
         _ => cx.sess().bug("debuginfo::basic_type_metadata - t is invalid type")
     };
@@ -990,8 +990,8 @@ pub fn compile_unit_metadata(cx: &CrateContext) -> DIDescriptor {
                 cx.sess().warn("debuginfo: Invalid path to crate's local root source file!");
                 fallback_path(cx)
             } else {
-                match abs_path.relative_from(work_dir) {
-                    Some(ref p) if p.is_relative() => {
+                match abs_path.strip_prefix(work_dir) {
+                    Ok(ref p) if p.is_relative() => {
                         if p.starts_with(Path::new("./")) {
                             path2cstr(p)
                         } else {
@@ -1020,7 +1020,7 @@ pub fn compile_unit_metadata(cx: &CrateContext) -> DIDescriptor {
             compile_unit_name,
             work_dir.as_ptr(),
             producer.as_ptr(),
-            cx.sess().opts.optimize != config::No,
+            cx.sess().opts.optimize != config::OptLevel::No,
             flags.as_ptr() as *const _,
             0,
             split_name.as_ptr() as *const _)
@@ -1341,7 +1341,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                 // DWARF representation of enums uniform.
 
                 // First create a description of the artificial wrapper struct:
-                let non_null_variant = &adt.variants[non_null_variant_index as usize];
+                let non_null_variant = &adt.variants[non_null_variant_index.0 as usize];
                 let non_null_variant_name = non_null_variant.name.as_str();
 
                 // The llvm type and metadata of the pointer
@@ -1389,7 +1389,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
 
                 // Encode the information about the null variant in the union
                 // member's name.
-                let null_variant_index = (1 - non_null_variant_index) as usize;
+                let null_variant_index = (1 - non_null_variant_index.0) as usize;
                 let null_variant_name = adt.variants[null_variant_index].name;
                 let union_member_name = format!("RUST$ENCODED$ENUM${}${}",
                                                 0,
@@ -1415,7 +1415,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                     describe_enum_variant(cx,
                                           self.enum_type,
                                           struct_def,
-                                          &adt.variants[nndiscr as usize],
+                                          &adt.variants[nndiscr.0 as usize],
                                           OptimizedDiscriminant,
                                           self.containing_scope,
                                           self.span);
@@ -1430,7 +1430,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
 
                 // Encode the information about the null variant in the union
                 // member's name.
-                let null_variant_index = (1 - nndiscr) as usize;
+                let null_variant_index = (1 - nndiscr.0) as usize;
                 let null_variant_name = adt.variants[null_variant_index].name;
                 let discrfield = discrfield.iter()
                                            .skip(1)
index 74510de3f318a772b3af533830ba3363177cfbf1..5e11a50be22735f00e2195c43a4df658e10493e5 100644 (file)
@@ -145,7 +145,7 @@ impl FunctionDebugContext {
     }
 }
 
-struct FunctionDebugContextData {
+pub struct FunctionDebugContextData {
     scope_map: RefCell<NodeMap<DIScope>>,
     fn_metadata: DISubprogram,
     argument_counter: Cell<usize>,
@@ -383,7 +383,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             true,
             scope_line as c_uint,
             FlagPrototyped as c_uint,
-            cx.sess().opts.optimize != config::No,
+            cx.sess().opts.optimize != config::OptLevel::No,
             llfn,
             template_parameters,
             ptr::null_mut())
@@ -596,7 +596,7 @@ fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                     file_metadata,
                     loc.line as c_uint,
                     type_metadata,
-                    cx.sess().opts.optimize != config::No,
+                    cx.sess().opts.optimize != config::OptLevel::No,
                     0,
                     address_operations.as_ptr(),
                     address_operations.len() as c_uint,
index 6769c010435aa6736301227711dbb4d1f1ab3509..518a78f8fd4c7752941776521e372a72eaa029d0 100644 (file)
@@ -19,7 +19,6 @@ use middle::subst::{self, Substs};
 use middle::ty::{self, Ty};
 
 use rustc_front::hir;
-use syntax::ast_util;
 
 // Compute the name of the type as it should be stored in debuginfo. Does not do
 // any caching, i.e. calling the function twice with the same type will also do
@@ -44,9 +43,9 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         ty::TyBool => output.push_str("bool"),
         ty::TyChar => output.push_str("char"),
         ty::TyStr => output.push_str("str"),
-        ty::TyInt(int_ty) => output.push_str(ast_util::int_ty_to_string(int_ty)),
-        ty::TyUint(uint_ty) => output.push_str(ast_util::uint_ty_to_string(uint_ty)),
-        ty::TyFloat(float_ty) => output.push_str(ast_util::float_ty_to_string(float_ty)),
+        ty::TyInt(int_ty) => output.push_str(int_ty.ty_to_string()),
+        ty::TyUint(uint_ty) => output.push_str(uint_ty.ty_to_string()),
+        ty::TyFloat(float_ty) => output.push_str(float_ty.ty_to_string()),
         ty::TyStruct(def, substs) |
         ty::TyEnum(def, substs) => {
             push_item_name(cx, def.did, qualified, output);
diff --git a/src/librustc_trans/trans/disr.rs b/src/librustc_trans/trans/disr.rs
new file mode 100644 (file)
index 0000000..7cb10a8
--- /dev/null
@@ -0,0 +1,49 @@
+// 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.
+
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub struct Disr(pub u64);
+
+impl Disr {
+    pub fn wrapping_add(self, other: Self) -> Self {
+        Disr(self.0.wrapping_add(other.0))
+    }
+}
+
+impl ::std::ops::BitAnd for Disr {
+    type Output = Disr;
+    fn bitand(self, other: Self) -> Self {
+        Disr(self.0 & other.0)
+    }
+}
+
+impl From<::middle::ty::Disr> for Disr {
+    fn from(i: ::middle::ty::Disr) -> Disr {
+        Disr(i)
+    }
+}
+
+impl From<usize> for Disr {
+    fn from(i: usize) -> Disr {
+        Disr(i as u64)
+    }
+}
+
+impl PartialOrd for Disr {
+    fn partial_cmp(&self, other: &Disr) -> Option<::std::cmp::Ordering> {
+        self.0.partial_cmp(&other.0)
+    }
+}
+
+impl Ord for Disr {
+    fn cmp(&self, other: &Disr) -> ::std::cmp::Ordering {
+        self.0.cmp(&other.0)
+    }
+}
index 0fb26d420f8d97e3df8b10c3df419c076a4cabf7..ab2f4462757d57529e43c414c5781e923edc6301 100644 (file)
@@ -71,6 +71,7 @@ use trans::machine;
 use trans::meth;
 use trans::tvec;
 use trans::type_of;
+use trans::Disr;
 use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer};
 use middle::ty::adjustment::{AdjustUnsafeFnPointer, CustomCoerceUnsized};
 use middle::ty::{self, Ty};
@@ -83,7 +84,7 @@ use trans::type_::Type;
 use rustc_front;
 use rustc_front::hir;
 
-use syntax::{ast, ast_util, codemap};
+use syntax::{ast, codemap};
 use syntax::parse::token::InternedString;
 use syntax::ptr::P;
 use syntax::parse::token;
@@ -165,7 +166,9 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 hir::ExprPath(..) => {
                     match bcx.def(expr.id) {
                         def::DefConst(did) => {
-                            let const_expr = consts::get_const_expr(bcx.ccx(), did, expr);
+                            let empty_substs = bcx.tcx().mk_substs(Substs::trans_empty());
+                            let const_expr = consts::get_const_expr(bcx.ccx(), did, expr,
+                                                                    empty_substs);
                             // Temporarily get cleanup scopes out of the way,
                             // as they require sub-expressions to be contained
                             // inside the current AST scope.
@@ -547,8 +550,8 @@ fn coerce_unsized<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
             let iter = src_fields.iter().zip(target_fields).enumerate();
             for (i, (src_ty, target_ty)) in iter {
-                let ll_source = adt::trans_field_ptr(bcx, &repr_source, source_val, 0, i);
-                let ll_target = adt::trans_field_ptr(bcx, &repr_target, target_val, 0, i);
+                let ll_source = adt::trans_field_ptr(bcx, &repr_source, source_val, Disr(0), i);
+                let ll_target = adt::trans_field_ptr(bcx, &repr_target, target_val, Disr(0), i);
 
                 // If this is the field we need to coerce, recurse on it.
                 if i == coerce_index {
@@ -656,6 +659,9 @@ fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let _icx = push_ctxt("trans_datum_unadjusted");
 
     match expr.node {
+        hir::ExprType(ref e, _) => {
+            trans(bcx, &**e)
+        }
         hir::ExprPath(..) => {
             trans_def(bcx, expr, bcx.def(expr.id))
         }
@@ -941,6 +947,9 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         hir::ExprBreak(label_opt) => {
             controlflow::trans_break(bcx, expr, label_opt.map(|l| l.node.name))
         }
+        hir::ExprType(ref e, _) => {
+            trans_into(bcx, &**e, Ignore)
+        }
         hir::ExprAgain(label_opt) => {
             controlflow::trans_cont(bcx, expr, label_opt.map(|l| l.node.name))
         }
@@ -1064,6 +1073,9 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
 
     match expr.node {
+        hir::ExprType(ref e, _) => {
+            trans_into(bcx, &**e, dest)
+        }
         hir::ExprPath(..) => {
             trans_def_dps_unadjusted(bcx, expr, bcx.def(expr.id), dest)
         }
@@ -1143,7 +1155,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 args.iter().enumerate().map(|(i, arg)| (i, &**arg)).collect();
             trans_adt(bcx,
                       expr_ty(bcx, expr),
-                      0,
+                      Disr(0),
                       &numbered_fields[..],
                       None,
                       dest,
@@ -1187,7 +1199,13 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                         &format!("closure expr without closure type: {:?}", t)),
             };
 
-            closure::trans_closure_expr(dest, decl, body, expr.id, def_id, substs).unwrap_or(bcx)
+            closure::trans_closure_expr(dest,
+                                        decl,
+                                        body,
+                                        expr.id,
+                                        def_id,
+                                        substs,
+                                        &expr.attrs).unwrap_or(bcx)
         }
         hir::ExprCall(ref f, ref args) => {
             if bcx.tcx().is_method_call(expr.id) {
@@ -1278,7 +1296,7 @@ fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 // Nullary variant.
                 let ty = expr_ty(bcx, ref_expr);
                 let repr = adt::represent_type(bcx.ccx(), ty);
-                adt::trans_set_discr(bcx, &*repr, lldest, variant.disr_val);
+                adt::trans_set_discr(bcx, &*repr, lldest, Disr::from(variant.disr_val));
                 return bcx;
             }
         }
@@ -1287,7 +1305,7 @@ fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             match ty.sty {
                 ty::TyStruct(def, _) if def.has_dtor() => {
                     let repr = adt::represent_type(bcx.ccx(), ty);
-                    adt::trans_set_discr(bcx, &*repr, lldest, 0);
+                    adt::trans_set_discr(bcx, &*repr, lldest, Disr(0));
                 }
                 _ => {}
             }
@@ -1449,7 +1467,7 @@ pub struct StructBaseInfo<'a, 'tcx> {
 /// which remaining fields are copied; see comments on `StructBaseInfo`.
 pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                                  ty: Ty<'tcx>,
-                                 discr: ty::Disr,
+                                 discr: Disr,
                                  fields: &[(usize, &hir::Expr)],
                                  optbase: Option<StructBaseInfo<'a, 'tcx>>,
                                  dest: Dest,
@@ -1472,6 +1490,8 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         }
     };
 
+    debug!("trans_adt");
+
     // This scope holds intermediates that must be cleaned should
     // panic occur before the ADT as a whole is ready.
     let custom_cleanup_scope = fcx.push_custom_cleanup_scope();
@@ -1515,7 +1535,7 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         debug_location.apply(bcx.fcx);
 
         // Second, trans the base to the dest.
-        assert_eq!(discr, 0);
+        assert_eq!(discr, Disr(0));
 
         let addr = adt::MaybeSizedValue::sized(addr);
         match expr_kind(bcx.tcx(), &*base.expr) {
@@ -2178,15 +2198,19 @@ fn auto_ref<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let referent_ty = lv_datum.ty;
     let ptr_ty = bcx.tcx().mk_imm_ref(bcx.tcx().mk_region(ty::ReStatic), 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
+    // we microoptimize it to be an Rvalue datum to avoid the extra alloca and level of
+    // indirection and for thin pointers, this has no ill effects.
+    let kind  = if type_is_sized(bcx.tcx(), referent_ty) {
+        RvalueExpr(Rvalue::new(ByValue))
+    } else {
+        LvalueExpr(lv_datum.kind)
+    };
+
     // Get the pointer.
     let llref = lv_datum.to_llref();
-
-    // Construct the resulting datum, using what was the "by ref"
-    // ValueRef of type `referent_ty` to be the "by value" ValueRef
-    // of type `&referent_ty`.
-    // Pointers to DST types are non-immediate, and therefore still use ByRef.
-    let kind  = if type_is_sized(bcx.tcx(), referent_ty) { ByValue } else { ByRef };
-    DatumBlock::new(bcx, Datum::new(llref, ptr_ty, RvalueExpr(Rvalue::new(kind))))
+    DatumBlock::new(bcx, Datum::new(llref, ptr_ty, kind))
 }
 
 fn deref_multiple<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
@@ -2601,6 +2625,10 @@ fn expr_kind(tcx: &ty::ctxt, expr: &hir::Expr) -> ExprKind {
             }
         }
 
+        hir::ExprType(ref expr, _) => {
+            expr_kind(tcx, expr)
+        }
+
         hir::ExprUnary(hir::UnDeref, _) |
         hir::ExprField(..) |
         hir::ExprTupField(..) |
@@ -2622,7 +2650,7 @@ fn expr_kind(tcx: &ty::ctxt, expr: &hir::Expr) -> ExprKind {
             ExprKind::RvalueDps
         }
 
-        hir::ExprLit(ref lit) if ast_util::lit_is_str(&**lit) => {
+        hir::ExprLit(ref lit) if lit.node.is_str() => {
             ExprKind::RvalueDps
         }
 
index 9012ecaa2134fe5cde58e5e2dc7edc5fc4b55c6c..b1f62477bb7672ef059e655f98ba78eaf1c9bfa4 100644 (file)
@@ -35,7 +35,8 @@ use std::cmp;
 use std::iter::once;
 use libc::c_uint;
 use syntax::abi::{Cdecl, Aapcs, C, Win64, Abi};
-use syntax::abi::{PlatformIntrinsic, RustIntrinsic, Rust, RustCall, Stdcall, Fastcall, System};
+use syntax::abi::{PlatformIntrinsic, RustIntrinsic, Rust, RustCall, Stdcall};
+use syntax::abi::{Fastcall, Vectorcall, System};
 use syntax::attr;
 use syntax::codemap::Span;
 use syntax::parse::token::{InternedString, special_idents};
@@ -104,6 +105,7 @@ pub fn llvm_calling_convention(ccx: &CrateContext,
 
         Stdcall => llvm::X86StdcallCallConv,
         Fastcall => llvm::X86FastcallCallConv,
+        Vectorcall => llvm::X86_VectorCall,
         C => llvm::CCallConv,
         Win64 => llvm::X86_64_Win64,
 
@@ -460,12 +462,13 @@ fn gate_simd_ffi(tcx: &ty::ctxt, decl: &hir::FnDecl, ty: &ty::BareFnTy) {
     if !tcx.sess.features.borrow().simd_ffi {
         let check = |ast_ty: &hir::Ty, ty: ty::Ty| {
             if ty.is_simd() {
-                tcx.sess.span_err(ast_ty.span,
+                tcx.sess.struct_span_err(ast_ty.span,
                               &format!("use of SIMD type `{}` in FFI is highly experimental and \
                                         may result in invalid code",
-                                       pprust::ty_to_string(ast_ty)));
-                tcx.sess.fileline_help(ast_ty.span,
-                                   "add #![feature(simd_ffi)] to the crate attributes to enable");
+                                       pprust::ty_to_string(ast_ty)))
+                    .fileline_help(ast_ty.span,
+                                   "add #![feature(simd_ffi)] to the crate attributes to enable")
+                    .emit();
             }
         };
         let sig = &ty.sig.0;
@@ -639,7 +642,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
         let llfn = declare::define_internal_rust_fn(ccx, &ps, t);
         attributes::from_fn_attrs(ccx, attrs, llfn);
-        base::trans_fn(ccx, decl, body, llfn, param_substs, id, &[]);
+        base::trans_fn(ccx, decl, body, llfn, param_substs, id, attrs);
         llfn
     }
 
index 3fc9bc0d66d3d48d7834cf6ea4807f03adfd1be9..a1165ffe171d06673d736b518e57b8a557774469 100644 (file)
@@ -88,6 +88,10 @@ pub fn trans_exchange_free_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     }
 }
 
+fn type_needs_drop<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
+    tcx.type_needs_drop_given_env(ty, &tcx.empty_parameter_environment())
+}
+
 pub fn get_drop_glue_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                     t: Ty<'tcx>) -> Ty<'tcx> {
     let tcx = ccx.tcx();
@@ -106,11 +110,11 @@ pub fn get_drop_glue_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     // returned `tcx.types.i8` does not appear unsound. The impact on
     // code quality is unknown at this time.)
 
-    if !type_needs_drop(tcx, t) {
+    if !type_needs_drop(&tcx, t) {
         return tcx.types.i8;
     }
     match t.sty {
-        ty::TyBox(typ) if !type_needs_drop(tcx, typ)
+        ty::TyBox(typ) if !type_needs_drop(&tcx, typ)
                          && type_is_sized(tcx, typ) => {
             let llty = sizing_type_of(ccx, typ);
             // `Box<ZeroSizeType>` does not allocate.
index 29965755eac76c76cd7dd6257f14c154de96e7c8..baf244c2e7960613d3dc11ce077e35b3cd860d55 100644 (file)
@@ -15,12 +15,13 @@ use middle::subst::Substs;
 use trans::base::{push_ctxt, trans_item, get_item_val, trans_fn};
 use trans::common::*;
 
+use rustc::dep_graph::DepNode;
 use rustc_front::hir;
 
-fn instantiate_inline(ccx: &CrateContext, fn_id: DefId)
-    -> Option<DefId> {
+fn instantiate_inline(ccx: &CrateContext, fn_id: DefId) -> Option<DefId> {
     debug!("instantiate_inline({:?})", fn_id);
     let _icx = push_ctxt("instantiate_inline");
+    let _task = ccx.tcx().dep_graph.in_task(DepNode::TransInlinedItem(fn_id));
 
     match ccx.external().borrow().get(&fn_id) {
         Some(&Some(node_id)) => {
@@ -165,7 +166,7 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: DefId)
                              llfn,
                              empty_substs,
                              impl_item.id,
-                             &[]);
+                             &impl_item.attrs);
                     // See linkage comments on items.
                     if ccx.sess().opts.cg.codegen_units == 1 {
                         SetLinkage(llfn, InternalLinkage);
index 27f5e31eaaf7cc3e7070f580dbe9963b1eb7292a..0c9b076cb650a4c8873313696ed1090e4603038d 100644 (file)
@@ -14,7 +14,7 @@ use arena::TypedArena;
 use intrinsics::{self, Intrinsic};
 use libc;
 use llvm;
-use llvm::{SequentiallyConsistent, Acquire, Release, AtomicXchg, ValueRef, TypeKind};
+use llvm::{ValueRef, TypeKind};
 use middle::infer;
 use middle::subst;
 use middle::subst::FnSpace;
@@ -32,13 +32,13 @@ use trans::debuginfo::DebugLoc;
 use trans::declare;
 use trans::expr;
 use trans::glue;
-use trans::type_of::*;
 use trans::type_of;
 use trans::machine;
-use trans::machine::llsize_of;
 use trans::type_::Type;
-use middle::ty::{self, Ty, HasTypeFlags};
+use middle::ty::{self, Ty, TypeFoldable};
+use trans::Disr;
 use middle::subst::Substs;
+use rustc::dep_graph::DepNode;
 use rustc_front::hir;
 use syntax::abi::{self, RustIntrinsic};
 use syntax::ast;
@@ -103,6 +103,7 @@ pub fn span_transmute_size_error(a: &Session, b: Span, msg: &str) {
 /// Performs late verification that intrinsics are used correctly. At present,
 /// the only intrinsic that needs such verification is `transmute`.
 pub fn check_intrinsics(ccx: &CrateContext) {
+    let _task = ccx.tcx().dep_graph.in_task(DepNode::IntrinsicUseCheck);
     let mut last_failing_id = None;
     for transmute_restriction in ccx.tcx().transmute_restrictions.borrow().iter() {
         // Sometimes, a single call to transmute will push multiple
@@ -848,7 +849,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                         let arg = adt::MaybeSizedValue::sized(llarg);
                         (0..contents.len())
                             .map(|i| {
-                                Load(bcx, adt::trans_field_ptr(bcx, repr_ptr, arg, 0, i))
+                                Load(bcx, adt::trans_field_ptr(bcx, repr_ptr, arg, Disr(0), i))
                             })
                             .collect()
                     }
index 123bd9f9c37653864cb05c3fcfbe1572f5a72faa..bd12dd8c3effcc9ad49ad5185daa835fd8e555c1 100644 (file)
@@ -28,14 +28,13 @@ use trans::consts;
 use trans::datum::*;
 use trans::debuginfo::DebugLoc;
 use trans::declare;
-use trans::expr::SaveIn;
 use trans::expr;
 use trans::glue;
 use trans::machine;
 use trans::monomorphize;
 use trans::type_::Type;
 use trans::type_of::*;
-use middle::ty::{self, Ty, HasTypeFlags};
+use middle::ty::{self, Ty, TypeFoldable};
 use middle::ty::MethodCall;
 
 use syntax::ast;
@@ -75,8 +74,13 @@ pub fn trans_impl(ccx: &CrateContext,
                     for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) {
                         let llfn = get_item_val(ccx, impl_item.id);
                         let empty_substs = tcx.mk_substs(Substs::trans_empty());
-                        trans_fn(ccx, &sig.decl, body, llfn,
-                                 empty_substs, impl_item.id, &[]);
+                        trans_fn(ccx,
+                                 &sig.decl,
+                                 body,
+                                 llfn,
+                                 empty_substs,
+                                 impl_item.id,
+                                 &impl_item.attrs);
                         update_linkage(ccx,
                                        llfn,
                                        Some(impl_item.id),
@@ -473,7 +477,7 @@ fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 ///
 /// In fact, all virtual calls can be thought of as normal trait calls
 /// that go through this shim function.
-fn trans_object_shim<'a, 'tcx>(
+pub fn trans_object_shim<'a, 'tcx>(
     ccx: &'a CrateContext<'a, 'tcx>,
     upcast_trait_ref: ty::PolyTraitRef<'tcx>,
     method_id: DefId,
index 3ce08fb2f60ef76126b87c1cdb9d5f0dc5f466fc..852f08a0d343c84b8f295c67c7be840eddc16ecf 100644 (file)
@@ -8,14 +8,23 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use llvm::BasicBlockRef;
+use llvm::{BasicBlockRef, ValueRef};
+use rustc::middle::ty;
 use rustc::mir::repr as mir;
+use syntax::abi::Abi;
+use trans::adt;
+use trans::attributes;
 use trans::base;
 use trans::build;
-use trans::common::Block;
+use trans::common::{self, Block};
 use trans::debuginfo::DebugLoc;
+use trans::foreign;
+use trans::type_of;
+use trans::type_::Type;
+use trans::Disr;
 
 use super::MirContext;
+use super::operand::OperandValue::{FatPtr, Immediate, Ref};
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn trans_block(&mut self, bb: mir::BasicBlock) {
@@ -28,26 +37,37 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             bcx = self.trans_statement(bcx, statement);
         }
 
-        debug!("trans_block: terminator: {:?}", data.terminator);
+        debug!("trans_block: terminator: {:?}", data.terminator());
 
-        match data.terminator {
+        match *data.terminator() {
             mir::Terminator::Goto { target } => {
                 build::Br(bcx, self.llblock(target), DebugLoc::None)
             }
 
-            mir::Terminator::Panic { .. } => {
-                unimplemented!()
-            }
-
-            mir::Terminator::If { ref cond, targets: [true_bb, false_bb] } => {
+            mir::Terminator::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);
                 build::CondBr(bcx, cond.immediate(), lltrue, llfalse, DebugLoc::None);
             }
 
-            mir::Terminator::Switch { .. } => {
-                unimplemented!()
+            mir::Terminator::Switch { ref discr, ref adt_def, ref targets } => {
+                let discr_lvalue = self.trans_lvalue(bcx, discr);
+                let ty = discr_lvalue.ty.to_ty(bcx.tcx());
+                let repr = adt::represent_type(bcx.ccx(), ty);
+                let discr = adt::trans_get_discr(bcx, &repr, discr_lvalue.llval, None);
+
+                // The else branch of the Switch can't be hit, so branch to an unreachable
+                // instruction so LLVM knows that
+                let unreachable_blk = self.unreachable_block();
+                let switch = build::Switch(bcx, discr, unreachable_blk.llbb, targets.len());
+                assert_eq!(adt_def.variants.len(), targets.len());
+                for (adt_variant, target) in adt_def.variants.iter().zip(targets) {
+                    let llval = adt::trans_case(bcx, &*repr, Disr::from(adt_variant.disr_val));
+                    let llbb = self.llblock(*target);
+
+                    build::AddCase(switch, llval, llbb)
+                }
             }
 
             mir::Terminator::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
@@ -61,18 +81,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 }
             }
 
-            mir::Terminator::Diverge => {
-                if let Some(llpersonalityslot) = self.llpersonalityslot {
-                    let lp = build::Load(bcx, llpersonalityslot);
-                    // FIXME(lifetime) base::call_lifetime_end(bcx, self.personality);
-                    build::Resume(bcx, lp);
-                } else {
-                    // This fn never encountered anything fallible, so
-                    // a Diverge cannot actually happen. Note that we
-                    // do a total hack to ensure that we visit the
-                    // DIVERGE block last.
-                    build::Unreachable(bcx);
-                }
+            mir::Terminator::Resume => {
+                let ps = self.get_personality_slot(bcx);
+                let lp = build::Load(bcx, ps);
+                base::call_lifetime_end(bcx, ps);
+                base::trans_unwind_resume(bcx, lp);
             }
 
             mir::Terminator::Return => {
@@ -80,29 +93,199 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None);
             }
 
-            mir::Terminator::Call { .. } => {
-                unimplemented!()
-                //let llbb = unimplemented!(); // self.make_landing_pad(panic_bb);
-                //
-                //let tr_dest = self.trans_lvalue(bcx, &data.destination);
-                //
-                //// Create the callee. This will always be a fn
-                //// ptr and hence a kind of scalar.
-                //let callee = self.trans_operand(bcx, &data.func);
-                //
-                //// Process the arguments.
-                //
-                //let args = unimplemented!();
-                //
-                //callee::trans_call_inner(bcx,
-                //                         DebugLoc::None,
-                //                         |bcx, _| Callee {
-                //                             bcx: bcx,
-                //                             data: CalleeData::Fn(callee.llval),
-                //                             ty: callee.ty,
-                //                         },
-                //                         args,
-                //                         Some(Dest::SaveIn(tr_dest.llval)));
+            mir::Terminator::Call { ref func, ref args, ref kind } => {
+                // Create the callee. This will always be a fn ptr and hence a kind of scalar.
+                let callee = self.trans_operand(bcx, func);
+                let attrs = attributes::from_fn_type(bcx.ccx(), callee.ty);
+                let debugloc = DebugLoc::None;
+                // The arguments we'll be passing. Plus one to account for outptr, if used.
+                let mut llargs = Vec::with_capacity(args.len() + 1);
+                // Types of the arguments. We do not preallocate, because this vector is only
+                // filled when `is_foreign` is `true` and foreign calls are minority of the cases.
+                let mut arg_tys = Vec::new();
+
+                // Foreign-ABI functions are translated differently
+                let is_foreign = if let ty::TyBareFn(_, ref f) = callee.ty.sty {
+                    // We do not translate intrinsics here (they shouldn’t be functions)
+                    assert!(f.abi != Abi::RustIntrinsic && f.abi != Abi::PlatformIntrinsic);
+                    f.abi != Abi::Rust && f.abi != Abi::RustCall
+                } else {
+                    false
+                };
+
+                // Prepare the return value destination
+                let (ret_dest_ty, must_copy_dest) = if let Some(d) = kind.destination() {
+                    let dest = self.trans_lvalue(bcx, d);
+                    let ret_ty = dest.ty.to_ty(bcx.tcx());
+                    if !is_foreign && type_of::return_uses_outptr(bcx.ccx(), ret_ty) {
+                        llargs.push(dest.llval);
+                        (Some((dest, ret_ty)), false)
+                    } else {
+                        (Some((dest, ret_ty)), !common::type_is_zero_size(bcx.ccx(), ret_ty))
+                    }
+                } else {
+                    (None, false)
+                };
+
+                // Process the rest of the args.
+                for arg in args {
+                    let operand = self.trans_operand(bcx, arg);
+                    match operand.val {
+                        Ref(llval) | Immediate(llval) => llargs.push(llval),
+                        FatPtr(b, e) => {
+                            llargs.push(b);
+                            llargs.push(e);
+                        }
+                    }
+                    if is_foreign {
+                        arg_tys.push(operand.ty);
+                    }
+                }
+
+                // Many different ways to call a function handled here
+                match (is_foreign, base::avoid_invoke(bcx), kind) {
+                    // The two cases below are the only ones to use LLVM’s `invoke`.
+                    (false, false, &mir::CallKind::DivergingCleanup(cleanup)) => {
+                        let cleanup = self.bcx(cleanup);
+                        let landingpad = self.make_landing_pad(cleanup);
+                        let unreachable_blk = self.unreachable_block();
+                        build::Invoke(bcx,
+                                      callee.immediate(),
+                                      &llargs[..],
+                                      unreachable_blk.llbb,
+                                      landingpad.llbb,
+                                      Some(attrs),
+                                      debugloc);
+                    },
+                    (false, false, &mir::CallKind::ConvergingCleanup { ref targets, .. }) => {
+                        let cleanup = self.bcx(targets.1);
+                        let landingpad = self.make_landing_pad(cleanup);
+                        let (target, postinvoke) = if must_copy_dest {
+                            (bcx.fcx.new_block(false, "", None), Some(self.bcx(targets.0)))
+                        } else {
+                            (self.bcx(targets.0), None)
+                        };
+                        let invokeret = build::Invoke(bcx,
+                                                      callee.immediate(),
+                                                      &llargs[..],
+                                                      target.llbb,
+                                                      landingpad.llbb,
+                                                      Some(attrs),
+                                                      debugloc);
+                        if let Some(postinvoketarget) = postinvoke {
+                            // We translate the copy into a temoprary block. The temporary block is
+                            // necessary because the current block has already been terminated (by
+                            // `invoke`) and we cannot really translate into the target block
+                            // because:
+                            //  * The target block may have more than a single precedesor;
+                            //  * Some LLVM insns cannot have a preceeding store insn (phi,
+                            //    cleanuppad), and adding/prepending the store now may render
+                            //    those other instructions invalid.
+                            //
+                            // NB: This approach still may break some LLVM code. For example if the
+                            // target block starts with a `phi` (which may only match on immediate
+                            // precedesors), it cannot know about this temporary block thus
+                            // resulting in an invalid code:
+                            //
+                            // this:
+                            //     …
+                            //     %0 = …
+                            //     %1 = invoke to label %temp …
+                            // temp:
+                            //     store ty %1, ty* %dest
+                            //     br label %actualtargetblock
+                            // actualtargetblock:            ; preds: %temp, …
+                            //     phi … [%this, …], [%0, …] ; ERROR: phi requires to match only on
+                            //                               ; immediate precedesors
+                            let (ret_dest, ret_ty) = ret_dest_ty
+                                .expect("return destination and type not set");
+                            base::store_ty(target, invokeret, ret_dest.llval, ret_ty);
+                            build::Br(target, postinvoketarget.llbb, debugloc);
+                        }
+                    },
+                    (false, _, &mir::CallKind::DivergingCleanup(_)) |
+                    (false, _, &mir::CallKind::Diverging) => {
+                        build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc);
+                        build::Unreachable(bcx);
+                    }
+                    (false, _, k@&mir::CallKind::ConvergingCleanup { .. }) |
+                    (false, _, k@&mir::CallKind::Converging { .. }) => {
+                        // FIXME: Bug #20046
+                        let target = match *k {
+                            mir::CallKind::ConvergingCleanup { targets, .. } => targets.0,
+                            mir::CallKind::Converging { target, .. } => target,
+                            _ => unreachable!()
+                        };
+                        let llret = build::Call(bcx,
+                                                callee.immediate(),
+                                                &llargs[..],
+                                                Some(attrs),
+                                                debugloc);
+                        if must_copy_dest {
+                            let (ret_dest, ret_ty) = ret_dest_ty
+                                .expect("return destination and type not set");
+                            base::store_ty(bcx, llret, ret_dest.llval, ret_ty);
+                        }
+                        build::Br(bcx, self.llblock(target), debugloc);
+                    }
+                    // Foreign functions
+                    (true, _, k) => {
+                        let (dest, _) = ret_dest_ty
+                            .expect("return destination is not set");
+                        bcx = foreign::trans_native_call(bcx,
+                                                   callee.ty,
+                                                   callee.immediate(),
+                                                   dest.llval,
+                                                   &llargs[..],
+                                                   arg_tys,
+                                                   debugloc);
+                        match *k {
+                            mir::CallKind::ConvergingCleanup { targets, .. } =>
+                                build::Br(bcx, self.llblock(targets.0), debugloc),
+                            mir::CallKind::Converging { target, .. } =>
+                                build::Br(bcx, self.llblock(target), debugloc),
+                            _ => ()
+                        };
+                    },
+                }
+            }
+        }
+    }
+
+    fn get_personality_slot(&mut self, bcx: Block<'bcx, 'tcx>) -> ValueRef {
+        let ccx = bcx.ccx();
+        if let Some(slot) = self.llpersonalityslot {
+            slot
+        } else {
+            let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false);
+            let slot = base::alloca(bcx, llretty, "personalityslot");
+            self.llpersonalityslot = Some(slot);
+            base::call_lifetime_start(bcx, slot);
+            slot
+        }
+    }
+
+    fn make_landing_pad(&mut self, cleanup: Block<'bcx, 'tcx>) -> Block<'bcx, 'tcx> {
+        let bcx = cleanup.fcx.new_block(true, "cleanup", None);
+        let ccx = bcx.ccx();
+        let llpersonality = bcx.fcx.eh_personality();
+        let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false);
+        let llretval = build::LandingPad(bcx, llretty, llpersonality, 1);
+        build::SetCleanup(bcx, llretval);
+        let slot = self.get_personality_slot(bcx);
+        build::Store(bcx, llretval, slot);
+        build::Br(bcx, cleanup.llbb, DebugLoc::None);
+        bcx
+    }
+
+    fn unreachable_block(&mut self) -> Block<'bcx, 'tcx> {
+        match self.unreachable_block {
+            Some(b) => b,
+            None => {
+                let bl = self.fcx.new_block(false, "unreachable", None);
+                build::Unreachable(bl);
+                self.unreachable_block = Some(bl);
+                bl
             }
         }
     }
index 4de586543e9df6893ba65467d236994f2b53f108..84cc87e9b13850000069d9a0dd769a0579188a1e 100644 (file)
@@ -8,17 +8,21 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use middle::ty::{Ty, HasTypeFlags};
+use back::abi;
+use llvm::ValueRef;
+use middle::subst::Substs;
+use middle::ty::{Ty, TypeFoldable};
 use rustc::middle::const_eval::ConstVal;
 use rustc::mir::repr as mir;
-use trans::consts::{self, TrueConst};
-use trans::common::{self, Block};
-use trans::common::{C_bool, C_bytes, C_floating_f64, C_integral, C_str_slice};
+use trans::common::{self, Block, C_bool, C_bytes, C_floating_f64, C_integral, C_str_slice};
+use trans::consts;
+use trans::expr;
 use trans::type_of;
 
-use super::operand::OperandRef;
+use super::operand::{OperandRef, OperandValue};
 use super::MirContext;
 
+
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn trans_constval(&mut self,
                           bcx: Block<'bcx, 'tcx>,
@@ -26,48 +30,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                           ty: Ty<'tcx>)
                           -> OperandRef<'tcx>
     {
-        use super::operand::OperandValue::{Ref, Immediate};
-
         let ccx = bcx.ccx();
-        let llty = type_of::type_of(ccx, ty);
-        let val = match *cv {
-            ConstVal::Float(v) => Immediate(C_floating_f64(v, llty)),
-            ConstVal::Bool(v) => Immediate(C_bool(ccx, v)),
-            ConstVal::Int(v) => Immediate(C_integral(llty, v as u64, true)),
-            ConstVal::Uint(v) => Immediate(C_integral(llty, v, false)),
-            ConstVal::Str(ref v) => Immediate(C_str_slice(ccx, v.clone())),
-            ConstVal::ByteStr(ref v) => {
-                Immediate(consts::addr_of(ccx,
-                                          C_bytes(ccx, v),
-                                          1,
-                                          "byte_str"))
-            }
-
-            ConstVal::Struct(id) | ConstVal::Tuple(id) => {
-                let expr = bcx.tcx().map.expect_expr(id);
-                let (llval, _) = match consts::const_expr(ccx,
-                                                          expr,
-                                                          bcx.fcx.param_substs,
-                                                          None,
-                                                          TrueConst::Yes) {
-                    Ok(v) => v,
-                    Err(_) => panic!("constant eval failure"),
-                };
-                if common::type_is_immediate(bcx.ccx(), ty) {
-                    Immediate(llval)
-                } else {
-                    Ref(llval)
-                }
-            }
-            ConstVal::Function(_) => {
-                unimplemented!()
-            }
-            ConstVal::Array(..) => {
-                unimplemented!()
-            }
-            ConstVal::Repeat(..) => {
-                unimplemented!()
-            }
+        let val = self.trans_constval_inner(bcx, cv, ty, bcx.fcx.param_substs);
+        let val = if common::type_is_immediate(ccx, ty) {
+            OperandValue::Immediate(val)
+        } else if common::type_is_fat_ptr(bcx.tcx(), ty) {
+            let data = common::const_get_elt(ccx, val, &[abi::FAT_PTR_ADDR as u32]);
+            let extra = common::const_get_elt(ccx, val, &[abi::FAT_PTR_EXTRA as u32]);
+            OperandValue::FatPtr(data, extra)
+        } else {
+            OperandValue::Ref(val)
         };
 
         assert!(!ty.has_erasable_regions());
@@ -78,18 +50,46 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         }
     }
 
+    /// Translate ConstVal into a bare LLVM ValueRef.
+    fn trans_constval_inner(&mut self,
+                            bcx: common::Block<'bcx, 'tcx>,
+                            cv: &ConstVal,
+                            ty: Ty<'tcx>,
+                            param_substs: &'tcx Substs<'tcx>)
+                            -> ValueRef
+    {
+        let ccx = bcx.ccx();
+        let llty = type_of::type_of(ccx, ty);
+        match *cv {
+            ConstVal::Float(v) => C_floating_f64(v, llty),
+            ConstVal::Bool(v) => C_bool(ccx, v),
+            ConstVal::Int(v) => C_integral(llty, v as u64, true),
+            ConstVal::Uint(v) => C_integral(llty, v, false),
+            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(id) | ConstVal::Tuple(id) |
+            ConstVal::Array(id, _) | ConstVal::Repeat(id, _) => {
+                let expr = bcx.tcx().map.expect_expr(id);
+                expr::trans(bcx, expr).datum.val
+            },
+            ConstVal::Function(did) =>
+                self.trans_fn_ref(bcx, ty, param_substs, did).immediate()
+        }
+    }
+
     pub fn trans_constant(&mut self,
                           bcx: Block<'bcx, 'tcx>,
                           constant: &mir::Constant<'tcx>)
                           -> OperandRef<'tcx>
     {
-        let constant_ty = bcx.monomorphize(&constant.ty);
+        let ty = bcx.monomorphize(&constant.ty);
         match constant.literal {
-            mir::Literal::Item { .. } => {
-                unimplemented!()
+            mir::Literal::Item { def_id, kind, substs } => {
+                let substs = bcx.tcx().mk_substs(bcx.monomorphize(&substs));
+                self.trans_item_ref(bcx, ty, kind, substs, def_id)
             }
             mir::Literal::Value { ref value } => {
-                self.trans_constval(bcx, value, constant_ty)
+                self.trans_constval(bcx, value, ty)
             }
         }
     }
diff --git a/src/librustc_trans/trans/mir/did.rs b/src/librustc_trans/trans/mir/did.rs
new file mode 100644 (file)
index 0000000..3238869
--- /dev/null
@@ -0,0 +1,184 @@
+// 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.
+
+//! Code for translating references to other items (DefIds).
+
+use syntax::codemap::DUMMY_SP;
+use rustc::front::map;
+use rustc::middle::ty::{self, Ty, TypeFoldable};
+use rustc::middle::subst::Substs;
+use rustc::middle::const_eval;
+use rustc::middle::def_id::DefId;
+use rustc::middle::subst;
+use rustc::middle::traits;
+use rustc::mir::repr::ItemKind;
+use trans::common::{Block, fulfill_obligation};
+use trans::base;
+use trans::closure;
+use trans::expr;
+use trans::monomorphize;
+use trans::meth;
+use trans::inline;
+
+use super::MirContext;
+use super::operand::{OperandRef, OperandValue};
+
+impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
+    /// Translate reference to item.
+    pub fn trans_item_ref(&mut self,
+                          bcx: Block<'bcx, 'tcx>,
+                          ty: Ty<'tcx>,
+                          kind: ItemKind,
+                          substs: &'tcx Substs<'tcx>,
+                          did: DefId)
+                          -> OperandRef<'tcx> {
+        debug!("trans_item_ref(ty={:?}, kind={:?}, substs={:?}, did={})",
+            ty, kind, substs, bcx.tcx().item_path_str(did));
+
+        match kind {
+            ItemKind::Function => self.trans_fn_ref(bcx, ty, substs, did),
+            ItemKind::Method => match bcx.tcx().impl_or_trait_item(did).container() {
+                ty::ImplContainer(_) => self.trans_fn_ref(bcx, ty, substs, did),
+                ty::TraitContainer(tdid) => self.trans_static_method(bcx, ty, did, tdid, substs)
+            },
+            ItemKind::Constant => {
+                let did = inline::maybe_instantiate_inline(bcx.ccx(), did);
+                let expr = const_eval::lookup_const_by_id(bcx.tcx(), did, None, Some(substs))
+                            .expect("def was const, but lookup_const_by_id failed");
+                // FIXME: this is falling back to translating from HIR. This is not easy to fix,
+                // because we would have somehow adapt const_eval to work on MIR rather than HIR.
+                let d = expr::trans(bcx, expr);
+                OperandRef::from_rvalue_datum(d.datum.to_rvalue_datum(d.bcx, "").datum)
+            }
+        }
+    }
+
+    /// Translates references to a function-like items.
+    ///
+    /// That includes regular functions, non-static methods, struct and enum variant constructors,
+    /// closures and possibly more.
+    ///
+    /// This is an adaptation of callee::trans_fn_ref_with_substs.
+    pub fn trans_fn_ref(&mut self,
+                        bcx: Block<'bcx, 'tcx>,
+                        ty: Ty<'tcx>,
+                        substs: &'tcx Substs<'tcx>,
+                        did: DefId)
+                        -> OperandRef<'tcx> {
+        debug!("trans_fn_ref(ty={:?}, substs={:?}, did={})",
+            ty, substs, bcx.tcx().item_path_str(did));
+
+        let did = inline::maybe_instantiate_inline(bcx.ccx(), did);
+
+        if !substs.types.is_empty() || is_named_tuple_constructor(bcx.tcx(), did) {
+            let (val, fn_ty, _) = monomorphize::monomorphic_fn(bcx.ccx(), did, substs, None);
+            // FIXME: cast fnptr to proper type if necessary
+            OperandRef {
+                ty: fn_ty,
+                val: OperandValue::Immediate(val)
+            }
+        } else {
+            let val = if let Some(node_id) = bcx.tcx().map.as_local_node_id(did) {
+                base::get_item_val(bcx.ccx(), node_id)
+            } else {
+                base::trans_external_path(bcx.ccx(), did, ty)
+            };
+            // FIXME: cast fnptr to proper type if necessary
+            OperandRef {
+                ty: ty,
+                val: OperandValue::Immediate(val)
+            }
+        }
+    }
+
+    /// Translates references to static methods.
+    ///
+    /// This is an adaptation of meth::trans_static_method_callee
+    pub fn trans_static_method(&mut self,
+                               bcx: Block<'bcx, 'tcx>,
+                               ty: Ty<'tcx>,
+                               method_id: DefId,
+                               trait_id: DefId,
+                               substs: &'tcx Substs<'tcx>)
+                               -> OperandRef<'tcx> {
+        debug!("trans_static_method(ty={:?}, method={}, trait={}, substs={:?})",
+                ty,
+                bcx.tcx().item_path_str(method_id),
+                bcx.tcx().item_path_str(trait_id),
+                substs);
+
+        let ccx = bcx.ccx();
+        let tcx = bcx.tcx();
+        let subst::SeparateVecsPerParamSpace {
+            types: rcvr_type,
+            selfs: rcvr_self,
+            fns: rcvr_method
+        } = substs.clone().types.split();
+        let trait_substs = Substs::erased(
+            subst::VecPerParamSpace::new(rcvr_type, rcvr_self, Vec::new())
+        );
+        let trait_substs = tcx.mk_substs(trait_substs);
+        let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, trait_substs));
+        let vtbl = fulfill_obligation(ccx, DUMMY_SP, trait_ref);
+        match vtbl {
+            traits::VtableImpl(traits::VtableImplData { impl_def_id, substs: imp_substs, .. }) => {
+                assert!(!imp_substs.types.needs_infer());
+
+                let mname = tcx.item_name(method_id);
+
+                let subst::SeparateVecsPerParamSpace {
+                    types: impl_type,
+                    selfs: impl_self,
+                    fns: _
+                } = imp_substs.types.split();
+                let callee_substs = Substs::erased(
+                    subst::VecPerParamSpace::new(impl_type, impl_self, rcvr_method)
+                );
+                let mth = tcx.get_impl_method(impl_def_id, callee_substs, mname);
+                let mthsubsts = tcx.mk_substs(mth.substs);
+                self.trans_fn_ref(bcx, ty, mthsubsts, mth.method.def_id)
+            },
+            traits::VtableClosure(data) => {
+                let trait_closure_kind = bcx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
+                let llfn = closure::trans_closure_method(bcx.ccx(),
+                                                         data.closure_def_id,
+                                                         data.substs,
+                                                         trait_closure_kind);
+                OperandRef {
+                    ty: ty,
+                    val: OperandValue::Immediate(llfn)
+                }
+            },
+            traits::VtableObject(ref data) => {
+                let idx = traits::get_vtable_index_of_object_method(tcx, data, method_id);
+                OperandRef::from_rvalue_datum(
+                    meth::trans_object_shim(ccx, data.upcast_trait_ref.clone(), method_id, idx)
+                )
+            }
+            _ => {
+                tcx.sess.bug(&format!("static call to invalid vtable: {:?}", vtbl));
+            }
+        }
+   }
+}
+
+fn is_named_tuple_constructor(tcx: &ty::ctxt, def_id: DefId) -> bool {
+    let node_id = match tcx.map.as_local_node_id(def_id) {
+        Some(n) => n,
+        None => { return false; }
+    };
+    match tcx.map.find(node_id).expect("local item should be in ast map") {
+        map::NodeVariant(v) => {
+            v.node.data.is_tuple()
+        }
+        map::NodeStructCtor(_) => true,
+        _ => false
+    }
+}
index f3c2c3459796ac634f5e71418d20e664bccf1781..a6ba069742d9132f37c830655b58015b3b7f8a15 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use llvm::ValueRef;
-use rustc::middle::ty::{self, Ty, HasTypeFlags};
+use rustc::middle::ty::{self, Ty, TypeFoldable};
 use rustc::mir::repr as mir;
 use rustc::mir::tcx::LvalueTy;
 use trans::adt;
@@ -18,6 +18,9 @@ use trans::build;
 use trans::common::{self, Block};
 use trans::debuginfo::DebugLoc;
 use trans::machine;
+use trans::type_of;
+use llvm;
+use trans::Disr;
 
 use std::ptr;
 
@@ -91,10 +94,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     const_ty)
             },
             mir::Lvalue::ReturnPointer => {
-                let return_ty = bcx.monomorphize(&self.mir.return_ty);
-                let llval = fcx.get_ret_slot(bcx, return_ty, "return");
-                LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty.unwrap()))
-            }
+                let fn_return_ty = bcx.monomorphize(&self.mir.return_ty);
+                let return_ty = fn_return_ty.unwrap();
+                let llval = if !common::return_type_is_void(bcx.ccx(), return_ty) {
+                    fcx.get_ret_slot(bcx, fn_return_ty, "")
+                } 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).
+                    let llty = type_of::type_of(bcx.ccx(), return_ty).ptr_to();
+                    unsafe {
+                        llvm::LLVMGetUndef(llty.to_ref())
+                    }
+                };
+                LvalueRef::new_sized(llval, LvalueTy::from_ty(return_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);
@@ -122,7 +138,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         } else {
                             adt::MaybeSizedValue::unsized_(tr_base.llval, tr_base.llextra)
                         };
-                        (adt::trans_field_ptr(bcx, &base_repr, base, discr, field.index()),
+                        (adt::trans_field_ptr(bcx, &base_repr, base, Disr(discr), field.index()),
                          if is_sized {
                              ptr::null_mut()
                          } else {
@@ -132,7 +148,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     mir::ProjectionElem::Index(ref index) => {
                         let index = self.trans_operand(bcx, index);
                         let llindex = self.prepare_index(bcx, index.immediate());
-                        (build::InBoundsGEP(bcx, tr_base.llval, &[llindex]),
+                        let zero = common::C_uint(bcx.ccx(), 0u64);
+                        (build::InBoundsGEP(bcx, tr_base.llval, &[zero, llindex]),
                          ptr::null_mut())
                     }
                     mir::ProjectionElem::ConstantIndex { offset,
@@ -140,7 +157,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                                          min_length: _ } => {
                         let lloffset = common::C_u32(bcx.ccx(), offset);
                         let llindex = self.prepare_index(bcx, lloffset);
-                        (build::InBoundsGEP(bcx, tr_base.llval, &[llindex]),
+                        let zero = common::C_uint(bcx.ccx(), 0u64);
+                        (build::InBoundsGEP(bcx, tr_base.llval, &[zero, llindex]),
                          ptr::null_mut())
                     }
                     mir::ProjectionElem::ConstantIndex { offset,
@@ -150,7 +168,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         let lllen = self.lvalue_len(bcx, tr_base);
                         let llindex = build::Sub(bcx, lllen, lloffset, DebugLoc::None);
                         let llindex = self.prepare_index(bcx, llindex);
-                        (build::InBoundsGEP(bcx, tr_base.llval, &[llindex]),
+                        let zero = common::C_uint(bcx.ccx(), 0u64);
+                        (build::InBoundsGEP(bcx, tr_base.llval, &[zero, llindex]),
                          ptr::null_mut())
                     }
                     mir::ProjectionElem::Downcast(..) => {
index 27c9feaad35251beb9a355cc8a6470d9d03ac8b4..75ce33da2c9b9da66eb84a77ad40fbdabb3f1eaf 100644 (file)
@@ -28,6 +28,9 @@ use self::operand::OperandRef;
 pub struct MirContext<'bcx, 'tcx:'bcx> {
     mir: &'bcx mir::Mir<'tcx>,
 
+    /// Function context
+    fcx: &'bcx common::FunctionContext<'bcx, 'tcx>,
+
     /// When unwinding is initiated, we have to store this personality
     /// value somewhere so that we can load it and re-use it in the
     /// resume instruction. The personality is (afaik) some kind of
@@ -40,6 +43,9 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
     /// A `Block` for each MIR `BasicBlock`
     blocks: Vec<Block<'bcx, 'tcx>>,
 
+    /// Cached unreachable block
+    unreachable_block: Option<Block<'bcx, 'tcx>>,
+
     /// An LLVM alloca for each MIR `VarDecl`
     vars: Vec<LvalueRef<'tcx>>,
 
@@ -107,7 +113,10 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) {
     // Allocate a `Block` for every basic block
     let block_bcxs: Vec<Block<'bcx,'tcx>> =
         mir_blocks.iter()
-                  .map(|&bb| fcx.new_block(false, &format!("{:?}", bb), None))
+                  .map(|&bb|{
+                      let is_cleanup = mir.basic_block_data(bb).is_cleanup;
+                      fcx.new_block(is_cleanup, &format!("{:?}", bb), None)
+                  })
                   .collect();
 
     // Branch to the START block
@@ -116,8 +125,10 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) {
 
     let mut mircx = MirContext {
         mir: mir,
+        fcx: fcx,
         llpersonalityslot: None,
         blocks: block_bcxs,
+        unreachable_block: None,
         vars: vars,
         temps: temps,
         args: args,
@@ -125,16 +136,8 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) {
 
     // Translate the body of each block
     for &bb in &mir_blocks {
-        if bb != mir::DIVERGE_BLOCK {
-            mircx.trans_block(bb);
-        }
+        mircx.trans_block(bb);
     }
-
-    // Total hack: translate DIVERGE_BLOCK last. This is so that any
-    // panics which the fn may do can initialize the
-    // `llpersonalityslot` cell. We don't do this up front because the
-    // LLVM type of it is (frankly) annoying to compute.
-    mircx.trans_block(mir::DIVERGE_BLOCK);
 }
 
 /// Produce, for each argument, a `ValueRef` pointing at the
@@ -192,3 +195,4 @@ mod lvalue;
 mod rvalue;
 mod operand;
 mod statement;
+mod did;
index 106c1d05ab7f1eb00e4ed1b04283633aac54a9f4..114e78b05bddd0b72d55611b49dbde390676694f 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use llvm::ValueRef;
-use rustc::middle::ty::{Ty, HasTypeFlags};
+use rustc::middle::ty::{Ty, TypeFoldable};
 use rustc::mir::repr as mir;
 use trans::base;
 use trans::common::{self, Block};
@@ -76,6 +76,16 @@ impl<'tcx> OperandRef<'tcx> {
             }
         }
     }
+
+    pub fn from_rvalue_datum(datum: datum::Datum<'tcx, datum::Rvalue>) -> OperandRef {
+        OperandRef {
+            ty: datum.ty,
+            val: match datum.kind.mode {
+                datum::RvalueMode::ByRef => OperandValue::Ref(datum.val),
+                datum::RvalueMode::ByValue => OperandValue::Immediate(datum.val),
+            }
+        }
+    }
 }
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
@@ -159,6 +169,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                          operand: OperandRef<'tcx>)
     {
         debug!("store_operand: operand={}", operand.repr(bcx));
+        // Avoid generating stores of zero-sized values, because the only way to have a zero-sized
+        // value is through `undef`, and store itself is useless.
+        if common::type_is_zero_size(bcx.ccx(), operand.ty) {
+            return;
+        }
         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),
index 17e4ec8e8274c77fbfb94244b026f17135174767..f53653d7cad6aaf9cd3977231aa84f5d158d026a 100644 (file)
@@ -10,6 +10,7 @@
 
 use llvm::ValueRef;
 use rustc::middle::ty::{self, Ty};
+use middle::ty::cast::{CastTy, IntTy};
 use rustc::mir::repr as mir;
 
 use trans::asm;
@@ -19,28 +20,31 @@ use trans::common::{self, Block, Result};
 use trans::debuginfo::DebugLoc;
 use trans::declare;
 use trans::expr;
+use trans::adt;
 use trans::machine;
 use trans::type_::Type;
 use trans::type_of;
 use trans::tvec;
+use trans::Disr;
 
 use super::MirContext;
 use super::operand::{OperandRef, OperandValue};
+use super::lvalue::LvalueRef;
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn trans_rvalue(&mut self,
                         bcx: Block<'bcx, 'tcx>,
-                        lldest: ValueRef,
+                        dest: LvalueRef<'tcx>,
                         rvalue: &mir::Rvalue<'tcx>)
                         -> Block<'bcx, 'tcx>
     {
-        debug!("trans_rvalue(lldest={}, rvalue={:?})",
-               bcx.val_to_string(lldest),
+        debug!("trans_rvalue(dest.llval={}, rvalue={:?})",
+               bcx.val_to_string(dest.llval),
                rvalue);
 
         match *rvalue {
             mir::Rvalue::Use(ref operand) => {
-                self.trans_operand_into(bcx, lldest, operand);
+                self.trans_operand_into(bcx, dest.llval, operand);
                 bcx
             }
 
@@ -49,7 +53,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     // into-coerce of a thin pointer to a fat pointer - just
                     // use the operand path.
                     let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
-                    self.store_operand(bcx, lldest, temp);
+                    self.store_operand(bcx, dest.llval, temp);
                     return bcx;
                 }
 
@@ -72,12 +76,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         base::store_ty(bcx, llval, lltemp, operand.ty);
                         base::coerce_unsized_into(bcx,
                                                   lltemp, operand.ty,
-                                                  lldest, cast_ty);
+                                                  dest.llval, cast_ty);
                     }
                     OperandValue::Ref(llref) => {
                         base::coerce_unsized_into(bcx,
                                                   llref, operand.ty,
-                                                  lldest, cast_ty);
+                                                  dest.llval, cast_ty);
                     }
                 }
                 bcx
@@ -86,20 +90,42 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             mir::Rvalue::Repeat(ref elem, ref count) => {
                 let elem = self.trans_operand(bcx, elem);
                 let size = self.trans_constant(bcx, count).immediate();
-                let base = expr::get_dataptr(bcx, lldest);
+                let base = expr::get_dataptr(bcx, dest.llval);
                 tvec::iter_vec_raw(bcx, base, elem.ty, size, |bcx, llslot, _| {
                     self.store_operand(bcx, llslot, elem);
                     bcx
                 })
             }
 
-            mir::Rvalue::Aggregate(_, ref operands) => {
-                for (i, operand) in operands.iter().enumerate() {
-                    // Note: perhaps this should be StructGep, but
-                    // note that in some cases the values here will
-                    // not be structs but arrays.
-                    let lldest_i = build::GEPi(bcx, lldest, &[0, i]);
-                    self.trans_operand_into(bcx, lldest_i, operand);
+            mir::Rvalue::Aggregate(ref kind, ref operands) => {
+                match *kind {
+                    mir::AggregateKind::Adt(adt_def, index, _) => {
+                        let repr = adt::represent_type(bcx.ccx(), dest.ty.to_ty(bcx.tcx()));
+                        let disr = Disr::from(adt_def.variants[index].disr_val);
+                        adt::trans_set_discr(bcx, &*repr, dest.llval, Disr::from(disr));
+                        for (i, operand) in operands.iter().enumerate() {
+                            let op = self.trans_operand(bcx, operand);
+                            // Do not generate stores and GEPis for zero-sized fields.
+                            if !common::type_is_zero_size(bcx.ccx(), op.ty) {
+                                let val = adt::MaybeSizedValue::sized(dest.llval);
+                                let lldest_i = adt::trans_field_ptr(bcx, &*repr, val, disr, i);
+                                self.store_operand(bcx, lldest_i, op);
+                            }
+                        }
+                    },
+                    _ => {
+                        for (i, operand) in operands.iter().enumerate() {
+                            let op = self.trans_operand(bcx, operand);
+                            // Do not generate stores and GEPis for zero-sized fields.
+                            if !common::type_is_zero_size(bcx.ccx(), op.ty) {
+                                // Note: perhaps this should be StructGep, but
+                                // note that in some cases the values here will
+                                // not be structs but arrays.
+                                let dest = build::GEPi(bcx, dest.llval, &[0, i]);
+                                self.store_operand(bcx, dest, op);
+                            }
+                        }
+                    }
                 }
                 bcx
             }
@@ -113,21 +139,21 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 let llbase1 = build::GEPi(bcx, llbase, &[from_start]);
                 let adj = common::C_uint(ccx, from_start + from_end);
                 let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None);
-                let lladdrdest = expr::get_dataptr(bcx, lldest);
+                let lladdrdest = expr::get_dataptr(bcx, dest.llval);
                 build::Store(bcx, llbase1, lladdrdest);
-                let llmetadest = expr::get_meta(bcx, lldest);
+                let llmetadest = expr::get_meta(bcx, dest.llval);
                 build::Store(bcx, lllen1, llmetadest);
                 bcx
             }
 
-            mir::Rvalue::InlineAsm(inline_asm) => {
+            mir::Rvalue::InlineAsm(ref inline_asm) => {
                 asm::trans_inline_asm(bcx, inline_asm)
             }
 
             _ => {
                 assert!(rvalue_creates_operand(rvalue));
                 let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
-                self.store_operand(bcx, lldest, temp);
+                self.store_operand(bcx, dest.llval, temp);
                 bcx
             }
         }
@@ -185,7 +211,89 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                             }
                         }
                     }
-                    mir::CastKind::Misc => unimplemented!()
+                    mir::CastKind::Misc if common::type_is_immediate(bcx.ccx(), operand.ty) => {
+                        debug_assert!(common::type_is_immediate(bcx.ccx(), cast_ty));
+                        let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast");
+                        let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
+                        let ll_t_in = type_of::arg_type_of(bcx.ccx(), operand.ty);
+                        let ll_t_out = type_of::arg_type_of(bcx.ccx(), cast_ty);
+                        let (llval, ll_t_in, signed) = if let CastTy::Int(IntTy::CEnum) = r_t_in {
+                            let repr = adt::represent_type(bcx.ccx(), operand.ty);
+                            let llval = operand.immediate();
+                            let discr = adt::trans_get_discr(bcx, &*repr, llval, None);
+                            (discr, common::val_ty(discr), adt::is_discr_signed(&*repr))
+                        } else {
+                            (operand.immediate(), ll_t_in, operand.ty.is_signed())
+                        };
+
+                        let newval = match (r_t_in, r_t_out) {
+                            (CastTy::Int(_), CastTy::Int(_)) => {
+                                let srcsz = ll_t_in.int_width();
+                                let dstsz = ll_t_out.int_width();
+                                if srcsz == dstsz {
+                                    build::BitCast(bcx, llval, ll_t_out)
+                                } else if srcsz > dstsz {
+                                    build::Trunc(bcx, llval, ll_t_out)
+                                } else if signed {
+                                    build::SExt(bcx, llval, ll_t_out)
+                                } else {
+                                    build::ZExt(bcx, llval, ll_t_out)
+                                }
+                            }
+                            (CastTy::Float, CastTy::Float) => {
+                                let srcsz = ll_t_in.float_width();
+                                let dstsz = ll_t_out.float_width();
+                                if dstsz > srcsz {
+                                    build::FPExt(bcx, llval, ll_t_out)
+                                } else if srcsz > dstsz {
+                                    build::FPTrunc(bcx, llval, ll_t_out)
+                                } else {
+                                    llval
+                                }
+                            }
+                            (CastTy::Ptr(_), CastTy::Ptr(_)) |
+                            (CastTy::FnPtr, CastTy::Ptr(_)) |
+                            (CastTy::RPtr(_), CastTy::Ptr(_)) =>
+                                build::PointerCast(bcx, llval, ll_t_out),
+                            (CastTy::Ptr(_), CastTy::Int(_)) |
+                            (CastTy::FnPtr, CastTy::Int(_)) =>
+                                build::PtrToInt(bcx, llval, ll_t_out),
+                            (CastTy::Int(_), CastTy::Ptr(_)) =>
+                                build::IntToPtr(bcx, llval, ll_t_out),
+                            (CastTy::Int(_), CastTy::Float) if signed =>
+                                build::SIToFP(bcx, llval, ll_t_out),
+                            (CastTy::Int(_), CastTy::Float) =>
+                                build::UIToFP(bcx, llval, ll_t_out),
+                            (CastTy::Float, CastTy::Int(IntTy::I)) =>
+                                build::FPToSI(bcx, llval, ll_t_out),
+                            (CastTy::Float, CastTy::Int(_)) =>
+                                build::FPToUI(bcx, llval, ll_t_out),
+                            _ => bcx.ccx().sess().bug(
+                                &format!("unsupported cast: {:?} to {:?}", operand.ty, cast_ty)
+                            )
+                        };
+                        OperandValue::Immediate(newval)
+                    }
+                    mir::CastKind::Misc => { // Casts from a fat-ptr.
+                        let ll_cast_ty = type_of::arg_type_of(bcx.ccx(), cast_ty);
+                        let ll_from_ty = type_of::arg_type_of(bcx.ccx(), operand.ty);
+                        if let OperandValue::FatPtr(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 = build::PointerCast(bcx, data_ptr, ll_cft[0]);
+                                assert_eq!(ll_cft[1].kind(), ll_fft[1].kind());
+                                OperandValue::FatPtr(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.
+                                let llval = build::PointerCast(bcx, data_ptr, ll_cast_ty);
+                                OperandValue::Immediate(llval)
+                            }
+                        } else {
+                            panic!("Unexpected non-FatPtr operand")
+                        }
+                    }
                 };
                 (bcx, OperandRef {
                     val: val,
index 9894626e284a2495c4ef51d26b6380695cb5bce3..dae0d3b55c0ba2bec9c3bfee01abd13aa2197929 100644 (file)
@@ -31,7 +31,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         let index = index as usize;
                         match self.temps[index as usize] {
                             TempRef::Lvalue(tr_dest) => {
-                                self.trans_rvalue(bcx, tr_dest.llval, rvalue)
+                                self.trans_rvalue(bcx, tr_dest, rvalue)
                             }
                             TempRef::Operand(None) => {
                                 let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue);
@@ -47,7 +47,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     }
                     _ => {
                         let tr_dest = self.trans_lvalue(bcx, lvalue);
-                        self.trans_rvalue(bcx, tr_dest.llval, rvalue)
+                        self.trans_rvalue(bcx, tr_dest, rvalue)
                     }
                 }
             }
index b102e96af20e2ae3e974316b55af3db7210062ec..d87c17cbf88d477ceb78ee389798b075a0056c4a 100644 (file)
@@ -14,12 +14,14 @@ use middle::cstore::LinkMeta;
 pub use self::base::trans_crate;
 pub use self::context::CrateContext;
 pub use self::common::gensym_name;
+pub use self::disr::Disr;
 
 #[macro_use]
 mod macros;
 
 mod adt;
 mod asm;
+mod assert_dep_graph;
 mod attributes;
 mod base;
 mod basic_block;
@@ -30,6 +32,7 @@ mod cabi_aarch64;
 mod cabi_arm;
 mod cabi_mips;
 mod cabi_powerpc;
+mod cabi_powerpc64;
 mod cabi_x86;
 mod cabi_x86_64;
 mod cabi_x86_win64;
@@ -43,6 +46,7 @@ mod controlflow;
 mod datum;
 mod debuginfo;
 mod declare;
+mod disr;
 mod expr;
 mod foreign;
 mod glue;
index 9c1fcaff7c89568d65e18bdef88821c864d290b0..62e69cbb85e6fcf87a949bd9ec70c7052d67c1cc 100644 (file)
@@ -9,7 +9,6 @@
 // except according to those terms.
 
 use back::link::exported_name;
-use session;
 use llvm::ValueRef;
 use llvm;
 use middle::def_id::DefId;
@@ -24,7 +23,8 @@ use trans::base;
 use trans::common::*;
 use trans::declare;
 use trans::foreign;
-use middle::ty::{self, HasTypeFlags, Ty};
+use middle::ty::{self, Ty};
+use trans::Disr;
 use rustc::front::map as hir_map;
 
 use rustc_front::hir;
@@ -32,6 +32,7 @@ use rustc_front::hir;
 use syntax::abi;
 use syntax::ast;
 use syntax::attr;
+use syntax::errors;
 use std::hash::{Hasher, Hash, SipHasher};
 
 pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
@@ -83,8 +84,8 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
            hash_id);
 
 
-    let map_node = session::expect(
-        ccx.sess(),
+    let map_node = errors::expect(
+        ccx.sess().diagnostic(),
         ccx.tcx().map.find(fn_node_id),
         || {
             format!("while monomorphizing {:?}, couldn't find it in \
@@ -185,7 +186,13 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                               ccx, &**decl, &**body, &[], d, psubsts, fn_node_id,
                               Some(&hash[..]));
                       } else {
-                          trans_fn(ccx, &**decl, &**body, d, psubsts, fn_node_id, &[]);
+                          trans_fn(ccx,
+                                   &**decl,
+                                   &**body,
+                                   d,
+                                   psubsts,
+                                   fn_node_id,
+                                   &i.attrs);
                       }
                   }
 
@@ -201,7 +208,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
             assert_eq!(v.node.name, variant.name);
             let d = mk_lldecl(abi::Rust);
             attributes::inline(d, attributes::InlineAttr::Hint);
-            trans_enum_variant(ccx, fn_node_id, variant.disr_val, psubsts, d);
+            trans_enum_variant(ccx, fn_node_id, Disr::from(variant.disr_val), psubsts, d);
             d
         }
         hir_map::NodeImplItem(impl_item) => {
@@ -216,7 +223,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                  d,
                                  psubsts,
                                  impl_item.id,
-                                 &[]);
+                                 &impl_item.attrs);
                     }
                     d
                 }
@@ -232,8 +239,13 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                     let d = mk_lldecl(abi::Rust);
                     let needs_body = setup_lldecl(d, &trait_item.attrs);
                     if needs_body {
-                        trans_fn(ccx, &sig.decl, body, d,
-                                 psubsts, trait_item.id, &[]);
+                        trans_fn(ccx,
+                                 &sig.decl,
+                                 body,
+                                 d,
+                                 psubsts,
+                                 trait_item.id,
+                                 &trait_item.attrs);
                     }
                     d
                 }
@@ -288,7 +300,7 @@ pub fn apply_param_substs<'tcx,T>(tcx: &ty::ctxt<'tcx>,
                                   param_substs: &Substs<'tcx>,
                                   value: &T)
                                   -> T
-    where T : TypeFoldable<'tcx> + HasTypeFlags
+    where T : TypeFoldable<'tcx>
 {
     let substituted = value.subst(tcx, param_substs);
     normalize_associated_type(tcx, &substituted)
index c7e1af5853d11a48ca3b34eef95bb3cb75d3b3a6..3a1568a70c9922d8d8804c8daa75b0c6b402a090 100644 (file)
@@ -111,8 +111,15 @@ pub fn trans_slice_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
     // Always create an alloca even if zero-sized, to preserve
     // the non-null invariant of the inner slice ptr
-    let llfixed = base::alloc_ty(bcx, fixed_ty, "");
-    call_lifetime_start(bcx, llfixed);
+    let llfixed;
+    // Issue 30018: ensure state is initialized as dropped if necessary.
+    if fcx.type_needs_drop(vt.unit_ty) {
+        llfixed = base::alloc_ty_init(bcx, fixed_ty, InitAlloca::Dropped, "");
+    } else {
+        let uninit = InitAlloca::Uninit("fcx says vt.unit_ty is non-drop");
+        llfixed = base::alloc_ty_init(bcx, fixed_ty, uninit, "");
+        call_lifetime_start(bcx, llfixed);
+    };
 
     if count > 0 {
         // Arrange for the backing array to be cleaned up.
@@ -212,8 +219,8 @@ fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                         bcx = expr::trans_into(bcx, &**element,
                                                SaveIn(lleltptr));
                         let scope = cleanup::CustomScope(temp_scope);
-                        fcx.schedule_lifetime_end(scope, lleltptr);
-                        fcx.schedule_drop_mem(scope, lleltptr, vt.unit_ty, None);
+                        // Issue #30822: mark memory as dropped after running destructor
+                        fcx.schedule_drop_and_fill_mem(scope, lleltptr, vt.unit_ty, None);
                     }
                     fcx.pop_custom_cleanup_scope(temp_scope);
                 }
index e65a212e41b130e01cac5d5dc4d5807b341a4cdf..8696bdd60e2915df6e664b0c866ea7c01324f513 100644 (file)
@@ -17,7 +17,7 @@ use trans::adt;
 use trans::common::*;
 use trans::foreign;
 use trans::machine;
-use middle::ty::{self, RegionEscape, Ty};
+use middle::ty::{self, Ty, TypeFoldable};
 
 use trans::type_::Type;
 
index b83f0a5be6f042dc9b596ec915fa930bebb1765f..98effeefad2a74fa9aaba4cc421683248c295368 100644 (file)
@@ -57,7 +57,7 @@ use middle::resolve_lifetime as rl;
 use middle::privacy::{AllPublic, LastMod};
 use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs, ParamSpace};
 use middle::traits;
-use middle::ty::{self, RegionEscape, Ty, ToPredicate, HasTypeFlags};
+use middle::ty::{self, Ty, ToPredicate, TypeFoldable};
 use middle::ty::wf::object_region_bounds;
 use require_c_abi_if_variadic;
 use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope,
@@ -68,6 +68,7 @@ use util::nodemap::FnvHashSet;
 
 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;
 
@@ -169,10 +170,8 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &hir::Lifetime)
             ty::ReLateBound(debruijn, ty::BrNamed(tcx.map.local_def_id(id), lifetime.name))
         }
 
-        Some(&rl::DefEarlyBoundRegion(space, index, id)) => {
-            let def_id = tcx.map.local_def_id(id);
+        Some(&rl::DefEarlyBoundRegion(space, index, _)) => {
             ty::ReEarlyBound(ty::EarlyBoundRegion {
-                def_id: def_id,
                 space: space,
                 index: index,
                 name: lifetime.name
@@ -181,7 +180,7 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &hir::Lifetime)
 
         Some(&rl::DefFreeRegion(scope, id)) => {
             ty::ReFree(ty::FreeRegion {
-                    scope: tcx.region_maps.item_extent(scope.node_id),
+                    scope: scope.to_code_extent(&tcx.region_maps),
                     bound_region: ty::BrNamed(tcx.map.local_def_id(id),
                                               lifetime.name)
                 })
@@ -197,7 +196,7 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &hir::Lifetime)
 }
 
 fn report_elision_failure(
-    tcx: &ty::ctxt,
+    db: &mut DiagnosticBuilder,
     default_span: Span,
     params: Vec<ElisionFailureInfo>)
 {
@@ -235,26 +234,26 @@ fn report_elision_failure(
     }
 
     if len == 0 {
-        fileline_help!(tcx.sess, default_span,
+        fileline_help!(db, default_span,
                        "this function's return type contains a borrowed value, but \
                         there is no value for it to be borrowed from");
-        fileline_help!(tcx.sess, default_span,
+        fileline_help!(db, default_span,
                        "consider giving it a 'static lifetime");
     } else if !any_lifetimes {
-        fileline_help!(tcx.sess, default_span,
+        fileline_help!(db, default_span,
                        "this function's return type contains a borrowed value with \
                         an elided lifetime, but the lifetime cannot be derived from \
                         the arguments");
-        fileline_help!(tcx.sess, default_span,
+        fileline_help!(db, default_span,
                        "consider giving it an explicit bounded or 'static \
                         lifetime");
     } else if len == 1 {
-        fileline_help!(tcx.sess, default_span,
+        fileline_help!(db, default_span,
                        "this function's return type contains a borrowed value, but \
                         the signature does not say which {} it is borrowed from",
                        m);
     } else {
-        fileline_help!(tcx.sess, default_span,
+        fileline_help!(db, default_span,
                        "this function's return type contains a borrowed value, but \
                         the signature does not say whether it is borrowed from {}",
                        m);
@@ -275,11 +274,12 @@ pub fn opt_ast_region_to_region<'tcx>(
         None => match rscope.anon_regions(default_span, 1) {
             Ok(rs) => rs[0],
             Err(params) => {
-                span_err!(this.tcx().sess, default_span, E0106,
-                          "missing lifetime specifier");
+                let mut err = struct_span_err!(this.tcx().sess, default_span, E0106,
+                                               "missing lifetime specifier");
                 if let Some(params) = params {
-                    report_elision_failure(this.tcx(), default_span, params);
+                    report_elision_failure(&mut err, default_span, params);
                 }
+                err.emit();
                 ty::ReStatic
             }
         }
@@ -719,6 +719,9 @@ fn trait_def_id<'tcx>(this: &AstConv<'tcx>, trait_ref: &hir::TraitRef) -> DefId
     let path = &trait_ref.path;
     match ::lookup_full_def(this.tcx(), path.span, trait_ref.ref_id) {
         def::DefTrait(trait_def_id) => trait_def_id,
+        def::DefErr => {
+            this.tcx().sess.fatal("cannot continue compilation due to previous error");
+        }
         _ => {
             span_fatal!(this.tcx().sess, path.span, E0245, "`{}` is not a trait",
                         path);
@@ -1043,9 +1046,9 @@ fn ast_ty_to_trait_ref<'tcx>(this: &AstConv<'tcx>,
             }
         }
         _ => {
-            span_err!(this.tcx().sess, ty.span, E0178,
-                      "expected a path on the left-hand side of `+`, not `{}`",
-                      pprust::ty_to_string(ty));
+            let mut err = struct_span_err!(this.tcx().sess, ty.span, E0178,
+                                           "expected a path on the left-hand side of `+`, not `{}`",
+                                           pprust::ty_to_string(ty));
             let hi = bounds.iter().map(|x| match *x {
                 hir::TraitTyParamBound(ref tr, _) => tr.span.hi,
                 hir::RegionTyParamBound(ref r) => r.span.hi,
@@ -1058,29 +1061,28 @@ fn ast_ty_to_trait_ref<'tcx>(this: &AstConv<'tcx>,
             match (&ty.node, full_span) {
                 (&hir::TyRptr(None, ref mut_ty), Some(full_span)) => {
                     let mutbl_str = if mut_ty.mutbl == hir::MutMutable { "mut " } else { "" };
-                    this.tcx().sess
-                        .span_suggestion(full_span, "try adding parentheses (per RFC 438):",
-                                         format!("&{}({} +{})",
-                                                 mutbl_str,
-                                                 pprust::ty_to_string(&*mut_ty.ty),
-                                                 pprust::bounds_to_string(bounds)));
+                    err.span_suggestion(full_span, "try adding parentheses (per RFC 438):",
+                                        format!("&{}({} +{})",
+                                                mutbl_str,
+                                                pprust::ty_to_string(&*mut_ty.ty),
+                                                pprust::bounds_to_string(bounds)));
                 }
                 (&hir::TyRptr(Some(ref lt), ref mut_ty), Some(full_span)) => {
                     let mutbl_str = if mut_ty.mutbl == hir::MutMutable { "mut " } else { "" };
-                    this.tcx().sess
-                        .span_suggestion(full_span, "try adding parentheses (per RFC 438):",
-                                         format!("&{} {}({} +{})",
-                                                 pprust::lifetime_to_string(lt),
-                                                 mutbl_str,
-                                                 pprust::ty_to_string(&*mut_ty.ty),
-                                                 pprust::bounds_to_string(bounds)));
+                    err.span_suggestion(full_span, "try adding parentheses (per RFC 438):",
+                                        format!("&{} {}({} +{})",
+                                                pprust::lifetime_to_string(lt),
+                                                mutbl_str,
+                                                pprust::ty_to_string(&*mut_ty.ty),
+                                                pprust::bounds_to_string(bounds)));
                 }
 
                 _ => {
-                    fileline_help!(this.tcx().sess, ty.span,
+                    fileline_help!(&mut err, ty.span,
                                "perhaps you forgot parentheses? (per RFC 438)");
                 }
             }
+            err.emit();
             Err(ErrorReported)
         }
     }
@@ -1133,7 +1135,8 @@ fn make_object_type<'tcx>(this: &AstConv<'tcx>,
         traits::astconv_object_safety_violations(tcx, principal.def_id());
     if !object_safety_violations.is_empty() {
         traits::report_object_safety_error(
-            tcx, span, principal.def_id(), object_safety_violations, false);
+            tcx, span, principal.def_id(), object_safety_violations)
+            .emit();
         return tcx.types.err;
     }
 
@@ -1234,17 +1237,18 @@ fn one_bound_for_assoc_type<'tcx>(tcx: &ty::ctxt<'tcx>,
     }
 
     if bounds.len() > 1 {
-        span_err!(tcx.sess, span, E0221,
-                  "ambiguous associated type `{}` in bounds of `{}`",
-                  assoc_name,
-                  ty_param_name);
+        let mut err = struct_span_err!(tcx.sess, span, E0221,
+                                       "ambiguous associated type `{}` in bounds of `{}`",
+                                       assoc_name,
+                                       ty_param_name);
 
         for bound in &bounds {
-            span_note!(tcx.sess, span,
+            span_note!(&mut err, span,
                        "associated type `{}` could derive from `{}`",
                        ty_param_name,
                        bound);
         }
+        err.emit();
     }
 
     Ok(bounds[0].clone())
@@ -1533,6 +1537,9 @@ fn base_def_to_ty<'tcx>(this: &AstConv<'tcx>,
         def::DefPrimTy(prim_ty) => {
             prim_ty_to_ty(tcx, base_segments, prim_ty)
         }
+        def::DefErr => {
+            return this.tcx().types.err;
+        }
         _ => {
             let id_node = tcx.map.as_local_node_id(def.def_id()).unwrap();
             span_err!(tcx.sess, span, E0248,
@@ -1703,12 +1710,13 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
                     }
                 }
                 Err(ref r) => {
-                    span_err!(tcx.sess, r.span, E0250,
-                              "array length constant evaluation error: {}",
-                              r.description());
+                    let mut err = struct_span_err!(tcx.sess, r.span, E0250,
+                                                   "array length constant evaluation error: {}",
+                                                   r.description());
                     if !ast_ty.span.contains(r.span) {
-                        span_note!(tcx.sess, ast_ty.span, "for array length here")
+                        span_note!(&mut err, ast_ty.span, "for array length here")
                     }
+                    err.emit();
                     this.tcx().types.err
                 }
             }
@@ -1791,75 +1799,31 @@ fn ty_of_method_or_bare_fn<'a, 'tcx>(this: &AstConv<'tcx>,
     // 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 mut explicit_self_category_result = None;
-    let (self_ty, implied_output_region) = match opt_self_info {
+    let (self_ty, explicit_self_category) = match opt_self_info {
         None => (None, None),
-        Some(self_info) => {
-            // This type comes from an impl or trait; no late-bound
-            // regions should be present.
-            assert!(!self_info.untransformed_self_ty.has_escaping_regions());
-
-            // Figure out and record the explicit self category.
-            let explicit_self_category =
-                determine_explicit_self_category(this, &rb, &self_info);
-            explicit_self_category_result = Some(explicit_self_category);
-            match explicit_self_category {
-                ty::StaticExplicitSelfCategory => {
-                    (None, None)
-                }
-                ty::ByValueExplicitSelfCategory => {
-                    (Some(self_info.untransformed_self_ty), None)
-                }
-                ty::ByReferenceExplicitSelfCategory(region, mutability) => {
-                    (Some(this.tcx().mk_ref(
-                                      this.tcx().mk_region(region),
-                                      ty::TypeAndMut {
-                                        ty: self_info.untransformed_self_ty,
-                                        mutbl: mutability
-                                      })),
-                     Some(region))
-                }
-                ty::ByBoxExplicitSelfCategory => {
-                    (Some(this.tcx().mk_box(self_info.untransformed_self_ty)), None)
-                }
-            }
-        }
+        Some(self_info) => determine_self_type(this, &rb, self_info)
     };
 
     // HACK(eddyb) replace the fake self type in the AST with the actual type.
-    let input_params = if self_ty.is_some() {
+    let arg_params = if self_ty.is_some() {
         &decl.inputs[1..]
     } else {
         &decl.inputs[..]
     };
-    let input_tys = input_params.iter().map(|a| ty_of_arg(this, &rb, a, None));
-    let input_pats: Vec<String> = input_params.iter()
-                                              .map(|a| pprust::pat_to_string(&*a.pat))
-                                              .collect();
-    let self_and_input_tys: Vec<Ty> =
-        self_ty.into_iter().chain(input_tys).collect();
-
+    let arg_tys: Vec<Ty> =
+        arg_params.iter().map(|a| ty_of_arg(this, &rb, a, None)).collect();
+    let arg_pats: Vec<String> =
+        arg_params.iter().map(|a| pprust::pat_to_string(&*a.pat)).collect();
 
     // Second, if there was exactly one lifetime (either a substitution or a
     // reference) in the arguments, then any anonymous regions in the output
     // have that lifetime.
-    let implied_output_region = match implied_output_region {
-        Some(r) => Ok(r),
-        None => {
-            let input_tys = if self_ty.is_some() {
-                // Skip the first argument if `self` is present.
-                &self_and_input_tys[1..]
-            } else {
-                &self_and_input_tys[..]
-            };
-
-            find_implied_output_region(this.tcx(), input_tys, input_pats)
-        }
+    let implied_output_region = match explicit_self_category {
+        Some(ty::ExplicitSelfCategory::ByReference(region, _)) => Ok(region),
+        _ => find_implied_output_region(this.tcx(), &arg_tys, arg_pats)
     };
 
     let output_ty = match decl.output {
-        hir::Return(ref output) if output.node == hir::TyInfer =>
-            ty::FnConverging(this.ty_infer(None, None, None, output.span)),
         hir::Return(ref output) =>
             ty::FnConverging(convert_ty_with_lifetime_elision(this,
                                                               implied_output_region,
@@ -1872,28 +1836,37 @@ fn ty_of_method_or_bare_fn<'a, 'tcx>(this: &AstConv<'tcx>,
         unsafety: unsafety,
         abi: abi,
         sig: ty::Binder(ty::FnSig {
-            inputs: self_and_input_tys,
+            inputs: self_ty.into_iter().chain(arg_tys).collect(),
             output: output_ty,
             variadic: decl.variadic
         }),
-    }, explicit_self_category_result)
+    }, explicit_self_category)
 }
 
-fn determine_explicit_self_category<'a, 'tcx>(this: &AstConv<'tcx>,
-                                              rscope: &RegionScope,
-                                              self_info: &SelfInfo<'a, 'tcx>)
-                                              -> ty::ExplicitSelfCategory
+fn determine_self_type<'a, 'tcx>(this: &AstConv<'tcx>,
+                                 rscope: &RegionScope,
+                                 self_info: SelfInfo<'a, 'tcx>)
+                                 -> (Option<Ty<'tcx>>, Option<ty::ExplicitSelfCategory>)
 {
+    let self_ty = self_info.untransformed_self_ty;
     return match self_info.explicit_self.node {
-        hir::SelfStatic => ty::StaticExplicitSelfCategory,
-        hir::SelfValue(_) => ty::ByValueExplicitSelfCategory,
+        hir::SelfStatic => (None, Some(ty::ExplicitSelfCategory::Static)),
+        hir::SelfValue(_) => {
+            (Some(self_ty), Some(ty::ExplicitSelfCategory::ByValue))
+        }
         hir::SelfRegion(ref lifetime, mutability, _) => {
             let region =
                 opt_ast_region_to_region(this,
                                          rscope,
                                          self_info.explicit_self.span,
                                          lifetime);
-            ty::ByReferenceExplicitSelfCategory(region, mutability)
+            (Some(this.tcx().mk_ref(
+                this.tcx().mk_region(region),
+                ty::TypeAndMut {
+                    ty: self_ty,
+                    mutbl: mutability
+                })),
+             Some(ty::ExplicitSelfCategory::ByReference(region, mutability)))
         }
         hir::SelfExplicit(ref ast_type, _) => {
             let explicit_type = ast_ty_to_ty(this, rscope, &**ast_type);
@@ -1909,12 +1882,12 @@ fn determine_explicit_self_category<'a, 'tcx>(this: &AstConv<'tcx>,
             // ```
             // impl Foo for &T {
             //     // Legal declarations:
-            //     fn method1(self: &&T); // ByReferenceExplicitSelfCategory
-            //     fn method2(self: &T); // ByValueExplicitSelfCategory
-            //     fn method3(self: Box<&T>); // ByBoxExplicitSelfCategory
+            //     fn method1(self: &&T); // ExplicitSelfCategory::ByReference
+            //     fn method2(self: &T); // ExplicitSelfCategory::ByValue
+            //     fn method3(self: Box<&T>); // ExplicitSelfCategory::ByBox
             //
             //     // Invalid cases will be caught later by `check_method_self_type`:
-            //     fn method_err1(self: &mut T); // ByReferenceExplicitSelfCategory
+            //     fn method_err1(self: &mut T); // ExplicitSelfCategory::ByReference
             // }
             // ```
             //
@@ -1925,7 +1898,7 @@ fn determine_explicit_self_category<'a, 'tcx>(this: &AstConv<'tcx>,
             // call it by-ref, by-box as appropriate. For method1, for
             // example, the impl type has one modifier, but the method
             // type has two, so we end up with
-            // ByReferenceExplicitSelfCategory.
+            // ExplicitSelfCategory::ByReference.
 
             let impl_modifiers = count_modifiers(self_info.untransformed_self_ty);
             let method_modifiers = count_modifiers(explicit_type);
@@ -1938,15 +1911,17 @@ fn determine_explicit_self_category<'a, 'tcx>(this: &AstConv<'tcx>,
                    impl_modifiers,
                    method_modifiers);
 
-            if impl_modifiers >= method_modifiers {
-                ty::ByValueExplicitSelfCategory
+            let category = if impl_modifiers >= method_modifiers {
+                ty::ExplicitSelfCategory::ByValue
             } else {
                 match explicit_type.sty {
-                    ty::TyRef(r, mt) => ty::ByReferenceExplicitSelfCategory(*r, mt.mutbl),
-                    ty::TyBox(_) => ty::ByBoxExplicitSelfCategory,
-                    _ => ty::ByValueExplicitSelfCategory,
+                    ty::TyRef(r, mt) => ty::ExplicitSelfCategory::ByReference(*r, mt.mutbl),
+                    ty::TyBox(_) => ty::ExplicitSelfCategory::ByBox,
+                    _ => ty::ExplicitSelfCategory::ByValue,
                 }
-            }
+            };
+
+            (Some(explicit_type), Some(category))
         }
     };
 
index 992c3026e4bb51e10bbb5a7396dbf717d19b7277..dfa144699b217ad0a6ec540dbf96f096ca41dccc 100644 (file)
@@ -14,7 +14,7 @@ use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding};
 use middle::pat_util::pat_is_resolved_const;
 use middle::privacy::{AllPublic, LastMod};
 use middle::subst::Substs;
-use middle::ty::{self, Ty, HasTypeFlags, LvaluePreference};
+use middle::ty::{self, Ty, TypeFoldable, LvaluePreference};
 use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
 use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation};
 use check::{check_expr_with_lvalue_pref};
@@ -143,20 +143,24 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
                     return;
                 }
             }
-            let const_did = tcx.def_map.borrow().get(&pat.id).unwrap().def_id();
-            let const_scheme = tcx.lookup_item_type(const_did);
-            assert!(const_scheme.generics.is_empty());
-            let const_ty = pcx.fcx.instantiate_type_scheme(pat.span,
-                                                           &Substs::empty(),
-                                                           &const_scheme.ty);
-            fcx.write_ty(pat.id, const_ty);
-
-            // FIXME(#20489) -- we should limit the types here to scalars or something!
-
-            // As with PatLit, what we really want here is that there
-            // exist a LUB, but for the cases that can occur, subtype
-            // is good enough.
-            demand::suptype(fcx, pat.span, expected, const_ty);
+            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 = pcx.fcx.instantiate_type_scheme(pat.span,
+                                                               &Substs::empty(),
+                                                               &const_scheme.ty);
+                fcx.write_ty(pat.id, const_ty);
+
+                // FIXME(#20489) -- we should limit the types here to scalars or something!
+
+                // As with PatLit, what we really want here is that there
+                // exist a LUB, but for the cases that can occur, subtype
+                // is good enough.
+                demand::suptype(fcx, pat.span, expected, const_ty);
+            } else {
+                fcx.write_error(pat.id);
+            }
         }
         hir::PatIdent(bm, ref path, ref sub) if pat_is_binding(&tcx.def_map.borrow(), pat) => {
             let typ = fcx.local_ty(pat.span, pat.id);
@@ -187,14 +191,15 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
 
             // if there are multiple arms, make sure they all agree on
             // what the type of the binding `x` ought to be
-            let canon_id = *pcx.map.get(&path.node.name).unwrap();
-            if canon_id != pat.id {
-                let ct = fcx.local_ty(pat.span, canon_id);
-                demand::eqtype(fcx, pat.span, ct, typ);
-            }
+            if let Some(&canon_id) = pcx.map.get(&path.node.name) {
+                if canon_id != pat.id {
+                    let ct = fcx.local_ty(pat.span, canon_id);
+                    demand::eqtype(fcx, pat.span, ct, typ);
+                }
 
-            if let Some(ref p) = *sub {
-                check_pat(pcx, &**p, expected);
+                if let Some(ref p) = *sub {
+                    check_pat(pcx, &**p, expected);
+                }
             }
         }
         hir::PatIdent(_, ref path, _) => {
@@ -209,6 +214,10 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
         hir::PatQPath(ref qself, ref path) => {
             let self_ty = fcx.to_ty(&qself.ty);
             let path_res = if let Some(&d) = tcx.def_map.borrow().get(&pat.id) {
+                if d.base_def == def::DefErr {
+                    fcx.write_error(pat.id);
+                    return;
+                }
                 d
             } else if qself.position == 0 {
                 // This is just a sentinel for finish_resolving_def_to_ty.
@@ -219,8 +228,9 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
                     depth: path.segments.len()
                 }
             } else {
-                tcx.sess.span_bug(pat.span,
-                                  &format!("unbound path {:?}", pat))
+                debug!("unbound path {:?}", pat);
+                fcx.write_error(pat.id);
+                return;
             };
             if let Some((opt_ty, segments, def)) =
                     resolve_ty_and_def_ufcs(fcx, path_res, Some(self_ty),
@@ -585,14 +595,10 @@ fn bad_struct_kind_err(sess: &Session, pat: &hir::Pat, path: &hir::Path, lint: b
     let name = pprust::path_to_string(path);
     let msg = format!("`{}` does not name a tuple variant or a tuple struct", name);
     if lint {
-        let expanded_msg =
-            format!("{}; RFC 218 disallowed matching of unit variants or unit structs via {}(..)",
-                    msg,
-                    name);
         sess.add_lint(lint::builtin::MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
                       pat.id,
                       pat.span,
-                      expanded_msg);
+                      msg);
     } else {
         span_err!(sess, pat.span, E0164, "{}", msg);
     }
@@ -609,7 +615,20 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
     let fcx = pcx.fcx;
     let tcx = pcx.fcx.ccx.tcx;
 
-    let path_res = *tcx.def_map.borrow().get(&pat.id).unwrap();
+    let path_res = match tcx.def_map.borrow().get(&pat.id) {
+        Some(&path_res) if path_res.base_def != def::DefErr => path_res,
+        _ => {
+            fcx.write_error(pat.id);
+
+            if let Some(subpats) = subpats {
+                for pat in subpats {
+                    check_pat(pcx, &**pat, tcx.types.err);
+                }
+            }
+
+            return;
+        }
+    };
 
     let (opt_ty, segments, def) = match resolve_ty_and_def_ufcs(fcx, path_res,
                                                                 None, path,
@@ -771,12 +790,13 @@ pub fn check_struct_pat_fields<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
     for &Spanned { node: ref field, span } in fields {
         let field_ty = match used_fields.entry(field.name) {
             Occupied(occupied) => {
-                span_err!(tcx.sess, span, E0025,
-                    "field `{}` bound multiple times in the pattern",
-                    field.name);
-                span_note!(tcx.sess, *occupied.get(),
-                    "field `{}` previously bound here",
-                    field.name);
+                let mut err = struct_span_err!(tcx.sess, span, E0025,
+                                               "field `{}` bound multiple times in the pattern",
+                                               field.name);
+                span_note!(&mut err, *occupied.get(),
+                           "field `{}` previously bound here",
+                           field.name);
+                err.emit();
                 tcx.types.err
             }
             Vacant(vacant) => {
index 8dc95562e44afabc536ab9faa05c9ef2ea58a41f..91916efa882778226c992ec971cc40d6f8c50eaa 100644 (file)
@@ -11,7 +11,6 @@
 use middle::infer::InferCtxt;
 use middle::traits::{self, FulfillmentContext, Normalized, MiscObligation,
                      SelectionContext, ObligationCause};
-use middle::ty::HasTypeFlags;
 use middle::ty::fold::TypeFoldable;
 use syntax::ast;
 use syntax::codemap::Span;
@@ -23,7 +22,7 @@ pub fn normalize_associated_types_in<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
                                                 body_id: ast::NodeId,
                                                 value: &T)
                                                 -> T
-    where T : TypeFoldable<'tcx> + HasTypeFlags
+    where T : TypeFoldable<'tcx>
 {
     debug!("normalize_associated_types_in(value={:?})", value);
     let mut selcx = SelectionContext::new(infcx);
index 6a23be682e9d3cd0a0c1ff3c4ba7682bf74babd4..a1b378d84d0010faf8f9a1fbbb3a4c1c612d748f 100644 (file)
@@ -26,6 +26,7 @@ use super::write_call;
 
 use CrateCtxt;
 use middle::cstore::LOCAL_CRATE;
+use middle::def;
 use middle::def_id::DefId;
 use middle::infer;
 use middle::ty::{self, LvaluePreference, Ty};
@@ -60,11 +61,12 @@ pub fn check_legal_trait_for_method_call(ccx: &CrateCtxt, span: Span, trait_id:
             return // not a closure method, everything is OK.
         };
 
-        span_err!(tcx.sess, span, E0174,
-                  "explicit use of unboxed closure method `{}` is experimental",
-                  method);
-        fileline_help!(tcx.sess, span,
-                   "add `#![feature(unboxed_closures)]` to the crate attributes to enable");
+        struct_span_err!(tcx.sess, span, E0174,
+                         "explicit use of unboxed closure method `{}` is experimental",
+                         method)
+            .fileline_help(span, "add `#![feature(unboxed_closures)]` to the crate \
+                                  attributes to enable")
+            .emit();
     }
 }
 
@@ -227,21 +229,23 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
             sig
         }
         _ => {
-            fcx.type_error_message(call_expr.span, |actual| {
+            let mut err = fcx.type_error_struct(call_expr.span, |actual| {
                 format!("expected function, found `{}`", actual)
             }, callee_ty, None);
 
             if let hir::ExprCall(ref expr, _) = call_expr.node {
                 let tcx = fcx.tcx();
                 if let Some(pr) = tcx.def_map.borrow().get(&expr.id) {
-                    if pr.depth == 0 {
+                    if pr.depth == 0 && pr.base_def != def::DefErr {
                         if let Some(span) = tcx.map.span_if_local(pr.def_id()) {
-                            tcx.sess.span_note(span, "defined here")
+                            err.span_note(span, "defined here");
                         }
                     }
                 }
             }
 
+            err.emit();
+
             // This is the "default" function signature, used in case of error.
             // In that case, we check each argument against "error" in order to
             // set up all the node type bindings.
index 13e5e46ed27002fe4109908a6976a6a5a7af164d..fd6c4f44ba42809d9ffb62e056b49b5f1dae95b3 100644 (file)
@@ -45,7 +45,7 @@ use super::structurally_resolved_type;
 
 use lint;
 use middle::def_id::DefId;
-use middle::ty::{self, Ty, HasTypeFlags};
+use middle::ty::{self, Ty, TypeFoldable};
 use middle::ty::cast::{CastKind, CastTy};
 use syntax::codemap::Span;
 use rustc_front::hir;
@@ -127,23 +127,25 @@ impl<'tcx> CastCheck<'tcx> {
             CastError::NeedViaThinPtr |
             CastError::NeedViaInt |
             CastError::NeedViaUsize => {
-                fcx.type_error_message(self.span, |actual| {
+                fcx.type_error_struct(self.span, |actual| {
                     format!("casting `{}` as `{}` is invalid",
                             actual,
                             fcx.infcx().ty_to_string(self.cast_ty))
-                }, self.expr_ty, None);
-                fcx.ccx.tcx.sess.fileline_help(self.span,
-                    &format!("cast through {} first", match e {
-                        CastError::NeedViaPtr => "a raw pointer",
-                        CastError::NeedViaThinPtr => "a thin pointer",
-                        CastError::NeedViaInt => "an integer",
-                        CastError::NeedViaUsize => "a usize",
-                        _ => unreachable!()
-                }));
+                }, self.expr_ty, None)
+                    .fileline_help(self.span,
+                        &format!("cast through {} first", match e {
+                            CastError::NeedViaPtr => "a raw pointer",
+                            CastError::NeedViaThinPtr => "a thin pointer",
+                            CastError::NeedViaInt => "an integer",
+                            CastError::NeedViaUsize => "a usize",
+                            _ => unreachable!()
+                        }))
+                    .emit();
             }
             CastError::CastToBool => {
-                span_err!(fcx.tcx().sess, self.span, E0054, "cannot cast as `bool`");
-                fcx.ccx.tcx.sess.fileline_help(self.span, "compare with zero instead");
+                struct_span_err!(fcx.tcx().sess, self.span, E0054, "cannot cast as `bool`")
+                    .fileline_help(self.span, "compare with zero instead")
+                    .emit();
             }
             CastError::CastToChar => {
                 fcx.type_error_message(self.span, |actual| {
@@ -165,12 +167,13 @@ impl<'tcx> CastCheck<'tcx> {
                 }, self.expr_ty, None);
             }
             CastError::DifferingKinds => {
-                fcx.type_error_message(self.span, |actual| {
+                fcx.type_error_struct(self.span, |actual| {
                     format!("casting `{}` as `{}` is invalid",
                             actual,
                             fcx.infcx().ty_to_string(self.cast_ty))
-                }, self.expr_ty, None);
-                fcx.ccx.tcx.sess.fileline_note(self.span, "vtable kinds may not match");
+                }, self.expr_ty, None)
+                    .fileline_note(self.span, "vtable kinds may not match")
+                    .emit();
             }
         }
     }
index 4f7a6395395461f4d444916b0934d5e5d3f4a42f..7792169d3eb46788f9d1b3f1af5da4622d663387 100644 (file)
@@ -75,7 +75,7 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
     fcx.write_ty(expr.id, closure_type);
 
     let fn_sig = fcx.tcx().liberate_late_bound_regions(
-        fcx.tcx().region_maps.item_extent(body.id), &fn_ty.sig);
+        fcx.tcx().region_maps.call_site_extent(expr.id, body.id), &fn_ty.sig);
 
     check_fn(fcx.ccx,
              hir::Unsafety::Normal,
index ace731c00723c0fb0ab6f8ca36736541affe5d4a..8f64e85de4b0f4708d6b481d9b53c0b48139431f 100644 (file)
@@ -68,7 +68,8 @@ use middle::traits::{predicate_for_trait_def, report_selection_error};
 use middle::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef};
 use middle::ty::adjustment::{AutoPtr, AutoUnsafe, AdjustReifyFnPointer};
 use middle::ty::adjustment::{AdjustUnsafeFnPointer};
-use middle::ty::{self, HasTypeFlags, LvaluePreference, TypeAndMut, Ty};
+use middle::ty::{self, LvaluePreference, TypeAndMut, Ty};
+use middle::ty::fold::TypeFoldable;
 use middle::ty::error::TypeError;
 use middle::ty::relate::RelateResult;
 use util::common::indent;
index d28a673f748c07c46157d74a253d9d386a908b5a..554424a36b19af688259725a2efaf3ad87e5642a 100644 (file)
@@ -55,9 +55,9 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
     // inscrutable, particularly for cases where one method has no
     // self.
     match (&trait_m.explicit_self, &impl_m.explicit_self) {
-        (&ty::StaticExplicitSelfCategory,
-         &ty::StaticExplicitSelfCategory) => {}
-        (&ty::StaticExplicitSelfCategory, _) => {
+        (&ty::ExplicitSelfCategory::Static,
+         &ty::ExplicitSelfCategory::Static) => {}
+        (&ty::ExplicitSelfCategory::Static, _) => {
             span_err!(tcx.sess, impl_m_span, E0185,
                 "method `{}` has a `{}` declaration in the impl, \
                         but not in the trait",
@@ -65,7 +65,7 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
                         impl_m.explicit_self);
             return;
         }
-        (_, &ty::StaticExplicitSelfCategory) => {
+        (_, &ty::ExplicitSelfCategory::Static) => {
             span_err!(tcx.sess, impl_m_span, E0186,
                 "method `{}` has a `{}` declaration in the trait, \
                         but not in the impl",
index d68959b99be0fee092346608b85510357499050e..0cf552b6efecb9165a4f9ba8f9ff75c324f5fdf4 100644 (file)
@@ -95,12 +95,13 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
 
     if let Err(_) = infer::mk_eqty(&infcx, true, infer::TypeOrigin::Misc(drop_impl_span),
                                    named_type, fresh_impl_self_ty) {
-        span_err!(tcx.sess, drop_impl_span, E0366,
-                  "Implementations of Drop cannot be specialized");
         let item_span = tcx.map.span(self_type_node_id);
-        tcx.sess.span_note(item_span,
-                           "Use same sequence of generic type and region \
-                            parameters that is on the struct/enum definition");
+        struct_span_err!(tcx.sess, drop_impl_span, E0366,
+                         "Implementations of Drop cannot be specialized")
+            .span_note(item_span,
+                       "Use same sequence of generic type and region \
+                        parameters that is on the struct/enum definition")
+            .emit();
         return Err(());
     }
 
@@ -197,11 +198,12 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
 
         if !assumptions_in_impl_context.contains(&predicate) {
             let item_span = tcx.map.span(self_type_node_id);
-            span_err!(tcx.sess, drop_impl_span, E0367,
-                      "The requirement `{}` is added only by the Drop impl.", predicate);
-            tcx.sess.span_note(item_span,
-                               "The same requirement must be part of \
-                                the struct/enum definition");
+            struct_span_err!(tcx.sess, drop_impl_span, E0367,
+                             "The requirement `{}` is added only by the Drop impl.", predicate)
+                .span_note(item_span,
+                           "The same requirement must be part of \
+                            the struct/enum definition")
+                .emit();
         }
     }
 
@@ -289,8 +291,8 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>
         Ok(()) => {}
         Err(Error::Overflow(ref ctxt, ref detected_on_typ)) => {
             let tcx = rcx.tcx();
-            span_err!(tcx.sess, span, E0320,
-                      "overflow while adding drop-check rules for {}", typ);
+            let mut err = struct_span_err!(tcx.sess, span, E0320,
+                                           "overflow while adding drop-check rules for {}", typ);
             match *ctxt {
                 TypeContext::Root => {
                     // no need for an additional note if the overflow
@@ -311,7 +313,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>
                         format!("`{}`", field)
                     };
                     span_note!(
-                        rcx.tcx().sess,
+                        &mut err,
                         span,
                         "overflowed on {} field {} type: {}",
                         variant_name,
@@ -319,6 +321,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>
                         detected_on_typ);
                 }
             }
+            err.emit();
         }
     }
 }
@@ -366,6 +369,10 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
         return Err(Error::Overflow(context, ty))
     }
 
+    // canoncialize the regions in `ty` before inserting - infinitely many
+    // region variables can refer to the same region.
+    let ty = cx.rcx.infcx().resolve_type_and_region_vars_if_possible(&ty);
+
     if !cx.breadcrumbs.insert(ty) {
         debug!("iterate_over_potentially_unsafe_regions_in_type \
                {}ty: {} scope: {:?} - cached",
index e0ad51b4ea1b54532dd56d0fe0d763d7aa5ab9ed..44b36294cb480d56867c65231661c9d9ecb784b1 100644 (file)
@@ -17,7 +17,7 @@ use middle::def_id::DefId;
 use middle::privacy::{AllPublic, DependsOn, LastPrivate, LastMod};
 use middle::subst;
 use middle::traits;
-use middle::ty::{self, RegionEscape, ToPredicate, ToPolyTraitRef, TraitRef};
+use middle::ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable};
 use middle::ty::adjustment::{AdjustDerefRef, AutoDerefRef, AutoPtr};
 use middle::infer;
 
@@ -274,13 +274,13 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                    method_ty.explicit_self);
 
             match method_ty.explicit_self {
-                ty::ByValueExplicitSelfCategory => {
+                ty::ExplicitSelfCategory::ByValue => {
                     // Trait method is fn(self), no transformation needed.
                     assert!(!unsize);
                     fcx.write_autoderef_adjustment(self_expr.id, autoderefs);
                 }
 
-                ty::ByReferenceExplicitSelfCategory(..) => {
+                ty::ExplicitSelfCategory::ByReference(..) => {
                     // Trait method is fn(&self) or fn(&mut self), need an
                     // autoref. Pull the region etc out of the type of first argument.
                     match transformed_self_ty.sty {
index ed819d46041e60ce45c5db274a6501b725a64003..44dd0ef7b17d836e055127ba664ee6aa1733316e 100644 (file)
@@ -19,9 +19,7 @@ use middle::def_id::DefId;
 use middle::subst;
 use middle::subst::Subst;
 use middle::traits;
-use middle::ty::{self, NoPreference, RegionEscape, Ty, ToPolyTraitRef, TraitRef};
-use middle::ty::HasTypeFlags;
-use middle::ty::fold::TypeFoldable;
+use middle::ty::{self, NoPreference, Ty, ToPolyTraitRef, TraitRef, TypeFoldable};
 use middle::infer;
 use middle::infer::{InferCtxt, TypeOrigin};
 use syntax::ast;
@@ -1144,10 +1142,10 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
         match *item {
             ty::ImplOrTraitItem::MethodTraitItem(ref method) =>
                 match method.explicit_self {
-                    ty::StaticExplicitSelfCategory => self.mode == Mode::Path,
-                    ty::ByValueExplicitSelfCategory |
-                    ty::ByReferenceExplicitSelfCategory(..) |
-                    ty::ByBoxExplicitSelfCategory => true,
+                    ty::ExplicitSelfCategory::Static => self.mode == Mode::Path,
+                    ty::ExplicitSelfCategory::ByValue |
+                    ty::ExplicitSelfCategory::ByReference(..) |
+                    ty::ExplicitSelfCategory::ByBox => true,
                 },
             ty::ImplOrTraitItem::ConstTraitItem(..) => self.mode == Mode::Path,
             _ => false,
index 955bc92a8f31e89bf1a9c982c4b98371ab793798..560e84b52d1d6966a8e2ee9fd70ff9703ce0632a 100644 (file)
@@ -16,7 +16,7 @@ use CrateCtxt;
 use astconv::AstConv;
 use check::{self, FnCtxt};
 use front::map as hir_map;
-use middle::ty::{self, Ty, ToPolyTraitRef, ToPredicate, HasTypeFlags};
+use middle::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable};
 use middle::cstore::{self, CrateStore, DefLike};
 use middle::def;
 use middle::def_id::DefId;
@@ -27,6 +27,7 @@ use util::nodemap::{FnvHashSet};
 
 use syntax::ast;
 use syntax::codemap::Span;
+use syntax::errors::DiagnosticBuilder;
 use rustc_front::print::pprust;
 use rustc_front::hir;
 
@@ -55,7 +56,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                            mode }) => {
             let cx = fcx.tcx();
 
-            fcx.type_error_message(
+            let mut err = fcx.type_error_struct(
                 span,
                 |actual| {
                     format!("no {} named `{}` found for type `{}` \
@@ -78,17 +79,21 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                         // snippet
                     };
 
-                    let span_stored_function = || {
-                        cx.sess.span_note(span,
+                    macro_rules! span_stored_function {
+                        () => {
+                            err.span_note(span,
                                           &format!("use `({0}.{1})(...)` if you meant to call \
                                                     the function stored in the `{1}` field",
                                                    expr_string, item_name));
-                    };
+                        }
+                    }
 
-                    let span_did_you_mean = || {
-                        cx.sess.span_note(span, &format!("did you mean to write `{0}.{1}`?",
+                    macro_rules! span_did_you_mean {
+                        () => {
+                            err.span_note(span, &format!("did you mean to write `{0}.{1}`?",
                                                          expr_string, item_name));
-                    };
+                        }
+                    }
 
                     // Determine if the field can be used as a function in some way
                     let field_ty = field.ty(cx, substs);
@@ -96,19 +101,22 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                     match field_ty.sty {
                         // Not all of these (e.g. unsafe fns) implement FnOnce
                         // so we look for these beforehand
-                        ty::TyClosure(..) | ty::TyBareFn(..) => span_stored_function(),
+                        ty::TyClosure(..) | ty::TyBareFn(..) => {
+                            span_stored_function!();
+                        }
                         // If it's not a simple function, look for things which implement FnOnce
                         _ => {
                             if let Ok(fn_once_trait_did) =
                                     cx.lang_items.require(FnOnceTraitLangItem) {
                                 let infcx = fcx.infcx();
                                 infcx.probe(|_| {
-                                    let fn_once_substs = Substs::new_trait(vec![
-                                                                            infcx.next_ty_var()],
-                                                                           Vec::new(),
-                                                                           field_ty);
-                                    let trait_ref = ty::TraitRef::new(fn_once_trait_did,
-                                                                      cx.mk_substs(fn_once_substs));
+                                    let fn_once_substs =
+                                        Substs::new_trait(vec![infcx.next_ty_var()],
+                                                          Vec::new(),
+                                                          field_ty);
+                                    let trait_ref =
+                                      ty::TraitRef::new(fn_once_trait_did,
+                                                        cx.mk_substs(fn_once_substs));
                                     let poly_trait_ref = trait_ref.to_poly_trait_ref();
                                     let obligation = Obligation::misc(span,
                                                                       fcx.body_id,
@@ -117,13 +125,13 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                     let mut selcx = SelectionContext::new(infcx);
 
                                     if selcx.evaluate_obligation(&obligation) {
-                                        span_stored_function();
+                                        span_stored_function!();
                                     } else {
-                                        span_did_you_mean();
+                                        span_did_you_mean!();
                                     }
                                 });
                             } else {
-                                span_did_you_mean()
+                                span_did_you_mean!();
                             }
                         }
                     }
@@ -131,11 +139,11 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             }
 
             if !static_sources.is_empty() {
-                cx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     "found defined static methods, maybe a `self` is missing?");
 
-                report_candidates(fcx, span, item_name, static_sources);
+                report_candidates(fcx, &mut err, span, item_name, static_sources);
             }
 
             if !unsatisfied_predicates.is_empty() {
@@ -145,7 +153,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                      p))
                     .collect::<Vec<_>>()
                     .join(", ");
-                cx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     &format!("the method `{}` exists but the \
                              following trait bounds were not satisfied: {}",
@@ -153,15 +161,17 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                              bound_list));
             }
 
-            suggest_traits_to_import(fcx, span, rcvr_ty, item_name,
-                                     rcvr_expr, out_of_scope_traits)
+            suggest_traits_to_import(fcx, &mut err, span, rcvr_ty, item_name,
+                                     rcvr_expr, out_of_scope_traits);
+            err.emit();
         }
 
         MethodError::Ambiguity(sources) => {
-            span_err!(fcx.sess(), span, E0034,
-                      "multiple applicable items in scope");
+            let mut err = struct_span_err!(fcx.sess(), span, E0034,
+                                           "multiple applicable items in scope");
 
-            report_candidates(fcx, span, item_name, sources);
+            report_candidates(fcx, &mut err, span, item_name, sources);
+            err.emit();
         }
 
         MethodError::ClosureAmbiguity(trait_def_id) => {
@@ -181,6 +191,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     }
 
     fn report_candidates(fcx: &FnCtxt,
+                         err: &mut DiagnosticBuilder,
                          span: Span,
                          item_name: ast::Name,
                          mut sources: Vec<CandidateSource>) {
@@ -213,7 +224,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                         }
                     };
 
-                    span_note!(fcx.sess(), item_span,
+                    span_note!(err, item_span,
                                "candidate #{} is defined in an impl{} for the type `{}`",
                                idx + 1,
                                insertion,
@@ -222,7 +233,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                 CandidateSource::TraitSource(trait_did) => {
                     let item = trait_item(fcx.tcx(), trait_did, item_name).unwrap();
                     let item_span = fcx.tcx().map.def_id_span(item.def_id(), span);
-                    span_note!(fcx.sess(), item_span,
+                    span_note!(err, item_span,
                                "candidate #{} is defined in the trait `{}`",
                                idx + 1,
                                fcx.tcx().item_path_str(trait_did));
@@ -236,6 +247,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 pub type AllTraitsVec = Vec<TraitInfo>;
 
 fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                      err: &mut DiagnosticBuilder,
                                       span: Span,
                                       rcvr_ty: Ty<'tcx>,
                                       item_name: ast::Name,
@@ -255,14 +267,13 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
             one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
 
-        fcx.sess().fileline_help(span, &msg[..]);
+        err.fileline_help(span, &msg[..]);
 
         for (i, trait_did) in candidates.iter().enumerate() {
-            fcx.sess().fileline_help(span,
-                                     &*format!("candidate #{}: use `{}`",
-                                               i + 1,
-                                               fcx.tcx().item_path_str(*trait_did)))
-
+            err.fileline_help(span,
+                              &*format!("candidate #{}: use `{}`",
+                                        i + 1,
+                                        fcx.tcx().item_path_str(*trait_did)));
         }
         return
     }
@@ -301,13 +312,13 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
             name = item_name);
 
-        fcx.sess().fileline_help(span, &msg[..]);
+        err.fileline_help(span, &msg[..]);
 
         for (i, trait_info) in candidates.iter().enumerate() {
-            fcx.sess().fileline_help(span,
-                                     &*format!("candidate #{}: `{}`",
-                                               i + 1,
-                                               fcx.tcx().item_path_str(trait_info.def_id)))
+            err.fileline_help(span,
+                              &*format!("candidate #{}: `{}`",
+                                        i + 1,
+                                        fcx.tcx().item_path_str(trait_info.def_id)));
         }
     }
 }
index e87af3a5dfbcb4ff15102920537bbc65913f173e..d501641b5389969bcdf5bce929f895d0890a1f2a 100644 (file)
@@ -82,6 +82,7 @@ use self::TupleArgumentsFlag::*;
 
 use astconv::{self, ast_region_to_region, ast_ty_to_ty, AstConv, PathParamMode};
 use check::_match::pat_ctxt;
+use dep_graph::DepNode;
 use fmt_macros::{Parser, Piece, Position};
 use middle::astconv_util::prohibit_type_params;
 use middle::cstore::LOCAL_CRATE;
@@ -93,10 +94,10 @@ use middle::pat_util::{self, pat_id_map};
 use middle::privacy::{AllPublic, LastMod};
 use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace};
 use middle::traits::{self, report_fulfillment_errors};
-use middle::ty::{FnSig, GenericPredicates, TypeScheme};
+use middle::ty::{GenericPredicates, TypeScheme};
 use middle::ty::{Disr, ParamTy, ParameterEnvironment};
 use middle::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
-use middle::ty::{self, HasTypeFlags, RegionEscape, ToPolyTraitRef, Ty};
+use middle::ty::{self, ToPolyTraitRef, Ty};
 use middle::ty::{MethodCall, MethodCallee};
 use middle::ty::adjustment;
 use middle::ty::error::TypeError;
@@ -119,15 +120,14 @@ use syntax::ast;
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
 use syntax::codemap::{self, Span, Spanned};
-use syntax::owned_slice::OwnedSlice;
+use syntax::errors::DiagnosticBuilder;
 use syntax::parse::token::{self, InternedString};
 use syntax::ptr::P;
-use syntax::util::lev_distance::lev_distance;
+use syntax::util::lev_distance::find_best_match_for_name;
 
 use rustc_front::intravisit::{self, Visitor};
 use rustc_front::hir;
 use rustc_front::hir::Visibility;
-use rustc_front::hir::{Item, ItemImpl};
 use rustc_front::print::pprust;
 use rustc_back::slice;
 
@@ -140,7 +140,6 @@ pub mod coercion;
 pub mod demand;
 pub mod method;
 mod upvar;
-mod wf;
 mod wfcheck;
 mod cast;
 mod closure;
@@ -319,7 +318,7 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> {
                                         body_id: ast::NodeId,
                                         value: &T)
                                         -> T
-        where T : TypeFoldable<'tcx> + HasTypeFlags
+        where T : TypeFoldable<'tcx>
     {
         let mut fulfillment_cx = self.infcx.fulfillment_cx.borrow_mut();
         assoc::normalize_associated_types_in(&self.infcx,
@@ -384,62 +383,45 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckItemBodiesVisitor<'a, 'tcx> {
     }
 }
 
-pub fn check_wf_old(ccx: &CrateCtxt) {
-    // FIXME(#25759). The new code below is much more reliable but (for now)
-    // only generates warnings. So as to ensure that we continue
-    // getting errors where we used to get errors, we run the old wf
-    // code first and abort if it encounters any errors. If no abort
-    // comes, we run the new code and issue warnings.
-    let krate = ccx.tcx.map.krate();
-    let mut visit = wf::CheckTypeWellFormedVisitor::new(ccx);
-    krate.visit_all_items(&mut visit);
-
-    // If types are not well-formed, it leads to all manner of errors
-    // downstream, so stop reporting errors at this point.
-    ccx.tcx.sess.abort_if_errors();
-}
-
 pub fn check_wf_new(ccx: &CrateCtxt) {
-    let krate = ccx.tcx.map.krate();
-    let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(ccx);
-    krate.visit_all_items(&mut visit);
-
-    // If types are not well-formed, it leads to all manner of errors
-    // downstream, so stop reporting errors at this point.
-    ccx.tcx.sess.abort_if_errors();
+    ccx.tcx.sess.abort_if_new_errors(|| {
+        let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(ccx);
+        ccx.tcx.visit_all_items_in_krate(DepNode::WfCheck, &mut visit);
+    });
 }
 
 pub fn check_item_types(ccx: &CrateCtxt) {
-    let krate = ccx.tcx.map.krate();
-    let mut visit = CheckItemTypesVisitor { ccx: ccx };
-    krate.visit_all_items(&mut visit);
-    ccx.tcx.sess.abort_if_errors();
+    ccx.tcx.sess.abort_if_new_errors(|| {
+        let mut visit = CheckItemTypesVisitor { ccx: ccx };
+        ccx.tcx.visit_all_items_in_krate(DepNode::TypeckItemType, &mut visit);
+    });
 }
 
 pub fn check_item_bodies(ccx: &CrateCtxt) {
-    let krate = ccx.tcx.map.krate();
-    let mut visit = CheckItemBodiesVisitor { ccx: ccx };
-    krate.visit_all_items(&mut visit);
-
-    ccx.tcx.sess.abort_if_errors();
+    ccx.tcx.sess.abort_if_new_errors(|| {
+        let mut visit = CheckItemBodiesVisitor { ccx: ccx };
+        ccx.tcx.visit_all_items_in_krate(DepNode::TypeckItemBody, &mut visit);
+    });
 }
 
 pub fn check_drop_impls(ccx: &CrateCtxt) {
-    let drop_trait = match ccx.tcx.lang_items.drop_trait() {
-        Some(id) => ccx.tcx.lookup_trait_def(id), None => { return }
-    };
-    drop_trait.for_each_impl(ccx.tcx, |drop_impl_did| {
-        if drop_impl_did.is_local() {
-            match dropck::check_drop_impl(ccx.tcx, drop_impl_did) {
-                Ok(()) => {}
-                Err(()) => {
-                    assert!(ccx.tcx.sess.has_errors());
+    ccx.tcx.sess.abort_if_new_errors(|| {
+        let _task = ccx.tcx.dep_graph.in_task(DepNode::Dropck);
+        let drop_trait = match ccx.tcx.lang_items.drop_trait() {
+            Some(id) => ccx.tcx.lookup_trait_def(id), None => { return }
+        };
+        drop_trait.for_each_impl(ccx.tcx, |drop_impl_did| {
+            let _task = ccx.tcx.dep_graph.in_task(DepNode::DropckImpl(drop_impl_did));
+            if drop_impl_did.is_local() {
+                match dropck::check_drop_impl(ccx.tcx, drop_impl_did) {
+                    Ok(()) => {}
+                    Err(()) => {
+                        assert!(ccx.tcx.sess.has_errors());
+                    }
                 }
             }
-        }
+        });
     });
-
-    ccx.tcx.sess.abort_if_errors();
 }
 
 fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
@@ -456,7 +438,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
             let inh = Inherited::new(ccx.tcx, &tables, param_env);
 
             // Compute the fty from point of view of inside fn.
-            let fn_scope = ccx.tcx.region_maps.item_extent(body.id);
+            let fn_scope = ccx.tcx.region_maps.call_site_extent(fn_id, body.id);
             let fn_sig =
                 fn_ty.sig.subst(ccx.tcx, &inh.infcx.parameter_environment.free_substs);
             let fn_sig =
@@ -721,11 +703,12 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
             for item in &m.items {
                 let pty = ccx.tcx.lookup_item_type(ccx.tcx.map.local_def_id(item.id));
                 if !pty.generics.types.is_empty() {
-                    span_err!(ccx.tcx.sess, item.span, E0044,
+                    let mut err = struct_span_err!(ccx.tcx.sess, item.span, E0044,
                         "foreign items may not have type parameters");
-                    span_help!(ccx.tcx.sess, item.span,
+                    span_help!(&mut err, item.span,
                         "consider using specialization instead of \
                         type parameters");
+                    err.emit();
                 }
 
                 if let hir::ForeignItemFn(ref fn_decl, _) = item.node {
@@ -891,75 +874,71 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     for impl_item in impl_items {
         let ty_impl_item = ccx.tcx.impl_or_trait_item(ccx.tcx.map.local_def_id(impl_item.id));
         let ty_trait_item = trait_items.iter()
-            .find(|ac| ac.name() == ty_impl_item.name())
-            .unwrap_or_else(|| {
-                // This is checked by resolve
-                tcx.sess.span_bug(impl_item.span,
-                                  &format!("impl-item `{}` is not a member of `{:?}`",
-                                           ty_impl_item.name(),
-                                           impl_trait_ref));
-            });
-        match impl_item.node {
-            hir::ImplItemKind::Const(..) => {
-                let impl_const = match ty_impl_item {
-                    ty::ConstTraitItem(ref cti) => cti,
-                    _ => tcx.sess.span_bug(impl_item.span, "non-const impl-item for const")
-                };
+            .find(|ac| ac.name() == ty_impl_item.name());
 
-                // Find associated const definition.
-                if let &ty::ConstTraitItem(ref trait_const) = ty_trait_item {
-                    compare_const_impl(ccx.tcx,
-                                       &impl_const,
-                                       impl_item.span,
-                                       trait_const,
-                                       &*impl_trait_ref);
-                } else {
-                    span_err!(tcx.sess, impl_item.span, E0323,
-                              "item `{}` is an associated const, \
-                              which doesn't match its trait `{:?}`",
-                              impl_const.name,
-                              impl_trait_ref)
+        if let Some(ty_trait_item) = ty_trait_item {
+            match impl_item.node {
+                hir::ImplItemKind::Const(..) => {
+                    let impl_const = match ty_impl_item {
+                        ty::ConstTraitItem(ref cti) => cti,
+                        _ => tcx.sess.span_bug(impl_item.span, "non-const impl-item for const")
+                    };
+
+                    // Find associated const definition.
+                    if let &ty::ConstTraitItem(ref trait_const) = ty_trait_item {
+                        compare_const_impl(ccx.tcx,
+                                           &impl_const,
+                                           impl_item.span,
+                                           trait_const,
+                                           &*impl_trait_ref);
+                    } else {
+                        span_err!(tcx.sess, impl_item.span, E0323,
+                                  "item `{}` is an associated const, \
+                                  which doesn't match its trait `{:?}`",
+                                  impl_const.name,
+                                  impl_trait_ref)
+                    }
                 }
-            }
-            hir::ImplItemKind::Method(ref sig, ref body) => {
-                check_trait_fn_not_const(ccx, impl_item.span, sig.constness);
+                hir::ImplItemKind::Method(ref sig, ref body) => {
+                    check_trait_fn_not_const(ccx, impl_item.span, sig.constness);
 
-                let impl_method = match ty_impl_item {
-                    ty::MethodTraitItem(ref mti) => mti,
-                    _ => tcx.sess.span_bug(impl_item.span, "non-method impl-item for method")
-                };
+                    let impl_method = match ty_impl_item {
+                        ty::MethodTraitItem(ref mti) => mti,
+                        _ => tcx.sess.span_bug(impl_item.span, "non-method impl-item for method")
+                    };
 
-                if let &ty::MethodTraitItem(ref trait_method) = ty_trait_item {
-                    compare_impl_method(ccx.tcx,
-                                        &impl_method,
-                                        impl_item.span,
-                                        body.id,
-                                        &trait_method,
-                                        &impl_trait_ref);
-                } else {
-                    span_err!(tcx.sess, impl_item.span, E0324,
-                              "item `{}` is an associated method, \
-                              which doesn't match its trait `{:?}`",
-                              impl_method.name,
-                              impl_trait_ref)
+                    if let &ty::MethodTraitItem(ref trait_method) = ty_trait_item {
+                        compare_impl_method(ccx.tcx,
+                                            &impl_method,
+                                            impl_item.span,
+                                            body.id,
+                                            &trait_method,
+                                            &impl_trait_ref);
+                    } else {
+                        span_err!(tcx.sess, impl_item.span, E0324,
+                                  "item `{}` is an associated method, \
+                                  which doesn't match its trait `{:?}`",
+                                  impl_method.name,
+                                  impl_trait_ref)
+                    }
                 }
-            }
-            hir::ImplItemKind::Type(_) => {
-                let impl_type = match ty_impl_item {
-                    ty::TypeTraitItem(ref tti) => tti,
-                    _ => tcx.sess.span_bug(impl_item.span, "non-type impl-item for type")
-                };
+                hir::ImplItemKind::Type(_) => {
+                    let impl_type = match ty_impl_item {
+                        ty::TypeTraitItem(ref tti) => tti,
+                        _ => tcx.sess.span_bug(impl_item.span, "non-type impl-item for type")
+                    };
 
-                if let &ty::TypeTraitItem(ref at) = ty_trait_item {
-                    if let Some(_) = at.ty {
-                        overridden_associated_type = Some(impl_item);
+                    if let &ty::TypeTraitItem(ref at) = ty_trait_item {
+                        if let Some(_) = at.ty {
+                            overridden_associated_type = Some(impl_item);
+                        }
+                    } else {
+                        span_err!(tcx.sess, impl_item.span, E0325,
+                                  "item `{}` is an associated type, \
+                                  which doesn't match its trait `{:?}`",
+                                  impl_type.name,
+                                  impl_trait_ref)
                     }
-                } else {
-                    span_err!(tcx.sess, impl_item.span, E0325,
-                              "item `{}` is an associated type, \
-                              which doesn't match its trait `{:?}`",
-                              impl_type.name,
-                              impl_trait_ref)
                 }
             }
         }
@@ -1063,7 +1042,7 @@ fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         return;
     }
     let tstr = fcx.infcx().ty_to_string(t_cast);
-    fcx.type_error_message(span, |actual| {
+    let mut err = fcx.type_error_struct(span, |actual| {
         format!("cast to unsized type: `{}` as `{}`", actual, tstr)
     }, t_expr, None);
     match t_expr.sty {
@@ -1075,16 +1054,16 @@ fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             if t_cast.is_trait() {
                 match fcx.tcx().sess.codemap().span_to_snippet(t_span) {
                     Ok(s) => {
-                        fcx.tcx().sess.span_suggestion(t_span,
-                                                       "try casting to a reference instead:",
-                                                       format!("&{}{}", mtstr, s));
+                        err.span_suggestion(t_span,
+                                            "try casting to a reference instead:",
+                                            format!("&{}{}", mtstr, s));
                     },
                     Err(_) =>
-                        span_help!(fcx.tcx().sess, t_span,
+                        span_help!(err, t_span,
                                    "did you mean `&{}{}`?", mtstr, tstr),
                 }
             } else {
-                span_help!(fcx.tcx().sess, span,
+                span_help!(err, span,
                            "consider using an implicit coercion to `&{}{}` instead",
                            mtstr, tstr);
             }
@@ -1092,19 +1071,20 @@ fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         ty::TyBox(..) => {
             match fcx.tcx().sess.codemap().span_to_snippet(t_span) {
                 Ok(s) => {
-                    fcx.tcx().sess.span_suggestion(t_span,
-                                                   "try casting to a `Box` instead:",
-                                                   format!("Box<{}>", s));
+                    err.span_suggestion(t_span,
+                                                          "try casting to a `Box` instead:",
+                                                           format!("Box<{}>", s));
                 },
                 Err(_) =>
-                    span_help!(fcx.tcx().sess, t_span, "did you mean `Box<{}>`?", tstr),
+                    span_help!(err, t_span, "did you mean `Box<{}>`?", tstr),
             }
         }
         _ => {
-            span_help!(fcx.tcx().sess, e_span,
+            span_help!(err, e_span,
                        "consider using a box or reference as appropriate");
         }
     }
+    err.emit();
     fcx.write_error(id);
 }
 
@@ -1357,7 +1337,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                   substs: &Substs<'tcx>,
                                   value: &T)
                                   -> T
-        where T : TypeFoldable<'tcx> + HasTypeFlags
+        where T : TypeFoldable<'tcx>
     {
         let value = value.subst(self.tcx(), substs);
         let result = self.normalize_associated_types_in(span, &value);
@@ -1383,7 +1363,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
 
     fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
-        where T : TypeFoldable<'tcx> + HasTypeFlags
+        where T : TypeFoldable<'tcx>
     {
         self.inh.normalize_associated_types_in(span, self.body_id, value)
     }
@@ -1469,10 +1449,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Some((adt, variant))
         } else if var_kind == ty::VariantKind::Unit {
             if !self.tcx().sess.features.borrow().braced_empty_structs {
-                self.tcx().sess.span_err(span, "empty structs and enum variants \
-                                                with braces are unstable");
-                fileline_help!(self.tcx().sess, span, "add #![feature(braced_empty_structs)] to \
-                                                       the crate features to enable");
+                let mut err = self.tcx().sess.struct_span_err(span,
+                                                              "empty structs and enum variants \
+                                                               with braces are unstable");
+                fileline_help!(&mut err, span, "add #![feature(braced_empty_structs)] to \
+                                                the crate features to enable");
+                err.emit();
             }
 
              Some((adt, variant))
@@ -1640,12 +1622,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                  sp: Span,
                                  mk_msg: M,
                                  actual_ty: Ty<'tcx>,
-                                 err: Option<&TypeError<'tcx>>) where
-        M: FnOnce(String) -> String,
+                                 err: Option<&TypeError<'tcx>>)
+        where M: FnOnce(String) -> String,
     {
         self.infcx().type_error_message(sp, mk_msg, actual_ty, err);
     }
 
+    pub fn type_error_struct<M>(&self,
+                                sp: Span,
+                                mk_msg: M,
+                                actual_ty: Ty<'tcx>,
+                                err: Option<&TypeError<'tcx>>)
+                                -> DiagnosticBuilder<'tcx>
+        where M: FnOnce(String) -> String,
+    {
+        self.infcx().type_error_struct(sp, mk_msg, actual_ty, err)
+    }
+
     pub fn report_mismatched_types(&self,
                                    sp: Span,
                                    e: Ty<'tcx>,
@@ -2674,6 +2667,14 @@ fn check_lit<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     }
 }
 
+fn check_expr_eq_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                expr: &'tcx hir::Expr,
+                                expected: Ty<'tcx>) {
+    check_expr_with_unifier(
+        fcx, expr, ExpectHasType(expected), NoPreference,
+        || demand::eqtype(fcx, expr.span, expected, fcx.expr_ty(expr)));
+}
+
 pub fn check_expr_has_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                      expr: &'tcx hir::Expr,
                                      expected: Ty<'tcx>) {
@@ -2931,7 +2932,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                             lvalue_pref: LvaluePreference,
                             base: &'tcx hir::Expr,
                             field: &Spanned<ast::Name>) {
-        let tcx = fcx.ccx.tcx;
         check_expr_with_lvalue_pref(fcx, base, lvalue_pref);
         let expr_t = structurally_resolved_type(fcx, expr.span,
                                                 fcx.expr_ty(base));
@@ -2963,19 +2963,18 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         }
 
         if method::exists(fcx, field.span, field.node, expr_t, expr.id) {
-            fcx.type_error_message(
-                field.span,
-                |actual| {
-                    format!("attempted to take value of method `{}` on type \
-                            `{}`", field.node, actual)
-                },
-                expr_t, None);
-
-            tcx.sess.fileline_help(field.span,
+            fcx.type_error_struct(field.span,
+                                  |actual| {
+                                       format!("attempted to take value of method `{}` on type \
+                                               `{}`", field.node, actual)
+                                   },
+                                   expr_t, None)
+                .fileline_help(field.span,
                                "maybe a `()` to call it is missing? \
-                               If not, try an anonymous function");
+                               If not, try an anonymous function")
+                .emit();
         } else {
-            fcx.type_error_message(
+            let mut err = fcx.type_error_struct(
                 expr.span,
                 |actual| {
                     format!("attempted access of field `{}` on \
@@ -2986,41 +2985,36 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                 },
                 expr_t, None);
             if let ty::TyStruct(def, _) = expr_t.sty {
-                suggest_field_names(def.struct_variant(), field, tcx, vec![]);
+                suggest_field_names(&mut err, def.struct_variant(), field, vec![]);
             }
+            err.emit();
         }
 
         fcx.write_error(expr.id);
     }
 
     // displays hints about the closest matches in field names
-    fn suggest_field_names<'tcx>(variant: ty::VariantDef<'tcx>,
+    fn suggest_field_names<'tcx>(err: &mut DiagnosticBuilder,
+                                 variant: ty::VariantDef<'tcx>,
                                  field: &Spanned<ast::Name>,
-                                 tcx: &ty::ctxt<'tcx>,
                                  skip : Vec<InternedString>) {
         let name = field.node.as_str();
+        let names = variant.fields
+                    .iter()
+                    .filter_map(|ref field| {
+                        // ignore already set fields and private fields from non-local crates
+                        if skip.iter().any(|x| *x == field.name.as_str()) ||
+                           (variant.did.krate != LOCAL_CRATE && field.vis != Visibility::Public) {
+                               None
+                        } else {
+                            Some(&field.name)
+                        }
+                    });
+
         // only find fits with at least one matching letter
-        let mut best_dist = name.len();
-        let mut best = None;
-        for elem in &variant.fields {
-            let n = elem.name.as_str();
-            // ignore already set fields
-            if skip.iter().any(|x| *x == n) {
-                continue;
-            }
-            // ignore private fields from non-local crates
-            if variant.did.krate != LOCAL_CRATE && elem.vis != Visibility::Public {
-                continue;
-            }
-            let dist = lev_distance(&n, &name);
-            if dist < best_dist {
-                best = Some(n);
-                best_dist = dist;
-            }
-        }
-        if let Some(n) = best {
-            tcx.sess.span_help(field.span,
-                &format!("did you mean `{}`?", n));
+        if let Some(name) = find_best_match_for_name(names, &name, Some(name.len())) {
+            err.span_help(field.span,
+                          &format!("did you mean `{}`?", name));
         }
     }
 
@@ -3095,7 +3089,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                       variant: ty::VariantDef<'tcx>,
                                       field: &hir::Field,
                                       skip_fields: &[hir::Field]) {
-        fcx.type_error_message(
+        let mut err = fcx.type_error_struct(
             field.name.span,
             |actual| if let ty::TyEnum(..) = ty.sty {
                 format!("struct variant `{}::{}` has no field named `{}`",
@@ -3108,7 +3102,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             None);
         // prevent all specified fields from being suggested
         let skip_fields = skip_fields.iter().map(|ref x| x.name.node.as_str());
-        suggest_field_names(variant, &field.name, fcx.tcx(), skip_fields.collect());
+        suggest_field_names(&mut err, variant, &field.name, skip_fields.collect());
+        err.emit();
     }
 
     fn check_expr_struct_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
@@ -3159,12 +3154,13 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             !remaining_fields.is_empty()
         {
             span_err!(tcx.sess, span, E0063,
-                      "missing field{}: {}",
+                      "missing field{} {} in initializer of `{}`",
                       if remaining_fields.len() == 1 {""} else {"s"},
                       remaining_fields.keys()
                                       .map(|n| format!("`{}`", n))
                                       .collect::<Vec<_>>()
-                                      .join(", "));
+                                      .join(", "),
+                      adt_ty);
         }
 
     }
@@ -3195,6 +3191,10 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
 
         // Find the relevant variant
         let def = lookup_full_def(tcx, path.span, expr.id);
+        if def == def::DefErr {
+            check_struct_fields_on_error(fcx, expr.id, fields, base_expr);
+            return;
+        }
         let (adt, variant) = match fcx.def_struct_variant(def, path.span) {
             Some((adt, variant)) => (adt, variant),
             None => {
@@ -3373,17 +3373,21 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
           if let Some((opt_ty, segments, def)) =
                   resolve_ty_and_def_ufcs(fcx, path_res, opt_self_ty, path,
                                           expr.span, expr.id) {
-              let (scheme, predicates) = type_scheme_and_predicates_for_def(fcx,
-                                                                            expr.span,
-                                                                            def);
-              instantiate_path(fcx,
-                               segments,
-                               scheme,
-                               &predicates,
-                               opt_ty,
-                               def,
-                               expr.span,
-                               id);
+              if def != def::DefErr {
+                  let (scheme, predicates) = type_scheme_and_predicates_for_def(fcx,
+                                                                                expr.span,
+                                                                                def);
+                  instantiate_path(fcx,
+                                   segments,
+                                   scheme,
+                                   &predicates,
+                                   opt_ty,
+                                   def,
+                                   expr.span,
+                                   id);
+              } else {
+                  fcx.write_ty(id, fcx.tcx().types.err);
+              }
           }
 
           // We always require that the type provided as the value for
@@ -3396,8 +3400,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
           for &(_, ref input) in &ia.inputs {
               check_expr(fcx, &**input);
           }
-          for &(_, ref out, _) in &ia.outputs {
-              check_expr(fcx, &**out);
+          for out in &ia.outputs {
+              check_expr(fcx, &*out.expr);
           }
           fcx.write_nil(id);
       }
@@ -3528,6 +3532,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             deferred_cast_checks.push(cast_check);
         }
       }
+      hir::ExprType(ref e, ref t) => {
+        let typ = fcx.to_ty(&**t);
+        check_expr_eq_type(fcx, &**e, typ);
+        fcx.write_ty(id, typ);
+      }
       hir::ExprVec(ref args) => {
         let uty = expected.to_option(fcx).and_then(|uty| {
             match uty.sty {
@@ -3776,35 +3785,8 @@ pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>,
                                                         def::Def)>
 {
 
-    // Associated constants can't depend on generic types.
-    fn have_disallowed_generic_consts<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                                def: def::Def,
-                                                ty: Ty<'tcx>,
-                                                span: Span,
-                                                node_id: ast::NodeId) -> bool {
-        match def {
-            def::DefAssociatedConst(..) => {
-                if ty.has_param_types() || ty.has_self_ty() {
-                    span_err!(fcx.sess(), span, E0329,
-                              "Associated consts cannot depend \
-                               on type parameters or Self.");
-                    fcx.write_error(node_id);
-                    return true;
-                }
-            }
-            _ => {}
-        }
-        false
-    }
-
     // If fully resolved already, we don't have to do anything.
     if path_res.depth == 0 {
-        if let Some(ty) = opt_self_ty {
-            if have_disallowed_generic_consts(fcx, path_res.full_def(), ty,
-                                              span, node_id) {
-                return None;
-            }
-        }
         Some((opt_self_ty, &path.segments, path_res.base_def))
     } else {
         let mut def = path_res.base_def;
@@ -3820,9 +3802,6 @@ pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>,
         let item_name = item_segment.identifier.name;
         match method::resolve_ufcs(fcx, span, item_name, ty, node_id) {
             Ok((def, lp)) => {
-                if have_disallowed_generic_consts(fcx, def, ty, span, node_id) {
-                    return None;
-                }
                 // Write back the new resolution.
                 fcx.ccx.tcx.def_map.borrow_mut()
                        .insert(node_id, def::PathResolution {
@@ -4157,9 +4136,9 @@ pub fn check_representable(tcx: &ty::ctxt,
     // caught by case 1.
     match rty.is_representable(tcx, sp) {
         Representability::SelfRecursive => {
-            span_err!(tcx.sess, sp, E0072, "invalid recursive {} type", designation);
-            tcx.sess.fileline_help(
-                sp, "wrap the inner value in a box to make it representable");
+            struct_span_err!(tcx.sess, sp, E0072, "invalid recursive {} type", designation)
+                .fileline_help(sp, "wrap the inner value in a box to make it representable")
+                .emit();
             return false
         }
         Representability::Representable | Representability::ContainsRecursive => (),
@@ -4199,7 +4178,7 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
                                     sp: Span,
                                     vs: &'tcx [hir::Variant],
                                     id: ast::NodeId) {
-
+    // disr_in_range should be removed once we have forced type hints for consts
     fn disr_in_range(ccx: &CrateCtxt,
                      ty: attr::IntType,
                      disr: ty::Disr) -> bool {
@@ -4256,11 +4235,12 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
             // Check for duplicate discriminant values
             match disr_vals.iter().position(|&x| x == current_disr_val) {
                 Some(i) => {
-                    span_err!(ccx.tcx.sess, v.span, E0081,
+                    let mut err = struct_span_err!(ccx.tcx.sess, v.span, E0081,
                         "discriminant value `{}` already exists", disr_vals[i]);
                     let variant_i_node_id = ccx.tcx.map.as_local_node_id(variants[i].did).unwrap();
-                    span_note!(ccx.tcx.sess, ccx.tcx.map.span(variant_i_node_id),
-                        "conflicting discriminant here")
+                    span_note!(&mut err, ccx.tcx.map.span(variant_i_node_id),
+                        "conflicting discriminant here");
+                    err.emit();
                 }
                 None => {}
             }
@@ -4269,10 +4249,11 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
                 attr::ReprAny | attr::ReprExtern => (),
                 attr::ReprInt(sp, ity) => {
                     if !disr_in_range(ccx, ity, current_disr_val) {
-                        span_err!(ccx.tcx.sess, v.span, E0082,
+                        let mut err = struct_span_err!(ccx.tcx.sess, v.span, E0082,
                             "discriminant value outside specified type");
-                        span_note!(ccx.tcx.sess, sp,
+                        span_note!(&mut err, sp,
                             "discriminant type specified here");
+                        err.emit();
                     }
                 }
                 attr::ReprSimd => {
@@ -4327,9 +4308,9 @@ fn type_scheme_and_predicates_for_def<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         def::DefTyParam(..) |
         def::DefMod(..) |
         def::DefForeignMod(..) |
-        def::DefUse(..) |
         def::DefLabel(..) |
-        def::DefSelfTy(..) => {
+        def::DefSelfTy(..) |
+        def::DefErr => {
             fcx.ccx.tcx.sess.span_bug(sp, &format!("expected value, found {:?}", defn));
         }
     }
@@ -4497,9 +4478,9 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         def::DefMod(..) |
         def::DefForeignMod(..) |
         def::DefLocal(..) |
-        def::DefUse(..) |
         def::DefLabel(..) |
-        def::DefUpvar(..) => {
+        def::DefUpvar(..) |
+        def::DefErr => {
             segment_spaces = vec![None; segments.len()];
         }
     }
@@ -4908,7 +4889,7 @@ pub fn may_break(cx: &ty::ctxt, id: ast::NodeId, b: &hir::Block) -> bool {
 }
 
 pub fn check_bounds_are_used<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
-                                       tps: &OwnedSlice<hir::TyParam>,
+                                       tps: &[hir::TyParam],
                                        ty: Ty<'tcx>) {
     debug!("check_bounds_are_used(n_tps={}, ty={:?})",
            tps.len(),  ty);
index 0c65f68f02e3e8fac792447de3f95495271511d6..f4841b75d13d55e8da0b3beb3efa8d158e3d60cd 100644 (file)
@@ -19,7 +19,7 @@ use super::{
     FnCtxt,
 };
 use middle::def_id::DefId;
-use middle::ty::{Ty, HasTypeFlags, PreferMutLvalue};
+use middle::ty::{Ty, TypeFoldable, PreferMutLvalue};
 use syntax::ast;
 use syntax::parse::token;
 use rustc_front::hir;
@@ -187,10 +187,10 @@ fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                               hir_util::binop_to_string(op.node),
                               lhs_ty);
                 } else {
-                    span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
-                              "binary operation `{}` cannot be applied to type `{}`",
-                              hir_util::binop_to_string(op.node),
-                              lhs_ty);
+                    let mut err = struct_span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
+                        "binary operation `{}` cannot be applied to type `{}`",
+                        hir_util::binop_to_string(op.node),
+                        lhs_ty);
                     let missing_trait = match op.node {
                         hir::BiAdd    => Some("std::ops::Add"),
                         hir::BiSub    => Some("std::ops::Sub"),
@@ -208,10 +208,11 @@ fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                     };
 
                     if let Some(missing_trait) = missing_trait {
-                        span_note!(fcx.tcx().sess, lhs_expr.span,
+                        span_note!(&mut err, lhs_expr.span,
                                    "an implementation of `{}` might be missing for `{}`",
                                     missing_trait, lhs_ty);
                     }
+                    err.emit();
                 }
             }
             fcx.tcx().types.err
index f980945dbf2206f12b02410727e0ce1f4850b078..47cd31d9898d19bb9f4e48fc14f45ae8bbc3718f 100644 (file)
@@ -61,7 +61,7 @@
 //!
 //!     struct Foo { i: i32 }
 //!     struct Bar { foo: Foo  }
-//!     fn get_i(x: &'a Bar) -> &'a i32 {
+//!     fn get_i<'a>(x: &'a Bar) -> &'a i32 {
 //!        let foo = &x.foo; // Lifetime L1
 //!        &foo.i            // Lifetime L2
 //!     }
@@ -89,17 +89,16 @@ use middle::free_region::FreeRegionMap;
 use middle::implicator::{self, Implication};
 use middle::mem_categorization as mc;
 use middle::mem_categorization::Categorization;
-use middle::region::CodeExtent;
+use middle::region::{self, CodeExtent};
 use middle::subst::Substs;
 use middle::traits;
-use middle::ty::{self, RegionEscape, ReScope, Ty, MethodCall, HasTypeFlags};
+use middle::ty::{self, Ty, MethodCall, TypeFoldable};
 use middle::infer::{self, GenericKind, InferCtxt, SubregionOrigin, TypeOrigin, VerifyBound};
 use middle::pat_util;
 use middle::ty::adjustment;
 use middle::ty::wf::ImpliedBound;
 
 use std::mem;
-use std::rc::Rc;
 use syntax::ast;
 use syntax::codemap::Span;
 use rustc_front::intravisit::{self, Visitor};
@@ -180,6 +179,9 @@ pub struct Rcx<'a, 'tcx: 'a> {
     // id of innermost fn body id
     body_id: ast::NodeId,
 
+    // call_site scope of innermost fn
+    call_site_scope: Option<CodeExtent>,
+
     // id of innermost fn or loop
     repeating_scope: ast::NodeId,
 
@@ -200,6 +202,7 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
         Rcx { fcx: fcx,
               repeating_scope: initial_repeating_scope,
               body_id: initial_body_id,
+              call_site_scope: None,
               subject: subject,
               region_bound_pairs: Vec::new(),
               free_region_map: FreeRegionMap::new(),
@@ -214,6 +217,10 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
         self.fcx.infcx()
     }
 
+    fn set_call_site_scope(&mut self, call_site_scope: Option<CodeExtent>) -> Option<CodeExtent> {
+        mem::replace(&mut self.call_site_scope, call_site_scope)
+    }
+
     fn set_body_id(&mut self, body_id: ast::NodeId) -> ast::NodeId {
         mem::replace(&mut self.body_id, body_id)
     }
@@ -275,7 +282,7 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
     }
 
     fn visit_fn_body(&mut self,
-                     id: ast::NodeId,
+                     id: ast::NodeId, // the id of the fn itself
                      fn_decl: &hir::FnDecl,
                      body: &hir::Block,
                      span: Span)
@@ -283,6 +290,10 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
         // When we enter a function, we can derive
         debug!("visit_fn_body(id={})", id);
 
+        let call_site = self.fcx.tcx().region_maps.lookup_code_extent(
+            region::CodeExtentData::CallSiteScope { fn_id: id, body_id: body.id });
+        let old_call_site_scope = self.set_call_site_scope(Some(call_site));
+
         let fn_sig = {
             let fn_sig_map = &self.infcx().tables.borrow().liberated_fn_sigs;
             match fn_sig_map.get(&id) {
@@ -300,7 +311,7 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
         // For the return type, if diverging, substitute `bool` just
         // because it will have no effect.
         //
-        // FIXME(#25759) return types should not be implied bounds
+        // FIXME(#27579) return types should not be implied bounds
         let fn_sig_tys: Vec<_> =
             fn_sig.inputs.iter()
                          .cloned()
@@ -315,9 +326,18 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
         self.visit_block(body);
         self.visit_region_obligations(body.id);
 
+        let call_site_scope = self.call_site_scope.unwrap();
+        debug!("visit_fn_body body.id {} call_site_scope: {:?}",
+               body.id, call_site_scope);
+        type_of_node_must_outlive(self,
+                                  infer::CallReturn(span),
+                                  body.id,
+                                  ty::ReScope(call_site_scope));
+
         self.region_bound_pairs.truncate(old_region_bounds_pairs_len);
 
         self.set_body_id(old_body_id);
+        self.set_call_site_scope(old_call_site_scope);
     }
 
     fn visit_region_obligations(&mut self, node_id: ast::NodeId)
@@ -405,8 +425,6 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
                       code: &traits::ObligationCauseCode<'tcx>)
                       -> SubregionOrigin<'tcx> {
         match *code {
-            traits::ObligationCauseCode::RFC1214(ref code) =>
-                infer::RFC1214Subregion(Rc::new(self.code_to_origin(span, sup_type, code))),
             traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) =>
                 infer::ReferenceOutlivesReferent(ref_type, span),
             _ =>
@@ -834,6 +852,17 @@ fn visit_expr(rcx: &mut Rcx, expr: &hir::Expr) {
             rcx.set_repeating_scope(repeating_scope);
         }
 
+        hir::ExprRet(Some(ref ret_expr)) => {
+            let call_site_scope = rcx.call_site_scope;
+            debug!("visit_expr ExprRet ret_expr.id {} call_site_scope: {:?}",
+                   ret_expr.id, call_site_scope);
+            type_of_node_must_outlive(rcx,
+                                      infer::CallReturn(ret_expr.span),
+                                      ret_expr.id,
+                                      ty::ReScope(call_site_scope.unwrap()));
+            intravisit::walk_expr(rcx, expr);
+        }
+
         _ => {
             intravisit::walk_expr(rcx, expr);
         }
@@ -1574,10 +1603,6 @@ fn components_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
                     origin.span(),
                     &format!("unresolved inference variable in outlives: {:?}", v));
             }
-            ty::outlives::Component::RFC1214(subcomponents) => {
-                let suborigin = infer::RFC1214Subregion(Rc::new(origin));
-                components_must_outlive(rcx, suborigin, subcomponents, region);
-            }
         }
     }
 }
diff --git a/src/librustc_typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs
deleted file mode 100644 (file)
index 3daf500..0000000
+++ /dev/null
@@ -1,672 +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 astconv::AstConv;
-use check::{FnCtxt, Inherited, blank_fn_ctxt, regionck, wfcheck};
-use constrained_type_params::{identify_constrained_type_params, Parameter};
-use CrateCtxt;
-use middle::region;
-use middle::subst::{self, TypeSpace, FnSpace, ParamSpace, SelfSpace};
-use middle::traits;
-use middle::ty::{self, Ty};
-use middle::ty::fold::{TypeFolder, TypeFoldable, super_fold_ty};
-
-use std::cell::RefCell;
-use std::collections::HashSet;
-use syntax::ast;
-use syntax::codemap::{DUMMY_SP, Span};
-use syntax::parse::token::special_idents;
-
-use rustc_front::intravisit::{self, Visitor, FnKind};
-use rustc_front::hir;
-
-pub struct CheckTypeWellFormedVisitor<'ccx, 'tcx:'ccx> {
-    ccx: &'ccx CrateCtxt<'ccx, 'tcx>,
-    cache: HashSet<Ty<'tcx>>
-}
-
-impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
-    pub fn new(ccx: &'ccx CrateCtxt<'ccx, 'tcx>) -> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
-        CheckTypeWellFormedVisitor { ccx: ccx, cache: HashSet::new() }
-    }
-
-    fn tcx(&self) -> &ty::ctxt<'tcx> {
-        self.ccx.tcx
-    }
-
-    /// Checks that the field types (in a struct def'n) or argument types (in an enum def'n) are
-    /// well-formed, meaning that they do not require any constraints not declared in the struct
-    /// definition itself. For example, this definition would be illegal:
-    ///
-    ///     struct Ref<'a, T> { x: &'a T }
-    ///
-    /// because the type did not declare that `T:'a`.
-    ///
-    /// We do this check as a pre-pass before checking fn bodies because if these constraints are
-    /// not included it frequently leads to confusing errors in fn bodies. So it's better to check
-    /// the types first.
-    fn check_item_well_formed(&mut self, item: &hir::Item) {
-        let ccx = self.ccx;
-        debug!("check_item_well_formed(it.id={}, it.name={})",
-               item.id,
-               ccx.tcx.item_path_str(ccx.tcx.map.local_def_id(item.id)));
-
-        match item.node {
-            /// Right now we check that every default trait implementation
-            /// has an implementation of itself. Basically, a case like:
-            ///
-            /// `impl Trait for T {}`
-            ///
-            /// has a requirement of `T: Trait` which was required for default
-            /// method implementations. Although this could be improved now that
-            /// there's a better infrastructure in place for this, it's being left
-            /// for a follow-up work.
-            ///
-            /// Since there's such a requirement, we need to check *just* positive
-            /// implementations, otherwise things like:
-            ///
-            /// impl !Send for T {}
-            ///
-            /// won't be allowed unless there's an *explicit* implementation of `Send`
-            /// for `T`
-            hir::ItemImpl(_, hir::ImplPolarity::Positive, _, _, _, _) => {
-                self.check_impl(item);
-            }
-            hir::ItemImpl(_, hir::ImplPolarity::Negative, _, Some(_), _, _) => {
-                let item_def_id = ccx.tcx.map.local_def_id(item.id);
-                let trait_ref = ccx.tcx.impl_trait_ref(item_def_id).unwrap();
-                ccx.tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id);
-                match ccx.tcx.lang_items.to_builtin_kind(trait_ref.def_id) {
-                    Some(ty::BoundSend) | Some(ty::BoundSync) => {}
-                    Some(_) | None => {
-                        if !ccx.tcx.trait_has_default_impl(trait_ref.def_id) {
-                            wfcheck::error_192(ccx, item.span);
-                        }
-                    }
-                }
-            }
-            hir::ItemFn(..) => {
-                self.check_item_type(item);
-            }
-            hir::ItemStatic(..) => {
-                self.check_item_type(item);
-            }
-            hir::ItemConst(..) => {
-                self.check_item_type(item);
-            }
-            hir::ItemStruct(ref struct_def, ref ast_generics) => {
-                self.check_type_defn(item, |fcx| {
-                    vec![struct_variant(fcx, struct_def)]
-                });
-
-                self.check_variances_for_type_defn(item, ast_generics);
-            }
-            hir::ItemEnum(ref enum_def, ref ast_generics) => {
-                self.check_type_defn(item, |fcx| {
-                    enum_variants(fcx, enum_def)
-                });
-
-                self.check_variances_for_type_defn(item, ast_generics);
-            }
-            hir::ItemTrait(_, _, _, ref items) => {
-                let trait_predicates =
-                    ccx.tcx.lookup_predicates(ccx.tcx.map.local_def_id(item.id));
-                reject_non_type_param_bounds(ccx.tcx, item.span, &trait_predicates);
-                if ccx.tcx.trait_has_default_impl(ccx.tcx.map.local_def_id(item.id)) {
-                    if !items.is_empty() {
-                        wfcheck::error_380(ccx, item.span);
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
-
-    fn with_fcx<F>(&mut self, item: &hir::Item, mut f: F) where
-        F: for<'fcx> FnMut(&mut CheckTypeWellFormedVisitor<'ccx, 'tcx>, &FnCtxt<'fcx, 'tcx>),
-    {
-        let ccx = self.ccx;
-        let item_def_id = ccx.tcx.map.local_def_id(item.id);
-        let type_scheme = ccx.tcx.lookup_item_type(item_def_id);
-        let type_predicates = ccx.tcx.lookup_predicates(item_def_id);
-        reject_non_type_param_bounds(ccx.tcx, item.span, &type_predicates);
-        let param_env = ccx.tcx.construct_parameter_environment(item.span,
-                                                                &type_scheme.generics,
-                                                                &type_predicates,
-                                                                item.id);
-        let tables = RefCell::new(ty::Tables::empty());
-        let inh = Inherited::new(ccx.tcx, &tables, param_env);
-        let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(type_scheme.ty), item.id);
-        f(self, &fcx);
-        fcx.select_all_obligations_or_error();
-        regionck::regionck_item(&fcx, item.id, item.span, &[]);
-    }
-
-    /// In a type definition, we check that to ensure that the types of the fields are well-formed.
-    fn check_type_defn<F>(&mut self, item: &hir::Item, mut lookup_fields: F) where
-        F: for<'fcx> FnMut(&FnCtxt<'fcx, 'tcx>) -> Vec<AdtVariant<'tcx>>,
-    {
-        self.with_fcx(item, |this, fcx| {
-            let variants = lookup_fields(fcx);
-            let mut bounds_checker = BoundsChecker::new(fcx,
-                                                        item.id,
-                                                        Some(&mut this.cache));
-            debug!("check_type_defn at bounds_checker.scope: {:?}", bounds_checker.scope);
-
-            for variant in &variants {
-                for field in &variant.fields {
-                    // Regions are checked below.
-                    bounds_checker.check_traits_in_ty(field.ty, field.span);
-                }
-
-                // For DST, all intermediate types must be sized.
-                if let Some((_, fields)) = variant.fields.split_last() {
-                    for field in fields {
-                        fcx.register_builtin_bound(
-                            field.ty,
-                            ty::BoundSized,
-                            traits::ObligationCause::new(field.span,
-                                                         fcx.body_id,
-                                                         traits::FieldSized));
-                    }
-                }
-            }
-
-            for field in variants.iter().flat_map(|v| v.fields.iter()) {
-                fcx.register_old_wf_obligation(field.ty, field.span, traits::MiscObligation);
-            }
-        });
-    }
-
-    fn check_item_type(&mut self,
-                       item: &hir::Item)
-    {
-        self.with_fcx(item, |this, fcx| {
-            let mut bounds_checker = BoundsChecker::new(fcx,
-                                                        item.id,
-                                                        Some(&mut this.cache));
-            debug!("check_item_type at bounds_checker.scope: {:?}", bounds_checker.scope);
-
-            let item_def_id = fcx.tcx().map.local_def_id(item.id);
-            let type_scheme = fcx.tcx().lookup_item_type(item_def_id);
-            let item_ty = fcx.instantiate_type_scheme(item.span,
-                                                      &fcx.inh
-                                                          .infcx
-                                                          .parameter_environment
-                                                          .free_substs,
-                                                      &type_scheme.ty);
-
-            bounds_checker.check_traits_in_ty(item_ty, item.span);
-        });
-    }
-
-    fn check_impl(&mut self,
-                  item: &hir::Item)
-    {
-        self.with_fcx(item, |this, fcx| {
-            let mut bounds_checker = BoundsChecker::new(fcx,
-                                                        item.id,
-                                                        Some(&mut this.cache));
-            debug!("check_impl at bounds_checker.scope: {:?}", bounds_checker.scope);
-
-            // Find the impl self type as seen from the "inside" --
-            // that is, with all type parameters converted from bound
-            // to free.
-            let self_ty = fcx.tcx().node_id_to_type(item.id);
-            let self_ty = fcx.instantiate_type_scheme(item.span,
-                                                      &fcx.inh
-                                                          .infcx
-                                                          .parameter_environment
-                                                          .free_substs,
-                                                      &self_ty);
-
-            bounds_checker.check_traits_in_ty(self_ty, item.span);
-
-            // Similarly, obtain an "inside" reference to the trait
-            // that the impl implements.
-            let trait_ref = match fcx.tcx().impl_trait_ref(fcx.tcx().map.local_def_id(item.id)) {
-                None => { return; }
-                Some(t) => { t }
-            };
-
-            let trait_ref = fcx.instantiate_type_scheme(item.span,
-                                                        &fcx.inh
-                                                            .infcx
-                                                            .parameter_environment
-                                                            .free_substs,
-                                                        &trait_ref);
-
-            // We are stricter on the trait-ref in an impl than the
-            // self-type.  In particular, we enforce region
-            // relationships. The reason for this is that (at least
-            // presently) "applying" an impl does not require that the
-            // application site check the well-formedness constraints on the
-            // trait reference. Instead, this is done at the impl site.
-            // Arguably this is wrong and we should treat the trait-reference
-            // the same way as we treat the self-type.
-            bounds_checker.check_trait_ref(&trait_ref, item.span);
-
-            let cause =
-                traits::ObligationCause::new(
-                    item.span,
-                    fcx.body_id,
-                    traits::ItemObligation(trait_ref.def_id));
-
-            // Find the supertrait bounds. This will add `int:Bar`.
-            let poly_trait_ref = ty::Binder(trait_ref);
-            let predicates = fcx.tcx().lookup_super_predicates(poly_trait_ref.def_id());
-            let predicates = predicates.instantiate_supertrait(fcx.tcx(), &poly_trait_ref);
-            let predicates = {
-                let selcx = &mut traits::SelectionContext::new(fcx.infcx());
-                traits::normalize(selcx, cause.clone(), &predicates)
-            };
-            for predicate in predicates.value.predicates {
-                fcx.register_predicate(traits::Obligation::new(cause.clone(), predicate));
-            }
-            for obligation in predicates.obligations {
-                fcx.register_predicate(obligation);
-            }
-        });
-    }
-
-    fn check_variances_for_type_defn(&self,
-                                     item: &hir::Item,
-                                     ast_generics: &hir::Generics)
-    {
-        let item_def_id = self.tcx().map.local_def_id(item.id);
-        let ty_predicates = self.tcx().lookup_predicates(item_def_id);
-        let variances = self.tcx().item_variances(item_def_id);
-
-        let mut constrained_parameters: HashSet<_> =
-            variances.types
-                     .iter_enumerated()
-                     .filter(|&(_, _, &variance)| variance != ty::Bivariant)
-                     .map(|(space, index, _)| self.param_ty(ast_generics, space, index))
-                     .map(|p| Parameter::Type(p))
-                     .collect();
-
-        identify_constrained_type_params(self.tcx(),
-                                         ty_predicates.predicates.as_slice(),
-                                         None,
-                                         &mut constrained_parameters);
-
-        for (space, index, _) in variances.types.iter_enumerated() {
-            let param_ty = self.param_ty(ast_generics, space, index);
-            if constrained_parameters.contains(&Parameter::Type(param_ty)) {
-                continue;
-            }
-            let span = self.ty_param_span(ast_generics, item, space, index);
-            self.report_bivariance(span, param_ty.name);
-        }
-
-        for (space, index, &variance) in variances.regions.iter_enumerated() {
-            if variance != ty::Bivariant {
-                continue;
-            }
-
-            assert_eq!(space, TypeSpace);
-            let span = ast_generics.lifetimes[index].lifetime.span;
-            let name = ast_generics.lifetimes[index].lifetime.name;
-            self.report_bivariance(span, name);
-        }
-    }
-
-    fn param_ty(&self,
-                ast_generics: &hir::Generics,
-                space: ParamSpace,
-                index: usize)
-                -> ty::ParamTy
-    {
-        let name = match space {
-            TypeSpace => ast_generics.ty_params[index].name,
-            SelfSpace => special_idents::type_self.name,
-            FnSpace => self.tcx().sess.bug("Fn space occupied?"),
-        };
-
-        ty::ParamTy { space: space, idx: index as u32, name: name }
-    }
-
-    fn ty_param_span(&self,
-                     ast_generics: &hir::Generics,
-                     item: &hir::Item,
-                     space: ParamSpace,
-                     index: usize)
-                     -> Span
-    {
-        match space {
-            TypeSpace => ast_generics.ty_params[index].span,
-            SelfSpace => item.span,
-            FnSpace => self.tcx().sess.span_bug(item.span, "Fn space occupied?"),
-        }
-    }
-
-    fn report_bivariance(&self,
-                         span: Span,
-                         param_name: ast::Name)
-    {
-        wfcheck::error_392(self.tcx(), span, param_name);
-
-        let suggested_marker_id = self.tcx().lang_items.phantom_data();
-        match suggested_marker_id {
-            Some(def_id) => {
-                self.tcx().sess.fileline_help(
-                    span,
-                    &format!("consider removing `{}` or using a marker such as `{}`",
-                             param_name,
-                             self.tcx().item_path_str(def_id)));
-            }
-            None => {
-                // no lang items, no help!
-            }
-        }
-    }
-}
-
-// Reject any predicates that do not involve a type parameter.
-fn reject_non_type_param_bounds<'tcx>(tcx: &ty::ctxt<'tcx>,
-                                      span: Span,
-                                      predicates: &ty::GenericPredicates<'tcx>) {
-    for predicate in &predicates.predicates {
-        match predicate {
-            &ty::Predicate::Trait(ty::Binder(ref tr)) => {
-                let found_param = tr.input_types().iter()
-                                    .flat_map(|ty| ty.walk())
-                                    .any(is_ty_param);
-                if !found_param { report_bound_error(tcx, span, tr.self_ty() )}
-            }
-            &ty::Predicate::TypeOutlives(ty::Binder(ty::OutlivesPredicate(ty, _))) => {
-                let found_param = ty.walk().any(|t| is_ty_param(t));
-                if !found_param { report_bound_error(tcx, span, ty) }
-            }
-            _ => {}
-        };
-    }
-
-    fn report_bound_error<'t>(tcx: &ty::ctxt<'t>,
-                          span: Span,
-                          bounded_ty: ty::Ty<'t>) {
-        span_err!(tcx.sess, span, E0193,
-            "cannot bound type `{}`, where clause \
-                bounds may only be attached to types involving \
-                type parameters",
-                bounded_ty)
-    }
-
-    fn is_ty_param(ty: ty::Ty) -> bool {
-        match &ty.sty {
-            &ty::TyParam(_) => true,
-            _ => false
-        }
-    }
-}
-
-fn reject_shadowing_type_parameters<'tcx>(tcx: &ty::ctxt<'tcx>,
-                                          span: Span,
-                                          generics: &ty::Generics<'tcx>) {
-    let impl_params = generics.types.get_slice(subst::TypeSpace).iter()
-        .map(|tp| tp.name).collect::<HashSet<_>>();
-
-    for method_param in generics.types.get_slice(subst::FnSpace) {
-        if impl_params.contains(&method_param.name) {
-            wfcheck::error_194(tcx, span, method_param.name);
-        }
-    }
-}
-
-impl<'ccx, 'tcx, 'v> Visitor<'v> for CheckTypeWellFormedVisitor<'ccx, 'tcx> {
-    fn visit_item(&mut self, i: &hir::Item) {
-        self.check_item_well_formed(i);
-        intravisit::walk_item(self, i);
-    }
-
-    fn visit_fn(&mut self,
-                fk: FnKind<'v>, fd: &'v hir::FnDecl,
-                b: &'v hir::Block, span: Span, id: ast::NodeId) {
-        match fk {
-            FnKind::Closure | FnKind::ItemFn(..) => {}
-            FnKind::Method(..) => {
-                match self.tcx().impl_or_trait_item(self.tcx().map.local_def_id(id)) {
-                    ty::ImplOrTraitItem::MethodTraitItem(ty_method) => {
-                        reject_shadowing_type_parameters(self.tcx(), span, &ty_method.generics)
-                    }
-                    _ => {}
-                }
-            }
-        }
-        intravisit::walk_fn(self, fk, fd, b, span)
-    }
-
-    fn visit_trait_item(&mut self, trait_item: &'v hir::TraitItem) {
-        if let hir::MethodTraitItem(_, None) = trait_item.node {
-            match self.tcx().impl_or_trait_item(self.tcx().map.local_def_id(trait_item.id)) {
-                ty::ImplOrTraitItem::MethodTraitItem(ty_method) => {
-                    reject_non_type_param_bounds(
-                        self.tcx(),
-                        trait_item.span,
-                        &ty_method.predicates);
-                    reject_shadowing_type_parameters(
-                        self.tcx(),
-                        trait_item.span,
-                        &ty_method.generics);
-                }
-                _ => {}
-            }
-        }
-
-        intravisit::walk_trait_item(self, trait_item)
-    }
-}
-
-pub struct BoundsChecker<'cx,'tcx:'cx> {
-    fcx: &'cx FnCtxt<'cx,'tcx>,
-    span: Span,
-
-    scope: region::CodeExtent,
-
-    binding_count: usize,
-    cache: Option<&'cx mut HashSet<Ty<'tcx>>>,
-}
-
-impl<'cx,'tcx> BoundsChecker<'cx,'tcx> {
-    pub fn new(fcx: &'cx FnCtxt<'cx,'tcx>,
-               scope: ast::NodeId,
-               cache: Option<&'cx mut HashSet<Ty<'tcx>>>)
-               -> BoundsChecker<'cx,'tcx> {
-        let scope = fcx.tcx().region_maps.item_extent(scope);
-        BoundsChecker { fcx: fcx, span: DUMMY_SP, scope: scope,
-                        cache: cache, binding_count: 0 }
-    }
-
-    /// Given a trait ref like `A : Trait<B>`, where `Trait` is defined as (say):
-    ///
-    ///     trait Trait<B:OtherTrait> : Copy { ... }
-    ///
-    /// This routine will check that `B : OtherTrait` and `A : Trait<B>`. It will also recursively
-    /// check that the types `A` and `B` are well-formed.
-    ///
-    /// Note that it does not (currently, at least) check that `A : Copy` (that check is delegated
-    /// to the point where impl `A : Trait<B>` is implemented).
-    pub fn check_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, span: Span) {
-        let trait_predicates = self.fcx.tcx().lookup_predicates(trait_ref.def_id);
-
-        let bounds = self.fcx.instantiate_bounds(span,
-                                                 trait_ref.substs,
-                                                 &trait_predicates);
-
-        self.fcx.add_obligations_for_parameters(
-            traits::ObligationCause::new(
-                span,
-                self.fcx.body_id,
-                traits::ItemObligation(trait_ref.def_id)),
-            &bounds);
-
-        for &ty in &trait_ref.substs.types {
-            self.check_traits_in_ty(ty, span);
-        }
-    }
-
-    fn check_traits_in_ty(&mut self, ty: Ty<'tcx>, span: Span) {
-        self.span = span;
-        // When checking types outside of a type def'n, we ignore
-        // region obligations. See discussion below in fold_ty().
-        self.binding_count += 1;
-        ty.fold_with(self);
-        self.binding_count -= 1;
-    }
-}
-
-impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
-    fn tcx(&self) -> &ty::ctxt<'tcx> {
-        self.fcx.tcx()
-    }
-
-    fn fold_binder<T>(&mut self, binder: &ty::Binder<T>) -> ty::Binder<T>
-        where T : TypeFoldable<'tcx>
-    {
-        self.binding_count += 1;
-        let value = self.fcx.tcx().liberate_late_bound_regions(
-            self.scope,
-            binder);
-        debug!("BoundsChecker::fold_binder: late-bound regions replaced: {:?} at scope: {:?}",
-               value, self.scope);
-        let value = value.fold_with(self);
-        self.binding_count -= 1;
-        ty::Binder(value)
-    }
-
-    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        debug!("BoundsChecker t={:?}",
-               t);
-
-        match self.cache {
-            Some(ref mut cache) => {
-                if !cache.insert(t) {
-                    // Already checked this type! Don't check again.
-                    debug!("cached");
-                    return t;
-                }
-            }
-            None => { }
-        }
-
-        match t.sty{
-            ty::TyStruct(def, substs) |
-            ty::TyEnum(def, substs) => {
-                let type_predicates = def.predicates(self.fcx.tcx());
-                let bounds = self.fcx.instantiate_bounds(self.span, substs,
-                                                         &type_predicates);
-
-                if self.binding_count == 0 {
-                    self.fcx.add_obligations_for_parameters(
-                        traits::ObligationCause::new(self.span,
-                                                     self.fcx.body_id,
-                                                     traits::ItemObligation(def.did)),
-                        &bounds);
-                } else {
-                    // There are two circumstances in which we ignore
-                    // region obligations.
-                    //
-                    // The first is when we are inside of a closure
-                    // type. This is because in that case the region
-                    // obligations for the parameter types are things
-                    // that the closure body gets to assume and the
-                    // caller must prove at the time of call. In other
-                    // words, if there is a type like `<'a, 'b> | &'a
-                    // &'b int |`, it is well-formed, and caller will
-                    // have to show that `'b : 'a` at the time of
-                    // call.
-                    //
-                    // The second is when we are checking for
-                    // well-formedness outside of a type def'n or fn
-                    // body. This is for a similar reason: in general,
-                    // we only do WF checking for regions in the
-                    // result of expressions and type definitions, so
-                    // to as allow for implicit where clauses.
-                    //
-                    // (I believe we should do the same for traits, but
-                    // that will require an RFC. -nmatsakis)
-                    let bounds = filter_to_trait_obligations(bounds);
-                    self.fcx.add_obligations_for_parameters(
-                        traits::ObligationCause::new(self.span,
-                                                     self.fcx.body_id,
-                                                     traits::ItemObligation(def.did)),
-                        &bounds);
-                }
-
-                self.fold_substs(substs);
-            }
-            _ => {
-                super_fold_ty(self, t);
-            }
-        }
-
-        t // we're not folding to produce a new type, so just return `t` here
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// ADT
-
-struct AdtVariant<'tcx> {
-    fields: Vec<AdtField<'tcx>>,
-}
-
-struct AdtField<'tcx> {
-    ty: Ty<'tcx>,
-    span: Span,
-}
-
-fn struct_variant<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                            struct_def: &hir::VariantData)
-                            -> AdtVariant<'tcx> {
-    let fields =
-        struct_def.fields().iter()
-        .map(|field| {
-            let field_ty = fcx.tcx().node_id_to_type(field.node.id);
-            let field_ty = fcx.instantiate_type_scheme(field.span,
-                                                       &fcx.inh
-                                                           .infcx
-                                                           .parameter_environment
-                                                           .free_substs,
-                                                       &field_ty);
-            AdtField { ty: field_ty, span: field.span }
-        })
-        .collect();
-    AdtVariant { fields: fields }
-}
-
-fn enum_variants<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                           enum_def: &hir::EnumDef)
-                           -> Vec<AdtVariant<'tcx>> {
-    enum_def.variants.iter()
-        .map(|variant| struct_variant(fcx, &variant.node.data))
-        .collect()
-}
-
-fn filter_to_trait_obligations<'tcx>(bounds: ty::InstantiatedPredicates<'tcx>)
-                                     -> ty::InstantiatedPredicates<'tcx>
-{
-    let mut result = ty::InstantiatedPredicates::empty();
-    for (space, _, predicate) in bounds.predicates.iter_enumerated() {
-        match *predicate {
-            ty::Predicate::Trait(..) |
-            ty::Predicate::Projection(..) => {
-                result.predicates.push(space, predicate.clone())
-            }
-            ty::Predicate::WellFormed(..) |
-            ty::Predicate::ObjectSafe(..) |
-            ty::Predicate::Equate(..) |
-            ty::Predicate::TypeOutlives(..) |
-            ty::Predicate::RegionOutlives(..) => {
-            }
-        }
-    }
-    result
-}
index a9bd0e3926811a4dace94bdcc60caac3d28514a5..4f3f716c20d61c76a67572db3972e51d6889d372 100644 (file)
@@ -13,6 +13,7 @@ use check::{FnCtxt, Inherited, blank_fn_ctxt, regionck};
 use constrained_type_params::{identify_constrained_type_params, Parameter};
 use CrateCtxt;
 use middle::def_id::DefId;
+use middle::region::{CodeExtent};
 use middle::subst::{self, TypeSpace, FnSpace, ParamSpace, SelfSpace};
 use middle::traits;
 use middle::ty::{self, Ty};
@@ -20,9 +21,9 @@ use middle::ty::fold::{TypeFolder};
 
 use std::cell::RefCell;
 use std::collections::HashSet;
-use std::rc::Rc;
 use syntax::ast;
 use syntax::codemap::{Span};
+use syntax::errors::DiagnosticBuilder;
 use syntax::parse::token::{special_idents};
 use rustc_front::intravisit::{self, Visitor};
 use rustc_front::hir;
@@ -37,8 +38,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
                -> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
         CheckTypeWellFormedVisitor {
             ccx: ccx,
-            code: traits::ObligationCauseCode::RFC1214(
-                Rc::new(traits::ObligationCauseCode::MiscObligation))
+            code: traits::ObligationCauseCode::MiscObligation
         }
     }
 
@@ -133,13 +133,14 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
         let code = self.code.clone();
         self.with_fcx(item_id, span, |fcx, this| {
             let free_substs = &fcx.inh.infcx.parameter_environment.free_substs;
-            let free_id = fcx.inh.infcx.parameter_environment.free_id;
+            let free_id_outlive = fcx.inh.infcx.parameter_environment.free_id_outlive;
 
             let item = fcx.tcx().impl_or_trait_item(fcx.tcx().map.local_def_id(item_id));
 
-            let mut implied_bounds = match item.container() {
-                ty::TraitContainer(_) => vec![],
-                ty::ImplContainer(def_id) => impl_implied_bounds(fcx, def_id, span)
+            let (mut implied_bounds, self_ty) = match item.container() {
+                ty::TraitContainer(_) => (vec![], fcx.tcx().mk_self_type()),
+                ty::ImplContainer(def_id) => (impl_implied_bounds(fcx, def_id, span),
+                                              fcx.tcx().lookup_item_type(def_id).ty)
             };
 
             match item {
@@ -152,7 +153,9 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
                     let method_ty = fcx.instantiate_type_scheme(span, free_substs, &method.fty);
                     let predicates = fcx.instantiate_bounds(span, free_substs, &method.predicates);
                     this.check_fn_or_method(fcx, span, &method_ty, &predicates,
-                                            free_id, &mut implied_bounds);
+                                            free_id_outlive, &mut implied_bounds);
+                    this.check_method_receiver(fcx, span, &method,
+                                               free_id_outlive, self_ty);
                 }
                 ty::TypeTraitItem(assoc_type) => {
                     if let Some(ref ty) = assoc_type.ty {
@@ -262,8 +265,9 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
             let predicates = fcx.instantiate_bounds(item.span, free_substs, &predicates);
 
             let mut implied_bounds = vec![];
+            let free_id_outlive = fcx.tcx().region_maps.call_site_extent(item.id, body.id);
             this.check_fn_or_method(fcx, item.span, bare_fn_ty, &predicates,
-                                    body.id, &mut implied_bounds);
+                                    free_id_outlive, &mut implied_bounds);
             implied_bounds
         })
     }
@@ -309,8 +313,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
                         ty::wf::trait_obligations(fcx.infcx(),
                                                   fcx.body_id,
                                                   &trait_ref,
-                                                  ast_trait_ref.path.span,
-                                                  true);
+                                                  ast_trait_ref.path.span);
                     for obligation in obligations {
                         fcx.register_predicate(obligation);
                     }
@@ -341,8 +344,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
                       .flat_map(|p| ty::wf::predicate_obligations(fcx.infcx(),
                                                                   fcx.body_id,
                                                                   p,
-                                                                  span,
-                                                                  true));
+                                                                  span));
 
         for obligation in obligations {
             fcx.register_predicate(obligation);
@@ -354,12 +356,11 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
                                 span: Span,
                                 fty: &ty::BareFnTy<'tcx>,
                                 predicates: &ty::InstantiatedPredicates<'tcx>,
-                                free_id: ast::NodeId,
+                                free_id_outlive: CodeExtent,
                                 implied_bounds: &mut Vec<Ty<'tcx>>)
     {
         let free_substs = &fcx.inh.infcx.parameter_environment.free_substs;
         let fty = fcx.instantiate_type_scheme(span, free_substs, fty);
-        let free_id_outlive = fcx.tcx().region_maps.item_extent(free_id);
         let sig = fcx.tcx().liberate_late_bound_regions(free_id_outlive, &fty.sig);
 
         for &input_ty in &sig.inputs {
@@ -380,6 +381,47 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
         self.check_where_clauses(fcx, span, predicates);
     }
 
+    fn check_method_receiver<'fcx>(&mut self,
+                                   fcx: &FnCtxt<'fcx,'tcx>,
+                                   span: Span,
+                                   method: &ty::Method<'tcx>,
+                                   free_id_outlive: CodeExtent,
+                                   self_ty: ty::Ty<'tcx>)
+    {
+        // check that the type of the method's receiver matches the
+        // method's first parameter.
+
+        let free_substs = &fcx.inh.infcx.parameter_environment.free_substs;
+        let fty = fcx.instantiate_type_scheme(span, free_substs, &method.fty);
+        let sig = fcx.tcx().liberate_late_bound_regions(free_id_outlive, &fty.sig);
+
+        debug!("check_method_receiver({:?},cat={:?},self_ty={:?},sig={:?})",
+               method.name, method.explicit_self, self_ty, sig);
+
+        let rcvr_ty = match method.explicit_self {
+            ty::ExplicitSelfCategory::Static => return,
+            ty::ExplicitSelfCategory::ByValue => self_ty,
+            ty::ExplicitSelfCategory::ByReference(region, mutability) => {
+                fcx.tcx().mk_ref(fcx.tcx().mk_region(region), ty::TypeAndMut {
+                    ty: self_ty,
+                    mutbl: mutability
+                })
+            }
+            ty::ExplicitSelfCategory::ByBox => fcx.tcx().mk_box(self_ty)
+        };
+        let rcvr_ty = fcx.instantiate_type_scheme(span, free_substs, &rcvr_ty);
+        let rcvr_ty = fcx.tcx().liberate_late_bound_regions(free_id_outlive,
+                                                            &ty::Binder(rcvr_ty));
+
+        debug!("check_method_receiver: receiver ty = {:?}", rcvr_ty);
+
+        let _ = ::require_same_types(
+            fcx.tcx(), Some(fcx.infcx()), false, span,
+            sig.inputs[0], rcvr_ty,
+            || "mismatched method receiver".to_owned()
+        );
+    }
+
     fn check_variances_for_type_defn(&self,
                                      item: &hir::Item,
                                      ast_generics: &hir::Generics)
@@ -455,12 +497,12 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
                          span: Span,
                          param_name: ast::Name)
     {
-        error_392(self.tcx(), span, param_name);
+        let mut err = error_392(self.tcx(), span, param_name);
 
         let suggested_marker_id = self.tcx().lang_items.phantom_data();
         match suggested_marker_id {
             Some(def_id) => {
-                self.tcx().sess.fileline_help(
+                err.fileline_help(
                     span,
                     &format!("consider removing `{}` or using a marker such as `{}`",
                              param_name,
@@ -470,6 +512,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
                 // no lang items, no help!
             }
         }
+        err.emit();
     }
 }
 
@@ -580,9 +623,10 @@ pub fn error_380<'ccx,'tcx>(ccx: &'ccx CrateCtxt<'ccx, 'tcx>, span: Span) {
                Trait for ..`) must have no methods or associated items")
 }
 
-pub fn error_392<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, param_name: ast::Name)  {
-    span_err!(tcx.sess, span, E0392,
-              "parameter `{}` is never used", param_name);
+pub fn error_392<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, param_name: ast::Name)
+                       -> DiagnosticBuilder<'tcx> {
+    struct_span_err!(tcx.sess, span, E0392,
+                     "parameter `{}` is never used", param_name)
 }
 
 pub fn error_194<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, name: ast::Name) {
index 984f227cebe79bc61adc4612bf7f8080f8877be9..c2abb074efa136690a25fca2cf752e37f0dbe5ca 100644 (file)
@@ -17,7 +17,7 @@ use astconv::AstConv;
 use check::FnCtxt;
 use middle::def_id::DefId;
 use middle::pat_util;
-use middle::ty::{self, Ty, MethodCall, MethodCallee, HasTypeFlags};
+use middle::ty::{self, Ty, MethodCall, MethodCallee};
 use middle::ty::adjustment;
 use middle::ty::fold::{TypeFolder,TypeFoldable};
 use middle::infer;
@@ -122,18 +122,20 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                 } else {
                     let tcx = self.tcx();
 
-                    if let hir::ExprAssignOp(..) = e.node {
+                    if let hir::ExprAssignOp(_, ref lhs, ref rhs) = e.node {
                         if
                             !tcx.sess.features.borrow().augmented_assignments &&
-                            !self.fcx.expr_ty(e).references_error()
+                            !self.fcx.expr_ty(e).references_error() &&
+                            !self.fcx.expr_ty(lhs).references_error() &&
+                            !self.fcx.expr_ty(rhs).references_error()
                         {
-                            tcx.sess.span_err(
-                                e.span,
-                                "overloaded augmented assignments are not stable");
-                            fileline_help!(
-                                tcx.sess, e.span,
-                                "add #![feature(augmented_assignments)] to the crate root \
-                                 to enable");
+                            tcx.sess.struct_span_err(e.span,
+                                                     "overloaded augmented assignments \
+                                                      are not stable")
+                                .fileline_help(e.span,
+                                               "add #![feature(augmented_assignments)] to the \
+                                                crate root to enable")
+                                .emit()
                         }
                     }
                 }
index cdbfda40813b4648a4b356de34358ee80b5acdc1..7465ff526b6de1398b569cbd55f74ef67e78e663 100644 (file)
@@ -20,8 +20,7 @@ use middle::def_id::DefId;
 use middle::lang_items::UnsizeTraitLangItem;
 use middle::subst::{self, Subst};
 use middle::traits;
-use middle::ty;
-use middle::ty::RegionEscape;
+use middle::ty::{self, TypeFoldable};
 use middle::ty::{ImplOrTraitItemId, ConstTraitItemId};
 use middle::ty::{MethodTraitItemId, TypeTraitItemId, ParameterEnvironment};
 use middle::ty::{Ty, TyBool, TyChar, TyEnum, TyError};
@@ -39,10 +38,10 @@ use std::rc::Rc;
 use syntax::codemap::Span;
 use syntax::parse::token;
 use util::nodemap::{DefIdMap, FnvHashMap};
+use rustc::dep_graph::DepNode;
 use rustc::front::map as hir_map;
-use rustc::front::map::NodeItem;
 use rustc_front::intravisit;
-use rustc_front::hir::{Item, ItemImpl,Crate};
+use rustc_front::hir::{Item, ItemImpl};
 use rustc_front::hir;
 
 mod orphan;
@@ -105,11 +104,13 @@ impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for CoherenceCheckVisitor<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
-    fn check(&self, krate: &Crate) {
+    fn check(&self) {
         // Check implementations and traits. This populates the tables
         // containing the inherent methods and extension methods. It also
         // builds up the trait inheritance table.
-        krate.visit_all_items(&mut CoherenceCheckVisitor { cc: self });
+        self.crate_context.tcx.visit_all_items_in_krate(
+            DepNode::CoherenceCheckImpl,
+            &mut CoherenceCheckVisitor { cc: self });
 
         // Copy over the inherent impls we gathered up during the walk into
         // the tcx.
@@ -148,11 +149,23 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
                    trait_ref,
                    item.name);
 
+            // Skip impls where one of the self type is an error type.
+            // This occurs with e.g. resolve failures (#30589).
+            if trait_ref.references_error() {
+                return;
+            }
+
             enforce_trait_manually_implementable(self.crate_context.tcx,
                                                  item.span,
                                                  trait_ref.def_id);
             self.add_trait_impl(trait_ref, impl_did);
         } else {
+            // Skip inherent impls where the self type is an error
+            // type. This occurs with e.g. resolve failures (#30589).
+            if self_type.ty.references_error() {
+                return;
+            }
+
             // Add the implementation to the mapping from implementation to base
             // type def ID, if there is a base type for this implementation and
             // the implementation does not have any associated traits.
@@ -503,17 +516,24 @@ fn enforce_trait_manually_implementable(tcx: &ty::ctxt, sp: Span, trait_def_id:
     } else {
         return // everything OK
     };
-    span_err!(tcx.sess, sp, E0183, "manual implementations of `{}` are experimental", trait_name);
-    fileline_help!(tcx.sess, sp,
-               "add `#![feature(unboxed_closures)]` to the crate attributes to enable");
+    let mut err = struct_span_err!(tcx.sess,
+                                   sp,
+                                   E0183,
+                                   "manual implementations of `{}` are experimental",
+                                   trait_name);
+    fileline_help!(&mut err, sp,
+                   "add `#![feature(unboxed_closures)]` to the crate attributes to enable");
+    err.emit();
 }
 
 pub fn check_coherence(crate_context: &CrateCtxt) {
+    let _task = crate_context.tcx.dep_graph.in_task(DepNode::Coherence);
+    let infcx = new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None, true);
     CoherenceChecker {
         crate_context: crate_context,
-        inference_context: new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None, true),
+        inference_context: infcx,
         inherent_impls: RefCell::new(FnvHashMap()),
-    }.check(crate_context.tcx.map.krate());
+    }.check();
     unsafety::check(crate_context.tcx);
     orphan::check(crate_context.tcx);
     overlap::check(crate_context.tcx);
index e6e31ba0819c5d605f1afeab1e4424ef1aeb2272..69eb7f51f37852228d2a41f30eaba1e2b6e50507 100644 (file)
@@ -17,13 +17,13 @@ use middle::traits;
 use middle::ty;
 use syntax::ast;
 use syntax::codemap::Span;
+use rustc::dep_graph::DepNode;
 use rustc_front::intravisit;
 use rustc_front::hir;
-use rustc_front::hir::{Item, ItemImpl};
 
 pub fn check(tcx: &ty::ctxt) {
     let mut orphan = OrphanChecker { tcx: tcx };
-    tcx.map.krate().visit_all_items(&mut orphan);
+    tcx.visit_all_items_in_krate(DepNode::CoherenceOrphanCheck, &mut orphan);
 }
 
 struct OrphanChecker<'cx, 'tcx:'cx> {
@@ -49,11 +49,11 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> {
         match lang_def_id {
             Some(lang_def_id) if lang_def_id == impl_def_id => { /* OK */ },
             _ => {
-                span_err!(self.tcx.sess, span, E0390,
+                struct_span_err!(self.tcx.sess, span, E0390,
                           "only a single inherent implementation marked with `#[lang = \"{}\"]` \
-                           is allowed for the `{}` primitive", lang, ty);
-                span_help!(self.tcx.sess, span,
-                           "consider using a trait to implement these methods");
+                           is allowed for the `{}` primitive", lang, ty)
+                    .span_help(span, "consider using a trait to implement these methods")
+                    .emit();
             }
         }
     }
@@ -205,6 +205,9 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> {
                                                   "f64",
                                                   item.span);
                     }
+                    ty::TyError => {
+                        return;
+                    }
                     _ => {
                         span_err!(self.tcx.sess, item.span, E0118,
                                   "no base type found for inherent implementation; \
@@ -232,10 +235,10 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> {
                     }
                     Err(traits::OrphanCheckErr::UncoveredTy(param_ty)) => {
                         span_err!(self.tcx.sess, item.span, E0210,
-                                "type parameter `{}` must be used as the type parameter for \
-                                 some local type (e.g. `MyStruct<T>`); only traits defined in \
-                                 the current crate can be implemented for a type parameter",
-                                param_ty);
+                                  "type parameter `{}` must be used as the type parameter for \
+                                   some local type (e.g. `MyStruct<T>`); only traits defined in \
+                                   the current crate can be implemented for a type parameter",
+                                  param_ty);
                         return;
                     }
                 }
index 693c8716ab58acec4bdf1da31fe32cbb9094707b..71c6fc1fd08ec7ad516dfc946d423f2595dc7172 100644 (file)
@@ -15,72 +15,69 @@ use middle::cstore::{CrateStore, LOCAL_CRATE};
 use middle::def_id::DefId;
 use middle::traits;
 use middle::ty;
-use middle::infer::{self, new_infer_ctxt};
+use middle::infer;
 use syntax::ast;
 use syntax::codemap::Span;
+use rustc::dep_graph::DepNode;
 use rustc_front::hir;
 use rustc_front::intravisit;
-use util::nodemap::DefIdMap;
+use util::nodemap::{DefIdMap, DefIdSet};
 
 pub fn check(tcx: &ty::ctxt) {
-    let mut overlap = OverlapChecker { tcx: tcx, default_impls: DefIdMap() };
-    overlap.check_for_overlapping_impls();
+    let mut overlap = OverlapChecker { tcx: tcx,
+                                       traits_checked: DefIdSet(),
+                                       default_impls: DefIdMap() };
 
     // this secondary walk specifically checks for some other cases,
     // like defaulted traits, for which additional overlap rules exist
-    tcx.map.krate().visit_all_items(&mut overlap);
+    tcx.visit_all_items_in_krate(DepNode::CoherenceOverlapCheckSpecial, &mut overlap);
 }
 
 struct OverlapChecker<'cx, 'tcx:'cx> {
     tcx: &'cx ty::ctxt<'tcx>,
 
+    // The set of traits where we have checked for overlap.  This is
+    // used to avoid checking the same trait twice.
+    //
+    // NB. It's ok to skip tracking this set because we fully
+    // encapsulate it, and we always create a task
+    // (`CoherenceOverlapCheck`) corresponding to each entry.
+    traits_checked: DefIdSet,
+
     // maps from a trait def-id to an impl id
     default_impls: DefIdMap<ast::NodeId>,
 }
 
 impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
-    fn check_for_overlapping_impls(&self) {
-        debug!("check_for_overlapping_impls");
-
-        // Collect this into a vector to avoid holding the
-        // refcell-lock during the
-        // check_for_overlapping_impls_of_trait() check, since that
-        // check can populate this table further with impls from other
-        // crates.
-        let trait_defs: Vec<_> = self.tcx.trait_defs.borrow().values().cloned().collect();
-
-        for trait_def in trait_defs {
-            self.tcx.populate_implementations_for_trait_if_necessary(trait_def.trait_ref.def_id);
-            self.check_for_overlapping_impls_of_trait(trait_def);
+    fn check_for_overlapping_impls_of_trait(&mut self, trait_def_id: DefId) {
+        debug!("check_for_overlapping_impls_of_trait(trait_def_id={:?})",
+               trait_def_id);
+
+        let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapCheck(trait_def_id));
+        if !self.traits_checked.insert(trait_def_id) {
+            return;
         }
-    }
 
-    fn check_for_overlapping_impls_of_trait(&self,
-                                            trait_def: &'tcx ty::TraitDef<'tcx>)
-    {
-        debug!("check_for_overlapping_impls_of_trait(trait_def={:?})",
-               trait_def);
+        let trait_def = self.tcx.lookup_trait_def(trait_def_id);
+        self.tcx.populate_implementations_for_trait_if_necessary(
+            trait_def.trait_ref.def_id);
 
         // We should already know all impls of this trait, so these
         // borrows are safe.
-        let blanket_impls = trait_def.blanket_impls.borrow();
-        let nonblanket_impls = trait_def.nonblanket_impls.borrow();
-        let trait_def_id = trait_def.trait_ref.def_id;
+        let (blanket_impls, nonblanket_impls) = trait_def.borrow_impl_lists(self.tcx);
 
         // Conflicts can only occur between a blanket impl and another impl,
         // or between 2 non-blanket impls of the same kind.
 
         for (i, &impl1_def_id) in blanket_impls.iter().enumerate() {
             for &impl2_def_id in &blanket_impls[(i+1)..] {
-                self.check_if_impls_overlap(trait_def_id,
-                                            impl1_def_id,
+                self.check_if_impls_overlap(impl1_def_id,
                                             impl2_def_id);
             }
 
             for v in nonblanket_impls.values() {
                 for &impl2_def_id in v {
-                    self.check_if_impls_overlap(trait_def_id,
-                                                impl1_def_id,
+                    self.check_if_impls_overlap(impl1_def_id,
                                                 impl2_def_id);
                 }
             }
@@ -89,8 +86,7 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
         for impl_group in nonblanket_impls.values() {
             for (i, &impl1_def_id) in impl_group.iter().enumerate() {
                 for &impl2_def_id in &impl_group[(i+1)..] {
-                    self.check_if_impls_overlap(trait_def_id,
-                                                impl1_def_id,
+                    self.check_if_impls_overlap(impl1_def_id,
                                                 impl2_def_id);
                 }
             }
@@ -121,44 +117,52 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
 
 
     fn check_if_impls_overlap(&self,
-                              trait_def_id: DefId,
                               impl1_def_id: DefId,
                               impl2_def_id: DefId)
     {
         if let Some((impl1_def_id, impl2_def_id)) = self.order_impls(
             impl1_def_id, impl2_def_id)
         {
-            debug!("check_if_impls_overlap({:?}, {:?}, {:?})",
-                   trait_def_id,
+            debug!("check_if_impls_overlap({:?}, {:?})",
                    impl1_def_id,
                    impl2_def_id);
 
             let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None, false);
-            if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) {
-                self.report_overlap_error(trait_def_id, impl1_def_id, impl2_def_id);
+            if let Some(trait_ref) = traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) {
+                self.report_overlap_error(impl1_def_id, impl2_def_id, trait_ref);
             }
         }
     }
 
-    fn report_overlap_error(&self, trait_def_id: DefId,
-                            impl1: DefId, impl2: DefId) {
-
-        span_err!(self.tcx.sess, self.span_of_impl(impl1), E0119,
-                  "conflicting implementations for trait `{}`",
-                  self.tcx.item_path_str(trait_def_id));
-
-        self.report_overlap_note(impl2);
-    }
+    fn report_overlap_error(&self,
+                            impl1: DefId,
+                            impl2: DefId,
+                            trait_ref: ty::TraitRef)
+    {
+        // only print the Self type if it's concrete; otherwise, it's not adding much information.
+        let self_type = {
+            trait_ref.substs.self_ty().and_then(|ty| {
+                if let ty::TyInfer(_) = ty.sty {
+                    None
+                } else {
+                    Some(format!(" for type `{}`", ty))
+                }
+            }).unwrap_or(String::new())
+        };
 
-    fn report_overlap_note(&self, impl2: DefId) {
+        let mut err = struct_span_err!(self.tcx.sess, self.span_of_impl(impl1), E0119,
+                                       "conflicting implementations of trait `{}`{}:",
+                                       trait_ref,
+                                       self_type);
 
         if impl2.is_local() {
-            span_note!(self.tcx.sess, self.span_of_impl(impl2),
-                       "note conflicting implementation here");
+            span_note!(&mut err, self.span_of_impl(impl2),
+                       "conflicting implementation is here:");
         } else {
             let cname = self.tcx.sess.cstore.crate_name(impl2.krate);
-            self.tcx.sess.note(&format!("conflicting implementation in crate `{}`", cname));
+            err.note(&format!("conflicting implementation in crate `{}`", cname));
         }
+        err.emit();
     }
 
     fn span_of_impl(&self, impl_did: DefId) -> Span {
@@ -171,42 +175,45 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
 impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> {
     fn visit_item(&mut self, item: &'v hir::Item) {
         match item.node {
-            hir::ItemDefaultImpl(_, _) => {
+            hir::ItemTrait(..) => {
+                let trait_def_id = self.tcx.map.local_def_id(item.id);
+                self.check_for_overlapping_impls_of_trait(trait_def_id);
+            }
+
+            hir::ItemDefaultImpl(..) => {
                 // look for another default impl; note that due to the
                 // general orphan/coherence rules, it must always be
                 // in this crate.
                 let impl_def_id = self.tcx.map.local_def_id(item.id);
                 let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap();
+
+                self.check_for_overlapping_impls_of_trait(trait_ref.def_id);
+
                 let prev_default_impl = self.default_impls.insert(trait_ref.def_id, item.id);
                 match prev_default_impl {
                     Some(prev_id) => {
-                        self.report_overlap_error(trait_ref.def_id,
-                                                  impl_def_id,
-                                                  self.tcx.map.local_def_id(prev_id));
+                        self.report_overlap_error(impl_def_id,
+                                                  self.tcx.map.local_def_id(prev_id),
+                                                  trait_ref);
                     }
                     None => { }
                 }
             }
-            hir::ItemImpl(_, _, _, Some(_), ref self_ty, _) => {
+            hir::ItemImpl(_, _, _, Some(_), _, _) => {
                 let impl_def_id = self.tcx.map.local_def_id(item.id);
                 let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap();
                 let trait_def_id = trait_ref.def_id;
+                self.check_for_overlapping_impls_of_trait(trait_def_id);
                 match trait_ref.self_ty().sty {
                     ty::TyTrait(ref data) => {
                         // This is something like impl Trait1 for Trait2. Illegal
                         // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe.
 
                         if !traits::is_object_safe(self.tcx, data.principal_def_id()) {
-                            // FIXME(#27579). This just means the
-                            // self-ty is illegal; WF will report this
-                            // error. But it will do so as a warning
-                            // for a release or two.  For backwards
-                            // compat reasons, then, we continue to
-                            // report it here so that things which
-                            // were errors remain errors.
-                            span_err!(self.tcx.sess, self_ty.span, E0372,
-                                      "the trait `{}` cannot be made into an object",
-                                      self.tcx.item_path_str(data.principal_def_id()));
+                            // This is an error, but it will be
+                            // reported by wfcheck.  Ignore it
+                            // here. This is tested by
+                            // `coherence-impl-trait-for-trait-object-safe.rs`.
                         } else {
                             let mut supertrait_def_ids =
                                 traits::supertrait_def_ids(self.tcx, data.principal_def_id());
index 40b38ad88394bcbadf28e915918ba341ffe3156a..936d26f9208502195de37c2de0cf30d908605bb4 100644 (file)
@@ -14,7 +14,6 @@
 use middle::ty;
 use rustc_front::intravisit;
 use rustc_front::hir;
-use rustc_front::hir::{Item, ItemImpl};
 
 pub fn check(tcx: &ty::ctxt) {
     let mut orphan = UnsafetyChecker { tcx: tcx };
index 86a56e718b5cf74bb958d3bdc80b7369c8bd27c5..eb204c5641495ed11d248ad1e4be8b3b80468a27 100644 (file)
@@ -34,13 +34,12 @@ lazilly and on demand, and include logic that checks for cycles.
 Demand is driven by calls to `AstConv::get_item_type_scheme` or
 `AstConv::lookup_trait_def`.
 
-Currently, we "convert" types and traits in three phases (note that
+Currently, we "convert" types and traits in two phases (note that
 conversion only affects the types of items / enum variants / methods;
 it does not e.g. compute the types of individual expressions):
 
 0. Intrinsics
-1. Trait definitions
-2. Type definitions
+1. Trait/Type definitions
 
 Conversion itself is done by simply walking each of the items in turn
 and invoking an appropriate function (e.g., `trait_def_of_item` or
@@ -56,38 +55,32 @@ There are some shortcomings in this design:
 - Because the type scheme includes defaults, cycles through type
   parameter defaults are illegal even if those defaults are never
   employed. This is not necessarily a bug.
-- The phasing of trait definitions before type definitions does not
-  seem to be necessary, sufficient, or particularly helpful, given that
-  processing a trait definition can trigger processing a type def and
-  vice versa. However, if I remove it, I get ICEs, so some more work is
-  needed in that area. -nmatsakis
 
 */
 
 use astconv::{self, AstConv, ty_of_arg, ast_ty_to_ty, ast_region_to_region};
+use lint;
 use middle::def;
 use middle::def_id::DefId;
 use constrained_type_params as ctp;
 use middle::lang_items::SizedTraitLangItem;
-use middle::free_region::FreeRegionMap;
-use middle::region;
 use middle::resolve_lifetime;
 use middle::const_eval::{self, ConstVal};
 use middle::const_eval::EvalHint::UncheckedExprHint;
 use middle::subst::{Substs, FnSpace, ParamSpace, SelfSpace, TypeSpace, VecPerParamSpace};
 use middle::ty::{ToPredicate, ImplContainer, ImplOrTraitItemContainer, TraitContainer};
-use middle::ty::{self, RegionEscape, ToPolyTraitRef, Ty, TypeScheme};
+use middle::ty::{self, ToPolyTraitRef, Ty, TypeScheme};
 use middle::ty::{VariantKind};
-use middle::ty::fold::{TypeFolder, TypeFoldable};
+use middle::ty::fold::{TypeFolder};
 use middle::ty::util::IntTypeExt;
-use middle::infer;
 use rscope::*;
+use rustc::dep_graph::DepNode;
 use rustc::front::map as hir_map;
-use util::common::{ErrorReported, memoized};
+use util::common::{ErrorReported, MemoizationMap};
 use util::nodemap::{FnvHashMap, FnvHashSet};
 use write_ty_to_tcx;
 
-use std::cell::{Cell, RefCell};
+use std::cell::RefCell;
 use std::collections::HashSet;
 use std::rc::Rc;
 
@@ -107,9 +100,6 @@ use rustc_front::print::pprust;
 pub fn collect_item_types(tcx: &ty::ctxt) {
     let ccx = &CrateCtxt { tcx: tcx, stack: RefCell::new(Vec::new()) };
 
-    let mut visitor = CollectTraitDefVisitor{ ccx: ccx };
-    ccx.tcx.map.krate().visit_all_items(&mut visitor);
-
     let mut visitor = CollectItemTypesVisitor{ ccx: ccx };
     ccx.tcx.map.krate().visit_all_items(&mut visitor);
 }
@@ -149,41 +139,17 @@ enum AstConvRequest {
 }
 
 ///////////////////////////////////////////////////////////////////////////
-// First phase: just collect *trait definitions* -- basically, the set
-// of type parameters and supertraits. This is information we need to
-// know later when parsing field defs.
-
-struct CollectTraitDefVisitor<'a, 'tcx: 'a> {
-    ccx: &'a CrateCtxt<'a, 'tcx>
-}
-
-impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for CollectTraitDefVisitor<'a, 'tcx> {
-    fn visit_item(&mut self, i: &hir::Item) {
-        match i.node {
-            hir::ItemTrait(..) => {
-                // computing the trait def also fills in the table
-                let _ = trait_def_of_item(self.ccx, i);
-            }
-            _ => { }
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Second phase: collection proper.
 
 struct CollectItemTypesVisitor<'a, 'tcx: 'a> {
     ccx: &'a CrateCtxt<'a, 'tcx>
 }
 
 impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for CollectItemTypesVisitor<'a, 'tcx> {
-    fn visit_item(&mut self, i: &hir::Item) {
-        convert_item(self.ccx, i);
-        intravisit::walk_item(self, i);
-    }
-    fn visit_foreign_item(&mut self, i: &hir::ForeignItem) {
-        convert_foreign_item(self.ccx, i);
-        intravisit::walk_foreign_item(self, i);
+    fn visit_item(&mut self, item: &hir::Item) {
+        let tcx = self.ccx.tcx;
+        let item_def_id = tcx.map.local_def_id(item.id);
+        let _task = tcx.dep_graph.in_task(DepNode::CollectItem(item_def_id));
+        convert_item(self.ccx, item);
     }
 }
 
@@ -195,16 +161,6 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> {
         ItemCtxt { ccx: self, param_bounds: param_bounds }
     }
 
-    fn method_ty(&self, method_id: ast::NodeId) -> Rc<ty::Method<'tcx>> {
-        let def_id = self.tcx.map.local_def_id(method_id);
-        match *self.tcx.impl_or_trait_items.borrow().get(&def_id).unwrap() {
-            ty::MethodTraitItem(ref mty) => mty.clone(),
-            _ => {
-                self.tcx.sess.bug(&format!("method with id {} has the wrong type", method_id));
-            }
-        }
-    }
-
     fn cycle_check<F,R>(&self,
                         span: Span,
                         request: AstConvRequest,
@@ -238,24 +194,24 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> {
         assert!(!cycle.is_empty());
         let tcx = self.tcx;
 
-        span_err!(tcx.sess, span, E0391,
+        let mut err = struct_span_err!(tcx.sess, span, E0391,
             "unsupported cyclic reference between types/traits detected");
 
         match cycle[0] {
             AstConvRequest::GetItemTypeScheme(def_id) |
             AstConvRequest::GetTraitDef(def_id) => {
-                tcx.sess.note(
+                err.note(
                     &format!("the cycle begins when processing `{}`...",
                              tcx.item_path_str(def_id)));
             }
             AstConvRequest::EnsureSuperPredicates(def_id) => {
-                tcx.sess.note(
+                err.note(
                     &format!("the cycle begins when computing the supertraits of `{}`...",
                              tcx.item_path_str(def_id)));
             }
             AstConvRequest::GetTypeParameterBounds(id) => {
                 let def = tcx.type_parameter_def(id);
-                tcx.sess.note(
+                err.note(
                     &format!("the cycle begins when computing the bounds \
                               for type parameter `{}`...",
                              def.name));
@@ -266,18 +222,18 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> {
             match *request {
                 AstConvRequest::GetItemTypeScheme(def_id) |
                 AstConvRequest::GetTraitDef(def_id) => {
-                    tcx.sess.note(
+                    err.note(
                         &format!("...which then requires processing `{}`...",
                                  tcx.item_path_str(def_id)));
                 }
                 AstConvRequest::EnsureSuperPredicates(def_id) => {
-                    tcx.sess.note(
+                    err.note(
                         &format!("...which then requires computing the supertraits of `{}`...",
                                  tcx.item_path_str(def_id)));
                 }
                 AstConvRequest::GetTypeParameterBounds(id) => {
                     let def = tcx.type_parameter_def(id);
-                    tcx.sess.note(
+                    err.note(
                         &format!("...which then requires computing the bounds \
                                   for type parameter `{}`...",
                                  def.name));
@@ -288,24 +244,25 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> {
         match cycle[0] {
             AstConvRequest::GetItemTypeScheme(def_id) |
             AstConvRequest::GetTraitDef(def_id) => {
-                tcx.sess.note(
+                err.note(
                     &format!("...which then again requires processing `{}`, completing the cycle.",
                              tcx.item_path_str(def_id)));
             }
             AstConvRequest::EnsureSuperPredicates(def_id) => {
-                tcx.sess.note(
+                err.note(
                     &format!("...which then again requires computing the supertraits of `{}`, \
                               completing the cycle.",
                              tcx.item_path_str(def_id)));
             }
             AstConvRequest::GetTypeParameterBounds(id) => {
                 let def = tcx.type_parameter_def(id);
-                tcx.sess.note(
+                err.note(
                     &format!("...which then again requires computing the bounds \
                               for type parameter `{}`, completing the cycle.",
                              def.name));
             }
         }
+        err.emit();
     }
 
     /// Loads the trait def for a given trait, returning ErrorReported if a cycle arises.
@@ -573,10 +530,10 @@ fn is_param<'tcx>(tcx: &ty::ctxt<'tcx>,
 
 fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                             container: ImplOrTraitItemContainer,
-                            sig: &hir::MethodSig,
-                            id: ast::NodeId,
                             name: ast::Name,
+                            id: ast::NodeId,
                             vis: hir::Visibility,
+                            sig: &hir::MethodSig,
                             untransformed_rcvr_ty: Ty<'tcx>,
                             rcvr_ty_generics: &ty::Generics<'tcx>,
                             rcvr_ty_predicates: &ty::GenericPredicates<'tcx>) {
@@ -681,33 +638,6 @@ fn convert_associated_type<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
        .insert(ccx.tcx.map.local_def_id(id), ty::TypeTraitItem(associated_type));
 }
 
-fn convert_methods<'a,'tcx,'i,I>(ccx: &CrateCtxt<'a, 'tcx>,
-                                 container: ImplOrTraitItemContainer,
-                                 methods: I,
-                                 untransformed_rcvr_ty: Ty<'tcx>,
-                                 rcvr_ty_generics: &ty::Generics<'tcx>,
-                                 rcvr_ty_predicates: &ty::GenericPredicates<'tcx>)
-    where I: Iterator<Item=(&'i hir::MethodSig, ast::NodeId, ast::Name, hir::Visibility, Span)>
-{
-    debug!("convert_methods(untransformed_rcvr_ty={:?}, rcvr_ty_generics={:?}, \
-                            rcvr_ty_predicates={:?})",
-           untransformed_rcvr_ty,
-           rcvr_ty_generics,
-           rcvr_ty_predicates);
-
-    for (sig, id, name, vis, _span) in methods {
-        convert_method(ccx,
-                       container,
-                       sig,
-                       id,
-                       name,
-                       vis,
-                       untransformed_rcvr_ty,
-                       rcvr_ty_generics,
-                       rcvr_ty_predicates);
-    }
-}
-
 fn ensure_no_ty_param_bounds(ccx: &CrateCtxt,
                                  span: Span,
                                  generics: &hir::Generics,
@@ -742,8 +672,12 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
     debug!("convert: item {} with id {}", it.name, it.id);
     match it.node {
         // These don't define types.
-        hir::ItemExternCrate(_) | hir::ItemUse(_) |
-        hir::ItemForeignMod(_) | hir::ItemMod(_) => {
+        hir::ItemExternCrate(_) | hir::ItemUse(_) | hir::ItemMod(_) => {
+        }
+        hir::ItemForeignMod(ref foreign_mod) => {
+            for item in &foreign_mod.items {
+                convert_foreign_item(ccx, item);
+            }
         }
         hir::ItemEnum(ref enum_definition, _) => {
             let (scheme, predicates) = convert_typed_item(ccx, it);
@@ -867,34 +801,17 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
                 }
             }
 
-            let methods = impl_items.iter().filter_map(|ii| {
-                if let hir::ImplItemKind::Method(ref sig, _) = ii.node {
+            for impl_item in impl_items {
+                if let hir::ImplItemKind::Method(ref sig, _) = impl_item.node {
                     // if the method specifies a visibility, use that, otherwise
                     // inherit the visibility from the impl (so `foo` in `pub impl
                     // { fn foo(); }` is public, but private in `impl { fn
                     // foo(); }`).
-                    let method_vis = ii.vis.inherit_from(parent_visibility);
-                    Some((sig, ii.id, ii.name, method_vis, ii.span))
-                } else {
-                    None
-                }
-            });
-            convert_methods(ccx,
-                            ImplContainer(def_id),
-                            methods,
-                            selfty,
-                            &ty_generics,
-                            &ty_predicates);
+                    let method_vis = impl_item.vis.inherit_from(parent_visibility);
 
-            for impl_item in impl_items {
-                if let hir::ImplItemKind::Method(ref sig, ref body) = impl_item.node {
-                    let body_id = body.id;
-                    check_method_self_type(ccx,
-                                           &BindingRscope::new(),
-                                           ccx.method_ty(impl_item.id),
-                                           selfty,
-                                           &sig.explicit_self,
-                                           body_id);
+                    convert_method(ccx, ImplContainer(def_id),
+                                   impl_item.name, impl_item.id, method_vis,
+                                   sig, selfty, &ty_generics, &ty_predicates);
                 }
             }
 
@@ -902,103 +819,80 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
         },
         hir::ItemTrait(_, _, _, ref trait_items) => {
             let trait_def = trait_def_of_item(ccx, it);
+            let def_id = trait_def.trait_ref.def_id;
             let _: Result<(), ErrorReported> = // any error is already reported, can ignore
-                ccx.ensure_super_predicates(it.span, ccx.tcx.map.local_def_id(it.id));
+                ccx.ensure_super_predicates(it.span, def_id);
             convert_trait_predicates(ccx, it);
-            let trait_predicates = tcx.lookup_predicates(ccx.tcx.map.local_def_id(it.id));
+            let trait_predicates = tcx.lookup_predicates(def_id);
 
             debug!("convert: trait_bounds={:?}", trait_predicates);
 
-            // Convert all the associated types.
+            // FIXME: is the ordering here important? I think it is.
+            let container = TraitContainer(def_id);
+
+            // Convert all the associated constants.
             for trait_item in trait_items {
-                match trait_item.node {
-                    hir::ConstTraitItem(ref ty, ref default) => {
-                        let ty = ccx.icx(&trait_predicates)
-                                    .to_ty(&ExplicitRscope, ty);
-                        tcx.register_item_type(ccx.tcx.map.local_def_id(trait_item.id),
-                                               TypeScheme {
-                                                   generics: trait_def.generics.clone(),
-                                                   ty: ty,
-                                               });
-                        convert_associated_const(ccx,
-                                                 TraitContainer(ccx.tcx.map.local_def_id(it.id)),
-                                                 trait_item.name,
-                                                 trait_item.id,
-                                                 hir::Public,
-                                                 ty,
-                                                 default.is_some())
-                    }
-                    _ => {}
+                if let hir::ConstTraitItem(ref ty, ref default) = trait_item.node {
+                    let ty = ccx.icx(&trait_predicates)
+                        .to_ty(&ExplicitRscope, ty);
+                    tcx.register_item_type(ccx.tcx.map.local_def_id(trait_item.id),
+                                           TypeScheme {
+                                               generics: trait_def.generics.clone(),
+                                               ty: ty,
+                                           });
+                    convert_associated_const(ccx,
+                                             container,
+                                             trait_item.name,
+                                             trait_item.id,
+                                             hir::Public,
+                                             ty,
+                                             default.is_some())
                 }
-            };
+            }
 
             // Convert all the associated types.
             for trait_item in trait_items {
-                match trait_item.node {
-                    hir::TypeTraitItem(_, ref opt_ty) => {
-                        let typ = opt_ty.as_ref().map({
-                            |ty| ccx.icx(&trait_predicates).to_ty(&ExplicitRscope, &ty)
-                        });
-
-                        convert_associated_type(ccx,
-                                                TraitContainer(ccx.tcx.map.local_def_id(it.id)),
-                                                trait_item.name,
-                                                trait_item.id,
-                                                hir::Public,
-                                                typ);
-                    }
-                    _ => {}
+                if let hir::TypeTraitItem(_, ref opt_ty) = trait_item.node {
+                    let typ = opt_ty.as_ref().map({
+                        |ty| ccx.icx(&trait_predicates).to_ty(&ExplicitRscope, &ty)
+                    });
+
+                    convert_associated_type(ccx,
+                                            container,
+                                            trait_item.name,
+                                            trait_item.id,
+                                            hir::Public,
+                                            typ);
                 }
-            };
+            }
 
-            let methods = trait_items.iter().filter_map(|ti| {
-                let sig = match ti.node {
-                    hir::MethodTraitItem(ref sig, _) => sig,
-                    _ => return None,
-                };
-                Some((sig, ti.id, ti.name, hir::Inherited, ti.span))
-            });
+            // Convert all the methods
+            for trait_item in trait_items {
+                if let hir::MethodTraitItem(ref sig, _) = trait_item.node {
+                    convert_method(ccx,
+                                   container,
+                                   trait_item.name,
+                                   trait_item.id,
+                                   hir::Inherited,
+                                   sig,
+                                   tcx.mk_self_type(),
+                                   &trait_def.generics,
+                                   &trait_predicates);
 
-            // Run convert_methods on the trait methods.
-            convert_methods(ccx,
-                            TraitContainer(ccx.tcx.map.local_def_id(it.id)),
-                            methods,
-                            tcx.mk_self_type(),
-                            &trait_def.generics,
-                            &trait_predicates);
+                }
+            }
 
             // Add an entry mapping
             let trait_item_def_ids = Rc::new(trait_items.iter().map(|trait_item| {
                 let def_id = ccx.tcx.map.local_def_id(trait_item.id);
                 match trait_item.node {
-                    hir::ConstTraitItem(..) => {
-                        ty::ConstTraitItemId(def_id)
-                    }
-                    hir::MethodTraitItem(..) => {
-                        ty::MethodTraitItemId(def_id)
-                    }
-                    hir::TypeTraitItem(..) => {
-                        ty::TypeTraitItemId(def_id)
-                    }
+                    hir::ConstTraitItem(..) => ty::ConstTraitItemId(def_id),
+                    hir::MethodTraitItem(..) => ty::MethodTraitItemId(def_id),
+                    hir::TypeTraitItem(..) => ty::TypeTraitItemId(def_id)
                 }
             }).collect());
             tcx.trait_item_def_ids.borrow_mut().insert(ccx.tcx.map.local_def_id(it.id),
                                                        trait_item_def_ids);
-
-            // This must be done after `collect_trait_methods` so that
-            // we have a method type stored for every method.
-            for trait_item in trait_items {
-                let sig = match trait_item.node {
-                    hir::MethodTraitItem(ref sig, _) => sig,
-                    _ => continue
-                };
-                check_method_self_type(ccx,
-                                       &BindingRscope::new(),
-                                       ccx.method_ty(trait_item.id),
-                                       tcx.mk_self_type(),
-                                       &sig.explicit_self,
-                                       it.id)
-            }
         },
         hir::ItemStruct(ref struct_def, _) => {
             let (scheme, predicates) = convert_typed_item(ccx, it);
@@ -1092,10 +986,11 @@ fn convert_struct_variant<'tcx>(tcx: &ty::ctxt<'tcx>,
             hir::NamedField(name, vis) => {
                 let dup_span = seen_fields.get(&name).cloned();
                 if let Some(prev_span) = dup_span {
-                    span_err!(tcx.sess, f.span, E0124,
-                              "field `{}` is already declared",
-                              name);
-                    span_note!(tcx.sess, prev_span, "previously declared here");
+                    let mut err = struct_span_err!(tcx.sess, f.span, E0124,
+                                                   "field `{}` is already declared",
+                                                   name);
+                    span_note!(&mut err, prev_span, "previously declared here");
+                    err.emit();
                 } else {
                     seen_fields.insert(name, f.span);
                 }
@@ -1111,7 +1006,12 @@ fn convert_struct_variant<'tcx>(tcx: &ty::ctxt<'tcx>,
         did: did,
         name: name,
         disr_val: disr_val,
-        fields: fields
+        fields: fields,
+        kind: match *def {
+            hir::VariantData::Struct(..) => ty::VariantKind::Struct,
+            hir::VariantData::Tuple(..) => ty::VariantKind::Tuple,
+            hir::VariantData::Unit(..) => ty::VariantKind::Unit,
+        }
     }
 }
 
@@ -1160,12 +1060,13 @@ fn convert_enum_def<'tcx>(tcx: &ty::ctxt<'tcx>,
                 None
             },
             Err(err) => {
-                span_err!(tcx.sess, err.span, E0080,
-                          "constant evaluation error: {}",
-                          err.description());
+                let mut diag = struct_span_err!(tcx.sess, err.span, E0080,
+                                                "constant evaluation error: {}",
+                                                err.description());
                 if !e.span.contains(err.span) {
-                    tcx.sess.span_note(e.span, "for enum discriminant here");
+                    diag.span_note(e.span, "for enum discriminant here");
                 }
+                diag.emit();
                 None
             }
         }
@@ -1334,13 +1235,14 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
 
     let paren_sugar = tcx.has_attr(def_id, "rustc_paren_sugar");
     if paren_sugar && !ccx.tcx.sess.features.borrow().unboxed_closures {
-        ccx.tcx.sess.span_err(
+        let mut err = ccx.tcx.sess.struct_span_err(
             it.span,
             "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \
              which traits can use parenthetical notation");
-        fileline_help!(ccx.tcx.sess, it.span,
+        fileline_help!(&mut err, it.span,
                    "add `#![feature(unboxed_closures)]` to \
                     the crate attributes to use it");
+        err.emit();
     }
 
     let substs = ccx.tcx.mk_substs(mk_trait_substs(ccx, generics));
@@ -1359,16 +1261,11 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
         substs: substs,
     };
 
-    let trait_def = ty::TraitDef {
-        paren_sugar: paren_sugar,
-        unsafety: unsafety,
-        generics: ty_generics,
-        trait_ref: trait_ref,
-        associated_type_names: associated_type_names,
-        nonblanket_impls: RefCell::new(FnvHashMap()),
-        blanket_impls: RefCell::new(vec![]),
-        flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS)
-    };
+    let trait_def = ty::TraitDef::new(unsafety,
+                                      paren_sugar,
+                                      ty_generics,
+                                      trait_ref,
+                                      associated_type_names);
 
     return tcx.intern_trait_def(trait_def);
 
@@ -1384,7 +1281,6 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                     .iter()
                     .enumerate()
                     .map(|(i, def)| ty::ReEarlyBound(ty::EarlyBoundRegion {
-                        def_id: tcx.map.local_def_id(def.lifetime.id),
                         space: TypeSpace,
                         index: i as u32,
                         name: def.lifetime.name
@@ -1529,12 +1425,17 @@ fn type_scheme_of_def_id<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
 }
 
 fn type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
-                                it: &hir::Item)
+                                item: &hir::Item)
                                 -> ty::TypeScheme<'tcx>
 {
-    memoized(&ccx.tcx.tcache,
-             ccx.tcx.map.local_def_id(it.id),
-             |_| compute_type_scheme_of_item(ccx, it))
+    let item_def_id = ccx.tcx.map.local_def_id(item.id);
+    ccx.tcx.tcache.memoize(item_def_id, || {
+        // NB. Since the `memoized` function enters a new task, and we
+        // are giving this task access to the item `item`, we must
+        // register a read.
+        ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id));
+        compute_type_scheme_of_item(ccx, item)
+    })
 }
 
 fn compute_type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
@@ -1648,13 +1549,18 @@ fn convert_typed_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
 
 fn type_scheme_of_foreign_item<'a, 'tcx>(
     ccx: &CrateCtxt<'a, 'tcx>,
-    it: &hir::ForeignItem,
+    item: &hir::ForeignItem,
     abi: abi::Abi)
     -> ty::TypeScheme<'tcx>
 {
-    memoized(&ccx.tcx.tcache,
-             ccx.tcx.map.local_def_id(it.id),
-             |_| compute_type_scheme_of_foreign_item(ccx, it, abi))
+    let item_def_id = ccx.tcx.map.local_def_id(item.id);
+    ccx.tcx.tcache.memoize(item_def_id, || {
+        // NB. Since the `memoized` function enters a new task, and we
+        // are giving this task access to the item `item`, we must
+        // register a read.
+        ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id));
+        compute_type_scheme_of_foreign_item(ccx, item, abi)
+    })
 }
 
 fn compute_type_scheme_of_foreign_item<'a, 'tcx>(
@@ -1859,10 +1765,8 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
     let early_lifetimes = early_bound_lifetimes_from_generics(space, ast_generics);
     for (index, param) in early_lifetimes.iter().enumerate() {
         let index = index as u32;
-        let def_id = tcx.map.local_def_id(param.lifetime.id);
         let region =
             ty::ReEarlyBound(ty::EarlyBoundRegion {
-                def_id: def_id,
                 space: space,
                 index: index,
                 name: param.lifetime.name
@@ -2012,6 +1916,17 @@ fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
 
     let parent = tcx.map.get_parent(param.id);
 
+    if space != TypeSpace && default.is_some() {
+        if !tcx.sess.features.borrow().default_type_parameter_fallback {
+            tcx.sess.add_lint(
+                lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
+                param.id,
+                param.span,
+                format!("defaults for type parameters are only allowed on type definitions, \
+                         like `struct` or `enum`"));
+        }
+    }
+
     let def = ty::TypeParameterDef {
         space: space,
         index: index,
@@ -2270,107 +2185,6 @@ fn mk_item_substs<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     Substs::new(types, regions)
 }
 
-/// Verifies that the explicit self type of a method matches the impl
-/// or trait. This is a bit weird but basically because right now we
-/// don't handle the general case, but instead map it to one of
-/// several pre-defined options using various heuristics, this method
-/// comes back to check after the fact that explicit type the user
-/// wrote actually matches what the pre-defined option said.
-fn check_method_self_type<'a, 'tcx, RS:RegionScope>(
-    ccx: &CrateCtxt<'a, 'tcx>,
-    rs: &RS,
-    method_type: Rc<ty::Method<'tcx>>,
-    required_type: Ty<'tcx>,
-    explicit_self: &hir::ExplicitSelf,
-    body_id: ast::NodeId)
-{
-    let tcx = ccx.tcx;
-    if let hir::SelfExplicit(ref ast_type, _) = explicit_self.node {
-        let typ = ccx.icx(&method_type.predicates).to_ty(rs, &**ast_type);
-        let base_type = match typ.sty {
-            ty::TyRef(_, tm) => tm.ty,
-            ty::TyBox(typ) => typ,
-            _ => typ,
-        };
-
-        let body_scope = tcx.region_maps.item_extent(body_id);
-
-        // "Required type" comes from the trait definition. It may
-        // contain late-bound regions from the method, but not the
-        // trait (since traits only have early-bound region
-        // parameters).
-        assert!(!base_type.has_regions_escaping_depth(1));
-        let required_type_free =
-            liberate_early_bound_regions(
-                tcx, body_scope,
-                &tcx.liberate_late_bound_regions(body_scope, &ty::Binder(required_type)));
-
-        // The "base type" comes from the impl. It too may have late-bound
-        // regions from the method.
-        assert!(!base_type.has_regions_escaping_depth(1));
-        let base_type_free =
-            liberate_early_bound_regions(
-                tcx, body_scope,
-                &tcx.liberate_late_bound_regions(body_scope, &ty::Binder(base_type)));
-
-        debug!("required_type={:?} required_type_free={:?} \
-                base_type={:?} base_type_free={:?}",
-               required_type,
-               required_type_free,
-               base_type,
-               base_type_free);
-
-        let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false);
-        drop(::require_same_types(tcx,
-                                  Some(&infcx),
-                                  false,
-                                  explicit_self.span,
-                                  base_type_free,
-                                  required_type_free,
-                                  || {
-                format!("mismatched self type: expected `{}`",
-                         required_type)
-        }));
-
-        // We could conceviably add more free-region relations here,
-        // but since this code is just concerned with checking that
-        // the `&Self` types etc match up, it's not really necessary.
-        // It would just allow people to be more approximate in some
-        // cases. In any case, we can do it later as we feel the need;
-        // I'd like this function to go away eventually.
-        let free_regions = FreeRegionMap::new();
-
-        infcx.resolve_regions_and_report_errors(&free_regions, body_id);
-    }
-
-    fn liberate_early_bound_regions<'tcx,T>(
-        tcx: &ty::ctxt<'tcx>,
-        scope: region::CodeExtent,
-        value: &T)
-        -> T
-        where T : TypeFoldable<'tcx>
-    {
-        /*!
-         * Convert early-bound regions into free regions; normally this is done by
-         * applying the `free_substs` from the `ParameterEnvironment`, but this particular
-         * method-self-type check is kind of hacky and done very early in the process,
-         * before we really have a `ParameterEnvironment` to check.
-         */
-
-        tcx.fold_regions(value, &mut false, |region, _| {
-            match region {
-                ty::ReEarlyBound(data) => {
-                    ty::ReFree(ty::FreeRegion {
-                        scope: scope,
-                        bound_region: ty::BrNamed(data.def_id, data.name)
-                    })
-                }
-                _ => region
-            }
-        })
-    }
-}
-
 /// Checks that all the type parameters on an impl
 fn enforce_impl_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
                                              ast_generics: &hir::Generics,
@@ -2440,9 +2254,7 @@ fn enforce_impl_lifetimes_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
                   .collect();
 
     for (index, lifetime_def) in ast_generics.lifetimes.iter().enumerate() {
-        let def_id = tcx.map.local_def_id(lifetime_def.lifetime.id);
-        let region = ty::EarlyBoundRegion { def_id: def_id,
-                                            space: TypeSpace,
+        let region = ty::EarlyBoundRegion { space: TypeSpace,
                                             index: index as u32,
                                             name: lifetime_def.lifetime.name };
         if
index 2d7178bd55c394f3ea0c881cb8b1a1754444817a..5f2582a548bacc6199f057d655aa4a97a9c5de41 100644 (file)
@@ -795,7 +795,7 @@ optional namespacing), a dereference, an indexing expression or a field
 reference.
 
 More details can be found here:
-https://doc.rust-lang.org/reference.html#lvalues,-rvalues-and-temporaries
+https://doc.rust-lang.org/reference.html#lvalues-rvalues-and-temporaries
 
 Now, we can go further. Here are some bad examples:
 
@@ -1569,8 +1569,8 @@ struct Foo {
     value: usize
 }
 
-impl MyTrait for Foo { // error: conflicting implementations for trait
-                       //        `MyTrait`
+impl MyTrait for Foo { // error: conflicting implementations of trait
+                       //        `MyTrait` for type `Foo`
     fn get(&self) -> usize { self.value }
 }
 ```
@@ -3114,14 +3114,6 @@ impl Baz for Bar { } // Note: This is OK
 ```
 "##,
 
-E0372: r##"
-Trying to implement a trait for a trait object (as in `impl Trait1 for
-Trait2 { ... }`) does not work if the trait is not object-safe. Please see the
-[RFC 255] for more details on object safety rules.
-
-[RFC 255]: https://github.com/rust-lang/rfcs/pull/255
-"##,
-
 E0379: r##"
 Trait methods cannot be declared `const` by design. For more information, see
 [RFC 911].
@@ -3486,6 +3478,7 @@ register_diagnostics! {
 //  E0319, // trait impls for defaulted traits allowed just for structs/enums
     E0320, // recursive overflow during dropck
     E0328, // cannot implement Unsize explicitly
+//  E0372, // coherence not object safe
     E0374, // the trait `CoerceUnsized` may only be implemented for a coercion
            // between structures with one field being coerced, none found
     E0375, // the trait `CoerceUnsized` may only be implemented for a coercion
index 495b8995ceea2f271c53add2f12b8ac54ee9626b..867d12a1def92eeef016d512918a40263ae3d30b 100644 (file)
@@ -62,11 +62,9 @@ independently:
 This API is completely unstable and subject to change.
 
 */
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
+
 #![crate_name = "rustc_typeck"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -94,6 +92,7 @@ extern crate rustc_platform_intrinsics as intrinsics;
 extern crate rustc_front;
 extern crate rustc_back;
 
+pub use rustc::dep_graph;
 pub use rustc::front;
 pub use rustc::lint;
 pub use rustc::middle;
@@ -104,13 +103,12 @@ use front::map as hir_map;
 use middle::def;
 use middle::infer::{self, TypeOrigin};
 use middle::subst;
-use middle::ty::{self, Ty, HasTypeFlags};
+use middle::ty::{self, Ty, TypeFoldable};
 use session::config;
 use util::common::time;
 use rustc_front::hir;
 
 use syntax::codemap::Span;
-use syntax::print::pprust::*;
 use syntax::{ast, abi};
 
 use std::cell::RefCell;
@@ -206,8 +204,9 @@ fn require_same_types<'a, 'tcx, M>(tcx: &ty::ctxt<'tcx>,
     match result {
         Ok(_) => true,
         Err(ref terr) => {
-            span_err!(tcx.sess, span, E0211, "{}: {}", msg(), terr);
-            tcx.note_and_explain_type_err(terr, span);
+            let mut err = struct_span_err!(tcx.sess, span, E0211, "{}: {}", msg(), terr);
+            tcx.note_and_explain_type_err(&mut err, terr, span);
+            err.emit();
             false
         }
     }
@@ -331,21 +330,24 @@ pub fn check_crate(tcx: &ty::ctxt, trait_map: ty::TraitMap) {
         tcx: tcx
     };
 
-    time(time_passes, "type collecting", ||
-         collect::collect_item_types(tcx));
-
     // this ensures that later parts of type checking can assume that items
     // have valid types and not error
-    tcx.sess.abort_if_errors();
+    tcx.sess.abort_if_new_errors(|| {
+        time(time_passes, "type collecting", ||
+             collect::collect_item_types(tcx));
+
+    });
 
     time(time_passes, "variance inference", ||
          variance::infer_variance(tcx));
 
-    time(time_passes, "coherence checking", ||
-        coherence::check_coherence(&ccx));
+    tcx.sess.abort_if_new_errors(|| {
+      time(time_passes, "coherence checking", ||
+          coherence::check_coherence(&ccx));
+    });
 
-    time(time_passes, "wf checking (old)", ||
-        check::check_wf_old(&ccx));
+    time(time_passes, "wf checking", ||
+        check::check_wf_new(&ccx));
 
     time(time_passes, "item-types checking", ||
         check::check_item_types(&ccx));
@@ -356,11 +358,6 @@ pub fn check_crate(tcx: &ty::ctxt, trait_map: ty::TraitMap) {
     time(time_passes, "drop-impl checking", ||
         check::check_drop_impls(&ccx));
 
-    // Do this last so that if there are errors in the old code, they
-    // get reported, and we don't get extra warnings.
-    time(time_passes, "wf checking (new)", ||
-        check::check_wf_new(&ccx));
-
     check_for_entry_fn(&ccx);
     tcx.sess.abort_if_errors();
 }
index 1fafe3484f0f24c9410606c225b94719f398424d..ce0e9e14035f51073965dccb22157995648eb48c 100644 (file)
@@ -266,6 +266,7 @@ use self::ParamKind::*;
 
 use arena;
 use arena::TypedArena;
+use dep_graph::DepNode;
 use middle::def_id::DefId;
 use middle::resolve_lifetime as rl;
 use middle::subst;
@@ -280,6 +281,7 @@ use rustc_front::intravisit::Visitor;
 use util::nodemap::NodeMap;
 
 pub fn infer_variance(tcx: &ty::ctxt) {
+    let _task = tcx.dep_graph.in_task(DepNode::Variance);
     let krate = tcx.map.krate();
     let mut arena = arena::TypedArena::new();
     let terms_cx = determine_parameters_to_be_inferred(tcx, &mut arena, krate);
@@ -1002,12 +1004,14 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
     /// Adds constraints appropriate for a region appearing in a
     /// context with ambient variance `variance`
     fn add_constraints_from_region(&mut self,
-                                   _generics: &ty::Generics<'tcx>,
+                                   generics: &ty::Generics<'tcx>,
                                    region: ty::Region,
                                    variance: VarianceTermPtr<'a>) {
         match region {
             ty::ReEarlyBound(ref data) => {
-                let node_id = self.tcx().map.as_local_node_id(data.def_id).unwrap();
+                let def_id =
+                    generics.regions.get(data.space, data.index as usize).def_id;
+                let node_id = self.tcx().map.as_local_node_id(def_id).unwrap();
                 if self.is_to_be_inferred(node_id) {
                     let index = self.inferred_index(node_id);
                     self.add_constraint(index, variance);
index 455e2feee4c2f9ba82a7101fd9ff06ebab0a3b84..46ecd3a80b5d12e8bc77f855773a888bc16caf36 100644 (file)
@@ -46,8 +46,8 @@ pub use tables::UNICODE_VERSION;
 /// This `struct` is created by the [`to_lowercase()`] method on [`char`]. See
 /// its documentation for more.
 ///
-/// [`to_lowercase()`]: primitive.char.html#method.escape_to_lowercase
-/// [`char`]: primitive.char.html
+/// [`to_lowercase()`]: ../primitive.char.html#method.to_lowercase
+/// [`char`]: ../primitive.char.html
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct ToLowercase(CaseMappingIter);
 
@@ -64,8 +64,8 @@ impl Iterator for ToLowercase {
 /// This `struct` is created by the [`to_uppercase()`] method on [`char`]. See
 /// its documentation for more.
 ///
-/// [`to_uppercase()`]: primitive.char.html#method.escape_to_uppercase
-/// [`char`]: primitive.char.html
+/// [`to_uppercase()`]: ../primitive.char.html#method.to_uppercase
+/// [`char`]: ../primitive.char.html
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct ToUppercase(CaseMappingIter);
 
@@ -126,7 +126,7 @@ impl char {
     ///
     /// A 'radix' here is sometimes also called a 'base'. A radix of two
     /// indicates a binary number, a radix of ten, decimal, and a radix of
-    /// sixteen, hexicdecimal, to give some common values. Arbitrary
+    /// sixteen, hexadecimal, to give some common values. Arbitrary
     /// radicum are supported.
     ///
     /// Compared to `is_numeric()`, this function only recognizes the characters
@@ -185,7 +185,7 @@ impl char {
     ///
     /// A 'radix' here is sometimes also called a 'base'. A radix of two
     /// indicates a binary number, a radix of ten, decimal, and a radix of
-    /// sixteen, hexicdecimal, to give some common values. Arbitrary
+    /// sixteen, hexadecimal, to give some common values. Arbitrary
     /// radicum are supported.
     ///
     /// 'Digit' is defined to be only the following characters:
index e440b1171863ca5d6879ab330eb7f5c492def77a..161da079110615b877a264c3bce951361fa16dbc 100644 (file)
 //! provide for basic string-related manipulations. This crate does not
 //! (yet) aim to provide a full set of Unicode tables.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustc_unicode"]
 #![unstable(feature = "unicode", issue = "27783")]
-#![cfg_attr(stage0, staged_api)]
 #![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",
@@ -34,9 +31,6 @@
        test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))]
 #![no_std]
 
-#![cfg_attr(stage0, feature(rustc_attrs))]
-#![cfg_attr(stage0, feature(no_std))]
-#![cfg_attr(stage0, allow(unused_attributes))]
 #![feature(core_char_ext)]
 #![feature(lang_items)]
 #![feature(staged_api)]
@@ -48,8 +42,8 @@ pub mod char;
 #[allow(deprecated)]
 pub mod str {
     pub use u_str::{UnicodeStr, SplitWhitespace};
-    pub use u_str::{utf8_char_width, is_utf16, Utf16Items, Utf16Item};
-    pub use u_str::{utf16_items, Utf16Encoder};
+    pub use u_str::{utf8_char_width, is_utf16};
+    pub use u_str::{Utf16Encoder};
 }
 
 // For use in libcollections, not re-exported in libstd.
index cf75cf525771408a7d9d64e5fc74ae3feeab2674..a147bea791c47f4ff1f95f3399cca7046f2af2d4 100644 (file)
 /// that the unicode parts of `CharExt` and `UnicodeStrPrelude` traits are based on.
 pub const UNICODE_VERSION: (u64, u64, u64) = (8, 0, 0);
 
-fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool {
+fn bsearch_range_table(c: char, r: &'static [(char, char)]) -> bool {
     use core::cmp::Ordering::{Equal, Less, Greater};
-    r.binary_search_by(|&(lo,hi)| {
-        if lo <= c && c <= hi { Equal }
-        else if hi < c { Less }
-        else { Greater }
-    }).is_ok()
+    r.binary_search_by(|&(lo, hi)| {
+         if c < lo {
+             Greater
+         } else if hi < c {
+             Less
+         } else {
+             Equal
+         }
+     })
+     .is_ok()
 }
 
 pub mod general_category {
@@ -1188,34 +1193,25 @@ pub mod property {
 }
 
 pub mod conversions {
-    use core::cmp::Ordering::{Equal, Less, Greater};
     use core::option::Option;
     use core::option::Option::{Some, None};
-    use core::result::Result::{Ok, Err};
 
     pub fn to_lower(c: char) -> [char; 3] {
         match bsearch_case_table(c, to_lowercase_table) {
-          None        => [c, '\0', '\0'],
-          Some(index) => to_lowercase_table[index].1
+            None        => [c, '\0', '\0'],
+            Some(index) => to_lowercase_table[index].1,
         }
     }
 
     pub fn to_upper(c: char) -> [char; 3] {
         match bsearch_case_table(c, to_uppercase_table) {
             None        => [c, '\0', '\0'],
-            Some(index) => to_uppercase_table[index].1
+            Some(index) => to_uppercase_table[index].1,
         }
     }
 
     fn bsearch_case_table(c: char, table: &'static [(char, [char; 3])]) -> Option<usize> {
-        match table.binary_search_by(|&(key, _)| {
-            if c == key { Equal }
-            else if key < c { Less }
-            else { Greater }
-        }) {
-            Ok(i) => Some(i),
-            Err(_) => None,
-        }
+        table.binary_search_by(|&(key, _)| key.cmp(&c)).ok()
     }
 
     const to_lowercase_table: &'static [(char, [char; 3])] = &[
index 4d9f5d5fdd4595a5a1738d09f8b6ddf50ce31535..f65c05672f68bd526ee3d4254fb76376becde7e4 100644 (file)
 //! This module provides functionality to `str` that requires the Unicode
 //! methods provided by the unicode parts of the CharExt trait.
 
-use char::{DecodeUtf16, decode_utf16};
 use core::char;
-use core::iter::{Cloned, Filter};
-use core::slice;
+use core::iter::Filter;
 use core::str::Split;
 
 /// An iterator over the non-whitespace substrings of a string,
@@ -127,97 +125,6 @@ pub fn is_utf16(v: &[u16]) -> bool {
     }
 }
 
-/// An iterator that decodes UTF-16 encoded codepoints from a vector
-/// of `u16`s.
-#[rustc_deprecated(since = "1.4.0", reason = "renamed to `char::DecodeUtf16`")]
-#[unstable(feature = "decode_utf16", reason = "not exposed in std", issue = "27830")]
-#[allow(deprecated)]
-#[derive(Clone)]
-pub struct Utf16Items<'a> {
-    decoder: DecodeUtf16<Cloned<slice::Iter<'a, u16>>>,
-}
-
-/// The possibilities for values decoded from a `u16` stream.
-#[rustc_deprecated(since = "1.4.0",
-                   reason = "`char::DecodeUtf16` uses `Result<char, u16>` instead")]
-#[unstable(feature = "decode_utf16", reason = "not exposed in std", issue = "27830")]
-#[allow(deprecated)]
-#[derive(Copy, PartialEq, Eq, Clone, Debug)]
-pub enum Utf16Item {
-    /// A valid codepoint.
-    ScalarValue(char),
-    /// An invalid surrogate without its pair.
-    LoneSurrogate(u16),
-}
-
-#[allow(deprecated)]
-impl Utf16Item {
-    /// Convert `self` to a `char`, taking `LoneSurrogate`s to the
-    /// replacement character (U+FFFD).
-    #[inline]
-    pub fn to_char_lossy(&self) -> char {
-        match *self {
-            Utf16Item::ScalarValue(c) => c,
-            Utf16Item::LoneSurrogate(_) => '\u{FFFD}',
-        }
-    }
-}
-
-#[rustc_deprecated(since = "1.4.0", reason = "use `char::DecodeUtf16` instead")]
-#[unstable(feature = "decode_utf16", reason = "not exposed in std", issue = "27830")]
-#[allow(deprecated)]
-impl<'a> Iterator for Utf16Items<'a> {
-    type Item = Utf16Item;
-
-    fn next(&mut self) -> Option<Utf16Item> {
-        self.decoder.next().map(|result| {
-            match result {
-                Ok(c) => Utf16Item::ScalarValue(c),
-                Err(s) => Utf16Item::LoneSurrogate(s),
-            }
-        })
-    }
-
-    #[inline]
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.decoder.size_hint()
-    }
-}
-
-/// Create an iterator over the UTF-16 encoded codepoints in `v`,
-/// returning invalid surrogates as `LoneSurrogate`s.
-///
-/// # Examples
-///
-/// ```
-/// #![feature(unicode, decode_utf16)]
-/// # #![allow(deprecated)]
-///
-/// extern crate rustc_unicode;
-///
-/// use rustc_unicode::str::Utf16Item::{ScalarValue, LoneSurrogate};
-///
-/// fn main() {
-///     // 𝄞mus<invalid>ic<invalid>
-///     let v = [0xD834, 0xDD1E, 0x006d, 0x0075,
-///              0x0073, 0xDD1E, 0x0069, 0x0063,
-///              0xD834];
-///
-///     assert_eq!(rustc_unicode::str::utf16_items(&v).collect::<Vec<_>>(),
-///                vec![ScalarValue('𝄞'),
-///                     ScalarValue('m'), ScalarValue('u'), ScalarValue('s'),
-///                     LoneSurrogate(0xDD1E),
-///                     ScalarValue('i'), ScalarValue('c'),
-///                     LoneSurrogate(0xD834)]);
-/// }
-/// ```
-#[rustc_deprecated(since = "1.4.0", reason = "renamed to `char::decode_utf16`")]
-#[unstable(feature = "decode_utf16", reason = "not exposed in std", issue = "27830")]
-#[allow(deprecated)]
-pub fn utf16_items<'a>(v: &'a [u16]) -> Utf16Items<'a> {
-    Utf16Items { decoder: decode_utf16(v.iter().cloned()) }
-}
-
 /// Iterator adaptor for encoding `char`s to UTF-16.
 #[derive(Clone)]
 pub struct Utf16Encoder<I> {
index a6314f5ec63c98ac850b47dd91054d8bc431f08f..30b478f486e0a8b1f771f0666b9e1824746152cd 100644 (file)
@@ -120,7 +120,8 @@ fn try_inline_def(cx: &DocContext, tcx: &ty::ctxt,
         attrs: load_attrs(cx, tcx, did),
         inner: inner,
         visibility: Some(hir::Public),
-        stability: stability::lookup(tcx, did).clean(cx),
+        stability: stability::lookup_stability(tcx, did).clean(cx),
+        deprecation: stability::lookup_deprecation(tcx, did).clean(cx),
         def_id: did,
     });
     Some(ret)
@@ -303,7 +304,8 @@ pub fn build_impl(cx: &DocContext,
             name: None,
             attrs: attrs,
             visibility: Some(hir::Inherited),
-            stability: stability::lookup(tcx, did).clean(cx),
+            stability: stability::lookup_stability(tcx, did).clean(cx),
+            deprecation: stability::lookup_deprecation(tcx, did).clean(cx),
             def_id: did,
         });
     }
@@ -319,7 +321,7 @@ pub fn build_impl(cx: &DocContext,
                 let did = assoc_const.def_id;
                 let type_scheme = tcx.lookup_item_type(did);
                 let default = if assoc_const.has_value {
-                    Some(const_eval::lookup_const_by_id(tcx, did, None)
+                    Some(const_eval::lookup_const_by_id(tcx, did, None, None)
                          .unwrap().span.to_src(cx))
                 } else {
                     None
@@ -333,7 +335,8 @@ pub fn build_impl(cx: &DocContext,
                     source: clean::Span::empty(),
                     attrs: vec![],
                     visibility: None,
-                    stability: stability::lookup(tcx, did).clean(cx),
+                    stability: stability::lookup_stability(tcx, did).clean(cx),
+                    deprecation: stability::lookup_deprecation(tcx, did).clean(cx),
                     def_id: did
                 })
             }
@@ -381,7 +384,8 @@ pub fn build_impl(cx: &DocContext,
                     source: clean::Span::empty(),
                     attrs: vec![],
                     visibility: None,
-                    stability: stability::lookup(tcx, did).clean(cx),
+                    stability: stability::lookup_stability(tcx, did).clean(cx),
+                    deprecation: stability::lookup_deprecation(tcx, did).clean(cx),
                     def_id: did
                 })
             }
@@ -414,7 +418,8 @@ pub fn build_impl(cx: &DocContext,
         name: None,
         attrs: attrs,
         visibility: Some(hir::Inherited),
-        stability: stability::lookup(tcx, did).clean(cx),
+        stability: stability::lookup_stability(tcx, did).clean(cx),
+        deprecation: stability::lookup_deprecation(tcx, did).clean(cx),
         def_id: did,
     });
 
@@ -474,7 +479,7 @@ fn build_const(cx: &DocContext, tcx: &ty::ctxt,
     use rustc::middle::const_eval;
     use rustc_front::print::pprust;
 
-    let expr = const_eval::lookup_const_by_id(tcx, did, None).unwrap_or_else(|| {
+    let expr = const_eval::lookup_const_by_id(tcx, did, None, None).unwrap_or_else(|| {
         panic!("expected lookup_const_by_id to succeed for {:?}", did);
     });
     debug!("converting constant expr {:?} to snippet", expr);
index 1336acb6b0355d170afad191f40ec12716d30c37..d2a5fd457d2fba4cdc9183f06796ad1d05ee037b 100644 (file)
@@ -62,7 +62,11 @@ mod simplify;
 
 // extract the stability index for a node from tcx, if possible
 fn get_stability(cx: &DocContext, def_id: DefId) -> Option<Stability> {
-    cx.tcx_opt().and_then(|tcx| stability::lookup(tcx, def_id)).clean(cx)
+    cx.tcx_opt().and_then(|tcx| stability::lookup_stability(tcx, def_id)).clean(cx)
+}
+
+fn get_deprecation(cx: &DocContext, def_id: DefId) -> Option<Deprecation> {
+    cx.tcx_opt().and_then(|tcx| stability::lookup_deprecation(tcx, def_id)).clean(cx)
 }
 
 pub trait Clean<T> {
@@ -108,7 +112,7 @@ impl<T, U> Clean<U> for ty::Binder<T> where T: Clean<U> {
     }
 }
 
-impl<T: Clean<U>, U> Clean<Vec<U>> for syntax::owned_slice::OwnedSlice<T> {
+impl<T: Clean<U>, U> Clean<Vec<U>> for P<[T]> {
     fn clean(&self, cx: &DocContext) -> Vec<U> {
         self.iter().map(|x| x.clean(cx)).collect()
     }
@@ -188,6 +192,7 @@ impl<'a, 'tcx> Clean<Crate> for visit_ast::RustdocVisitor<'a, 'tcx> {
                     attrs: child.attrs.clone(),
                     visibility: Some(hir::Public),
                     stability: None,
+                    deprecation: None,
                     def_id: DefId::local(prim.to_def_index()),
                     inner: PrimitiveItem(prim),
                 });
@@ -254,6 +259,7 @@ pub struct Item {
     pub visibility: Option<Visibility>,
     pub def_id: DefId,
     pub stability: Option<Stability>,
+    pub deprecation: Option<Deprecation>,
 }
 
 impl Item {
@@ -417,6 +423,7 @@ impl Clean<Item> for doctree::Module {
             source: whence.clean(cx),
             visibility: self.vis.clean(cx),
             stability: self.stab.clean(cx),
+            deprecation: self.depr.clean(cx),
             def_id: cx.map.local_def_id(self.id),
             inner: ModuleItem(Module {
                is_crate: self.is_crate,
@@ -1078,6 +1085,7 @@ impl Clean<Item> for doctree::Function {
             source: self.whence.clean(cx),
             visibility: self.vis.clean(cx),
             stability: self.stab.clean(cx),
+            deprecation: self.depr.clean(cx),
             def_id: cx.map.local_def_id(self.id),
             inner: FunctionItem(Function {
                 decl: self.decl.clean(cx),
@@ -1204,6 +1212,7 @@ impl Clean<Item> for doctree::Trait {
             def_id: cx.map.local_def_id(self.id),
             visibility: self.vis.clean(cx),
             stability: self.stab.clean(cx),
+            deprecation: self.depr.clean(cx),
             inner: TraitItem(Trait {
                 unsafety: self.unsafety,
                 items: self.items.clean(cx),
@@ -1254,6 +1263,7 @@ impl Clean<Item> for hir::TraitItem {
             def_id: cx.map.local_def_id(self.id),
             visibility: None,
             stability: get_stability(cx, cx.map.local_def_id(self.id)),
+            deprecation: get_deprecation(cx, cx.map.local_def_id(self.id)),
             inner: inner
         }
     }
@@ -1287,6 +1297,7 @@ impl Clean<Item> for hir::ImplItem {
             def_id: cx.map.local_def_id(self.id),
             visibility: self.vis.clean(cx),
             stability: get_stability(cx, cx.map.local_def_id(self.id)),
+            deprecation: get_deprecation(cx, cx.map.local_def_id(self.id)),
             inner: inner
         }
     }
@@ -1295,16 +1306,16 @@ impl Clean<Item> for hir::ImplItem {
 impl<'tcx> Clean<Item> for ty::Method<'tcx> {
     fn clean(&self, cx: &DocContext) -> Item {
         let (self_, sig) = match self.explicit_self {
-            ty::StaticExplicitSelfCategory => (hir::SelfStatic.clean(cx),
-                                               self.fty.sig.clone()),
+            ty::ExplicitSelfCategory::Static => (hir::SelfStatic.clean(cx),
+                                                 self.fty.sig.clone()),
             s => {
                 let sig = ty::Binder(ty::FnSig {
                     inputs: self.fty.sig.0.inputs[1..].to_vec(),
                     ..self.fty.sig.0.clone()
                 });
                 let s = match s {
-                    ty::ByValueExplicitSelfCategory => SelfValue,
-                    ty::ByReferenceExplicitSelfCategory(..) => {
+                    ty::ExplicitSelfCategory::ByValue => SelfValue,
+                    ty::ExplicitSelfCategory::ByReference(..) => {
                         match self.fty.sig.0.inputs[0].sty {
                             ty::TyRef(r, mt) => {
                                 SelfBorrowed(r.clean(cx), mt.mutbl.clean(cx))
@@ -1312,10 +1323,10 @@ impl<'tcx> Clean<Item> for ty::Method<'tcx> {
                             _ => unreachable!(),
                         }
                     }
-                    ty::ByBoxExplicitSelfCategory => {
+                    ty::ExplicitSelfCategory::ByBox => {
                         SelfExplicit(self.fty.sig.0.inputs[0].clean(cx))
                     }
-                    ty::StaticExplicitSelfCategory => unreachable!(),
+                    ty::ExplicitSelfCategory::Static => unreachable!(),
                 };
                 (s, sig)
             }
@@ -1357,6 +1368,7 @@ impl<'tcx> Clean<Item> for ty::Method<'tcx> {
             name: Some(self.name.clean(cx)),
             visibility: Some(hir::Inherited),
             stability: get_stability(cx, self.def_id),
+            deprecation: get_deprecation(cx, self.def_id),
             def_id: self.def_id,
             attrs: inline::load_attrs(cx, cx.tcx(), self.def_id),
             source: Span::empty(),
@@ -1572,8 +1584,13 @@ impl Clean<Type> for hir::Ty {
                 resolve_type(cx, p.clean(cx), self.id)
             }
             TyPath(Some(ref qself), ref p) => {
-                let mut trait_path = p.clone();
-                trait_path.segments.pop();
+                let mut segments: Vec<_> = p.segments.clone().into();
+                segments.pop();
+                let trait_path = hir::Path {
+                    span: p.span,
+                    global: p.global,
+                    segments: segments.into(),
+                };
                 Type::QPath {
                     name: p.segments.last().unwrap().identifier.name.clean(cx),
                     self_type: box qself.ty.clean(cx),
@@ -1715,6 +1732,7 @@ impl Clean<Item> for hir::StructField {
             source: self.span.clean(cx),
             visibility: Some(vis),
             stability: get_stability(cx, cx.map.local_def_id(self.node.id)),
+            deprecation: get_deprecation(cx, cx.map.local_def_id(self.node.id)),
             def_id: cx.map.local_def_id(self.node.id),
             inner: StructFieldItem(TypedStructField(self.node.ty.clean(cx))),
         }
@@ -1740,6 +1758,7 @@ impl<'tcx> Clean<Item> for ty::FieldDefData<'tcx, 'static> {
             source: Span::empty(),
             visibility: Some(self.vis),
             stability: get_stability(cx, self.did),
+            deprecation: get_deprecation(cx, self.did),
             def_id: self.did,
             inner: StructFieldItem(TypedStructField(self.unsubst_ty().clean(cx))),
         }
@@ -1771,6 +1790,7 @@ impl Clean<Item> for doctree::Struct {
             def_id: cx.map.local_def_id(self.id),
             visibility: self.vis.clean(cx),
             stability: self.stab.clean(cx),
+            deprecation: self.depr.clean(cx),
             inner: StructItem(Struct {
                 struct_type: self.struct_type,
                 generics: self.generics.clean(cx),
@@ -1817,6 +1837,7 @@ impl Clean<Item> for doctree::Enum {
             def_id: cx.map.local_def_id(self.id),
             visibility: self.vis.clean(cx),
             stability: self.stab.clean(cx),
+            deprecation: self.depr.clean(cx),
             inner: EnumItem(Enum {
                 variants: self.variants.clean(cx),
                 generics: self.generics.clean(cx),
@@ -1839,6 +1860,7 @@ impl Clean<Item> for doctree::Variant {
             source: self.whence.clean(cx),
             visibility: None,
             stability: self.stab.clean(cx),
+            deprecation: self.depr.clean(cx),
             def_id: cx.map.local_def_id(self.def.id()),
             inner: VariantItem(Variant {
                 kind: struct_def_to_variant_kind(&self.def, cx),
@@ -1876,6 +1898,7 @@ impl<'tcx> Clean<Item> for ty::VariantDefData<'tcx, 'static> {
                             //        at the needed information here.
                             def_id: self.did,
                             stability: get_stability(cx, self.did),
+                            deprecation: get_deprecation(cx, self.did),
                             inner: StructFieldItem(
                                 TypedStructField(field.unsubst_ty().clean(cx))
                             )
@@ -1892,6 +1915,7 @@ impl<'tcx> Clean<Item> for ty::VariantDefData<'tcx, 'static> {
             def_id: self.did,
             inner: VariantItem(Variant { kind: kind }),
             stability: get_stability(cx, self.did),
+            deprecation: get_deprecation(cx, self.did),
         }
     }
 }
@@ -2067,6 +2091,7 @@ impl Clean<Item> for doctree::Typedef {
             def_id: cx.map.local_def_id(self.id.clone()),
             visibility: self.vis.clean(cx),
             stability: self.stab.clean(cx),
+            deprecation: self.depr.clean(cx),
             inner: TypedefItem(Typedef {
                 type_: self.ty.clean(cx),
                 generics: self.gen.clean(cx),
@@ -2118,6 +2143,7 @@ impl Clean<Item> for doctree::Static {
             def_id: cx.map.local_def_id(self.id),
             visibility: self.vis.clean(cx),
             stability: self.stab.clean(cx),
+            deprecation: self.depr.clean(cx),
             inner: StaticItem(Static {
                 type_: self.type_.clean(cx),
                 mutability: self.mutability.clean(cx),
@@ -2142,6 +2168,7 @@ impl Clean<Item> for doctree::Constant {
             def_id: cx.map.local_def_id(self.id),
             visibility: self.vis.clean(cx),
             stability: self.stab.clean(cx),
+            deprecation: self.depr.clean(cx),
             inner: ConstantItem(Constant {
                 type_: self.type_.clean(cx),
                 expr: self.expr.span.to_src(cx),
@@ -2216,6 +2243,7 @@ impl Clean<Vec<Item>> for doctree::Impl {
             def_id: cx.map.local_def_id(self.id),
             visibility: self.vis.clean(cx),
             stability: self.stab.clean(cx),
+            deprecation: self.depr.clean(cx),
             inner: ImplItem(Impl {
                 unsafety: self.unsafety,
                 generics: self.generics.clean(cx),
@@ -2298,6 +2326,7 @@ impl Clean<Item> for doctree::DefaultImpl {
             def_id: cx.map.local_def_id(self.id),
             visibility: Some(hir::Public),
             stability: None,
+            deprecation: None,
             inner: DefaultImplItem(DefaultImpl {
                 unsafety: self.unsafety,
                 trait_: self.trait_.clean(cx),
@@ -2315,6 +2344,7 @@ impl Clean<Item> for doctree::ExternCrate {
             def_id: cx.map.local_def_id(0),
             visibility: self.vis.clean(cx),
             stability: None,
+            deprecation: None,
             inner: ExternCrateItem(self.name.clean(cx), self.path.clone())
         }
     }
@@ -2380,6 +2410,7 @@ impl Clean<Vec<Item>> for doctree::Import {
             def_id: cx.map.local_def_id(0),
             visibility: self.vis.clean(cx),
             stability: None,
+            deprecation: None,
             inner: ImportItem(inner)
         });
         ret
@@ -2466,6 +2497,7 @@ impl Clean<Item> for hir::ForeignItem {
             def_id: cx.map.local_def_id(self.id),
             visibility: self.vis.clean(cx),
             stability: get_stability(cx, cx.map.local_def_id(self.id)),
+            deprecation: get_deprecation(cx, cx.map.local_def_id(self.id)),
             inner: inner,
         }
     }
@@ -2659,6 +2691,7 @@ impl Clean<Item> for doctree::Macro {
             source: self.whence.clean(cx),
             visibility: hir::Public.clean(cx),
             stability: self.stab.clean(cx),
+            deprecation: self.depr.clean(cx),
             def_id: cx.map.local_def_id(self.id),
             inner: MacroItem(Macro {
                 source: format!("macro_rules! {} {{\n{}}}",
@@ -2680,6 +2713,12 @@ pub struct Stability {
     pub issue: Option<u32>
 }
 
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
+pub struct Deprecation {
+    pub since: String,
+    pub note: String,
+}
+
 impl Clean<Stability> for attr::Stability {
     fn clean(&self, _: &DocContext) -> Stability {
         Stability {
@@ -2689,12 +2728,12 @@ impl Clean<Stability> for attr::Stability {
                 attr::Stable {ref since} => since.to_string(),
                 _ => "".to_string(),
             },
-            deprecated_since: match self.depr {
-                Some(attr::Deprecation {ref since, ..}) => since.to_string(),
+            deprecated_since: match self.rustc_depr {
+                Some(attr::RustcDeprecation {ref since, ..}) => since.to_string(),
                 _=> "".to_string(),
             },
             reason: {
-                if let Some(ref depr) = self.depr {
+                if let Some(ref depr) = self.rustc_depr {
                     depr.reason.to_string()
                 } else if let attr::Unstable {reason: Some(ref reason), ..} = self.level {
                     reason.to_string()
@@ -2716,6 +2755,15 @@ impl<'a> Clean<Stability> for &'a attr::Stability {
     }
 }
 
+impl Clean<Deprecation> for attr::Deprecation {
+    fn clean(&self, _: &DocContext) -> Deprecation {
+        Deprecation {
+            since: self.since.as_ref().map_or("".to_string(), |s| s.to_string()),
+            note: self.note.as_ref().map_or("".to_string(), |s| s.to_string()),
+        }
+    }
+}
+
 impl<'tcx> Clean<Item> for ty::AssociatedConst<'tcx> {
     fn clean(&self, cx: &DocContext) -> Item {
         Item {
@@ -2726,6 +2774,7 @@ impl<'tcx> Clean<Item> for ty::AssociatedConst<'tcx> {
             visibility: None,
             def_id: self.def_id,
             stability: None,
+            deprecation: None,
         }
     }
 }
@@ -2782,7 +2831,8 @@ impl<'tcx> Clean<Item> for ty::AssociatedType<'tcx> {
             inner: AssociatedTypeItem(bounds, self.ty.clean(cx)),
             visibility: self.vis.clean(cx),
             def_id: self.def_id,
-            stability: stability::lookup(cx.tcx(), self.def_id).clean(cx),
+            stability: stability::lookup_stability(cx.tcx(), self.def_id).clean(cx),
+            deprecation: stability::lookup_deprecation(cx.tcx(), self.def_id).clean(cx),
         }
     }
 }
index 1ccab1b16ebdb09ee2e91d86ad616b194e31d0f7..a7fd170b91c37778196b1a080c66acd5c605aeee 100644 (file)
@@ -22,7 +22,8 @@ use rustc_resolve as resolve;
 use rustc_front::lowering::{lower_crate, LoweringContext};
 use rustc_metadata::cstore::CStore;
 
-use syntax::{ast, codemap, diagnostic};
+use syntax::{ast, codemap, errors};
+use syntax::errors::emitter::ColorConfig;
 use syntax::feature_gate::UnstableFeatures;
 use syntax::parse::token;
 
@@ -116,15 +117,16 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
         ..config::basic_options().clone()
     };
 
-    let codemap = codemap::CodeMap::new();
-    let diagnostic_handler = diagnostic::Handler::new(diagnostic::Auto, None, true);
-    let span_diagnostic_handler =
-        diagnostic::SpanHandler::new(diagnostic_handler, codemap);
+    let codemap = Rc::new(codemap::CodeMap::new());
+    let diagnostic_handler = errors::Handler::with_tty_emitter(ColorConfig::Auto,
+                                                               None,
+                                                               true,
+                                                               false,
+                                                               codemap.clone());
 
     let cstore = Rc::new(CStore::new(token::get_ident_interner()));
-    let cstore_ = ::rustc_driver::cstore_to_cratestore(cstore.clone());
-    let sess = session::build_session_(sessopts, cpath,
-                                       span_diagnostic_handler, cstore_);
+    let sess = session::build_session_(sessopts, cpath, diagnostic_handler,
+                                       codemap, cstore.clone());
     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
 
     let mut cfg = config::build_configuration(&sess);
@@ -152,6 +154,7 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
                                         &name,
                                         resolve::MakeGlobMap::No,
                                         |tcx, _, analysis| {
+        let _ignore = tcx.dep_graph.in_ignore();
         let ty::CrateAnalysis { access_levels, .. } = analysis;
 
         // Convert from a NodeId set to a DefId set since we don't always have easy access
index 0129ab43cefd87bb33255f6f2f1ad79dcc2ea623..fc0422b3a3f037ad6be59491121c57deb068532f 100644 (file)
@@ -24,7 +24,7 @@ use rustc_front::hir;
 
 pub struct Module {
     pub name: Option<Name>,
-    pub attrs: Vec<ast::Attribute>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub where_outer: Span,
     pub where_inner: Span,
     pub extern_crates: Vec<ExternCrate>,
@@ -40,6 +40,7 @@ pub struct Module {
     pub traits: Vec<Trait>,
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
     pub impls: Vec<Impl>,
     pub def_traits: Vec<DefaultImpl>,
     pub foreigns: Vec<hir::ForeignMod>,
@@ -54,9 +55,10 @@ impl Module {
             id: 0,
             vis: hir::Inherited,
             stab: None,
+            depr: None,
             where_outer: syntax::codemap::DUMMY_SP,
             where_inner: syntax::codemap::DUMMY_SP,
-            attrs      : Vec::new(),
+            attrs      : hir::HirVec::new(),
             extern_crates: Vec::new(),
             imports    : Vec::new(),
             structs    : Vec::new(),
@@ -96,21 +98,23 @@ pub enum TypeBound {
 pub struct Struct {
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
     pub id: NodeId,
     pub struct_type: StructType,
     pub name: Name,
     pub generics: hir::Generics,
-    pub attrs: Vec<ast::Attribute>,
-    pub fields: Vec<hir::StructField>,
+    pub attrs: hir::HirVec<ast::Attribute>,
+    pub fields: hir::HirVec<hir::StructField>,
     pub whence: Span,
 }
 
 pub struct Enum {
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
-    pub variants: Vec<Variant>,
+    pub depr: Option<attr::Deprecation>,
+    pub variants: hir::HirVec<Variant>,
     pub generics: hir::Generics,
-    pub attrs: Vec<ast::Attribute>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub id: NodeId,
     pub whence: Span,
     pub name: Name,
@@ -118,19 +122,21 @@ pub struct Enum {
 
 pub struct Variant {
     pub name: Name,
-    pub attrs: Vec<ast::Attribute>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub def: hir::VariantData,
     pub stab: Option<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
     pub whence: Span,
 }
 
 pub struct Function {
     pub decl: hir::FnDecl,
-    pub attrs: Vec<ast::Attribute>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub id: NodeId,
     pub name: Name,
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
     pub unsafety: hir::Unsafety,
     pub constness: hir::Constness,
     pub whence: Span,
@@ -143,10 +149,11 @@ pub struct Typedef {
     pub gen: hir::Generics,
     pub name: Name,
     pub id: ast::NodeId,
-    pub attrs: Vec<ast::Attribute>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub whence: Span,
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
 }
 
 #[derive(Debug)]
@@ -155,9 +162,10 @@ pub struct Static {
     pub mutability: hir::Mutability,
     pub expr: P<hir::Expr>,
     pub name: Name,
-    pub attrs: Vec<ast::Attribute>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
     pub id: ast::NodeId,
     pub whence: Span,
 }
@@ -166,9 +174,10 @@ pub struct Constant {
     pub type_: P<hir::Ty>,
     pub expr: P<hir::Expr>,
     pub name: Name,
-    pub attrs: Vec<ast::Attribute>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
     pub id: ast::NodeId,
     pub whence: Span,
 }
@@ -176,14 +185,15 @@ pub struct Constant {
 pub struct Trait {
     pub unsafety: hir::Unsafety,
     pub name: Name,
-    pub items: Vec<hir::TraitItem>,
+    pub items: hir::HirVec<hir::TraitItem>,
     pub generics: hir::Generics,
-    pub bounds: Vec<hir::TyParamBound>,
-    pub attrs: Vec<ast::Attribute>,
+    pub bounds: hir::HirVec<hir::TyParamBound>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub id: ast::NodeId,
     pub whence: Span,
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
 }
 
 pub struct Impl {
@@ -192,11 +202,12 @@ pub struct Impl {
     pub generics: hir::Generics,
     pub trait_: Option<hir::TraitRef>,
     pub for_: P<hir::Ty>,
-    pub items: Vec<hir::ImplItem>,
-    pub attrs: Vec<ast::Attribute>,
+    pub items: hir::HirVec<hir::ImplItem>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub whence: Span,
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
     pub id: ast::NodeId,
 }
 
@@ -204,17 +215,18 @@ pub struct DefaultImpl {
     pub unsafety: hir::Unsafety,
     pub trait_: hir::TraitRef,
     pub id: ast::NodeId,
-    pub attrs: Vec<ast::Attribute>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub whence: Span,
 }
 
 pub struct Macro {
     pub name: Name,
     pub id: ast::NodeId,
-    pub attrs: Vec<ast::Attribute>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub whence: Span,
-    pub matchers: Vec<Span>,
+    pub matchers: hir::HirVec<Span>,
     pub stab: Option<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
     pub imported_from: Option<Name>,
 }
 
@@ -222,14 +234,14 @@ pub struct ExternCrate {
     pub name: Name,
     pub path: Option<String>,
     pub vis: hir::Visibility,
-    pub attrs: Vec<ast::Attribute>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub whence: Span,
 }
 
 pub struct Import {
     pub id: NodeId,
     pub vis: hir::Visibility,
-    pub attrs: Vec<ast::Attribute>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub node: hir::ViewPath_,
     pub whence: Span,
 }
index 0a1860c66f273ff36be921c64251a48cfa238dfa..5a4f95d1a1a5a7b24f4ce8050e7faeb105b83b09 100644 (file)
@@ -19,7 +19,7 @@ pub trait DocFolder : Sized {
 
     /// don't override!
     fn fold_item_recur(&mut self, item: Item) -> Option<Item> {
-        let Item { attrs, name, source, visibility, def_id, inner, stability } = item;
+        let Item { attrs, name, source, visibility, def_id, inner, stability, deprecation } = item;
         let inner = inner;
         let inner = match inner {
             StructItem(mut i) => {
@@ -66,7 +66,8 @@ pub trait DocFolder : Sized {
         };
 
         Some(Item { attrs: attrs, name: name, source: source, inner: inner,
-                    visibility: visibility, stability: stability, def_id: def_id })
+                    visibility: visibility, stability: stability, deprecation: deprecation,
+                    def_id: def_id })
     }
 
     fn fold_mod(&mut self, m: Module) -> Module {
index 45969ed0c644d40da55040901ff628d4f7828ef9..850045382e1f2c95d1f30d75557f403a352ffa49 100644 (file)
@@ -819,7 +819,7 @@ fn clean_srcpath<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F) wh
     F: FnMut(&str),
 {
     // make it relative, if possible
-    let p = p.relative_from(src_root).unwrap_or(p);
+    let p = p.strip_prefix(src_root).unwrap_or(p);
 
     let mut iter = p.iter().map(|x| x.to_str().unwrap()).peekable();
     while let Some(c) = iter.next() {
@@ -1058,14 +1058,16 @@ impl DocFolder for Cache {
                         }
                     });
 
-                    self.search_index.push(IndexItem {
-                        ty: shortty(&item),
-                        name: s.to_string(),
-                        path: path.join("::").to_string(),
-                        desc: shorter(item.doc_value()),
-                        parent: parent,
-                        search_type: get_index_search_type(&item, parent_basename),
-                    });
+                    if item.def_id.index != CRATE_DEF_INDEX {
+                        self.search_index.push(IndexItem {
+                            ty: shortty(&item),
+                            name: s.to_string(),
+                            path: path.join("::").to_string(),
+                            desc: shorter(item.doc_value()),
+                            parent: parent,
+                            search_type: get_index_search_type(&item, parent_basename),
+                        });
+                    }
                 }
                 (Some(parent), None) if is_method || (!self.privmod && !hidden_field)=> {
                     if parent.is_local() {
@@ -1801,7 +1803,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
 }
 
 fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Option<String> {
-    item.stability.as_ref().and_then(|stab| {
+    let mut result = item.stability.as_ref().and_then(|stab| {
         let reason = if show_reason && !stab.reason.is_empty() {
             format!(": {}", stab.reason)
         } else {
@@ -1817,10 +1819,10 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Optio
         } else if stab.level == stability::Unstable {
             let unstable_extra = if show_reason {
                 match (!stab.feature.is_empty(), &cx.issue_tracker_base_url, stab.issue) {
-                    (true, &Some(ref tracker_url), Some(issue_no)) =>
+                    (true, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 =>
                         format!(" (<code>{}</code> <a href=\"{}{}\">#{}</a>)",
                                 Escape(&stab.feature), tracker_url, issue_no, issue_no),
-                    (false, &Some(ref tracker_url), Some(issue_no)) =>
+                    (false, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 =>
                         format!(" (<a href=\"{}{}\">#{}</a>)", Escape(&tracker_url), issue_no,
                                 issue_no),
                     (true, _, _) =>
@@ -1836,7 +1838,27 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Optio
         };
         Some(format!("<em class='stab {}'>{}</em>",
                      item.stability_class(), text))
-    })
+    });
+
+    if result.is_none() {
+        result = item.deprecation.as_ref().and_then(|depr| {
+            let note = if show_reason && !depr.note.is_empty() {
+                format!(": {}", depr.note)
+            } else {
+                String::new()
+            };
+            let since = if show_reason && !depr.since.is_empty() {
+                format!(" since {}", Escape(&depr.since))
+            } else {
+                String::new()
+            };
+
+            let text = format!("Deprecated{}{}", since, Markdown(&note));
+            Some(format!("<em class='stab deprecated'>{}</em>", text))
+        });
+    }
+
+    result
 }
 
 struct Initializer<'a>(&'a str);
index 9b1db51bba50f4092d9026db0c886cd60ab1b018..0bde582c19f286da7b12798c378e25e4fbb3eefb 100644 (file)
@@ -143,6 +143,10 @@ pre {
     padding: 20px;
 }
 
+img {
+    max-width: 100%;
+}
+
 .content.source {
     margin-top: 50px;
     max-width: none;
index af6510cb3870e1fcf44b9739f30e596c692b68ca..ac5b64f37aafedf9e940823ef1d6d8b5391a157d 100644 (file)
@@ -8,23 +8,19 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "rustdoc"]
 #![unstable(feature = "rustdoc", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![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/",
-   html_playground_url = "https://play.rust-lang.org/")]
+       html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
+       html_root_url = "https://doc.rust-lang.org/nightly/",
+       html_playground_url = "https://play.rust-lang.org/")]
 
 #![feature(box_patterns)]
 #![feature(box_syntax)]
 #![feature(dynamic_lib)]
 #![feature(libc)]
-#![feature(path_relative_from)]
 #![feature(rustc_private)]
 #![feature(set_stdio)]
 #![feature(slice_patterns)]
@@ -53,6 +49,7 @@ extern crate serialize as rustc_serialize; // used by deriving
 
 use std::cell::RefCell;
 use std::collections::HashMap;
+use std::default::Default;
 use std::env;
 use std::fs::File;
 use std::io::{self, Read, Write};
@@ -65,7 +62,7 @@ use externalfiles::ExternalHtml;
 use serialize::Decodable;
 use serialize::json::{self, Json};
 use rustc::session::search_paths::SearchPaths;
-use syntax::diagnostic;
+use rustc::session::config::ErrorOutputType;
 
 // reexported from `clean` so it can be easily updated with the mod itself
 pub use clean::SCHEMA_VERSION;
@@ -228,7 +225,7 @@ pub fn main_args(args: &[String]) -> isize {
 
     let mut libs = SearchPaths::new();
     for s in &matches.opt_strs("L") {
-        libs.add_path(s, diagnostic::Auto);
+        libs.add_path(s, ErrorOutputType::default());
     }
     let externs = match parse_externs(&matches) {
         Ok(ex) => ex,
@@ -363,7 +360,7 @@ fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matche
     // First, parse the crate and extract all relevant information.
     let mut paths = SearchPaths::new();
     for s in &matches.opt_strs("L") {
-        paths.add_path(s, diagnostic::Auto);
+        paths.add_path(s, ErrorOutputType::default());
     }
     let cfgs = matches.opt_strs("cfg");
     let triple = matches.opt_str("target");
index 3322794c7781e28827a66df5cb66ebb52a49d445..de117b44b6f6ac60a34862d60b57b5c198b616ce 100644 (file)
@@ -34,7 +34,8 @@ use rustc_back::tempdir::TempDir;
 use rustc_driver::{driver, Compilation};
 use rustc_metadata::cstore::CStore;
 use syntax::codemap::CodeMap;
-use syntax::diagnostic;
+use syntax::errors;
+use syntax::errors::emitter::ColorConfig;
 use syntax::parse::token;
 
 use core;
@@ -71,17 +72,19 @@ pub fn run(input: &str,
         ..config::basic_options().clone()
     };
 
-    let codemap = CodeMap::new();
-    let diagnostic_handler = diagnostic::Handler::new(diagnostic::Auto, None, true);
-    let span_diagnostic_handler =
-    diagnostic::SpanHandler::new(diagnostic_handler, codemap);
+    let codemap = Rc::new(CodeMap::new());
+    let diagnostic_handler = errors::Handler::with_tty_emitter(ColorConfig::Auto,
+                                                               None,
+                                                               true,
+                                                               false,
+                                                               codemap.clone());
 
     let cstore = Rc::new(CStore::new(token::get_ident_interner()));
-    let cstore_ = ::rustc_driver::cstore_to_cratestore(cstore.clone());
     let sess = session::build_session_(sessopts,
                                        Some(input_path.clone()),
-                                       span_diagnostic_handler,
-                                       cstore_);
+                                       diagnostic_handler,
+                                       codemap,
+                                       cstore.clone());
     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
 
     let mut cfg = config::build_configuration(&sess);
@@ -220,22 +223,22 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
         }
     }
     let data = Arc::new(Mutex::new(Vec::new()));
-    let emitter = diagnostic::EmitterWriter::new(box Sink(data.clone()), None);
+    let codemap = Rc::new(CodeMap::new());
+    let emitter = errors::emitter::EmitterWriter::new(box Sink(data.clone()),
+                                                      None,
+                                                      codemap.clone());
     let old = io::set_panic(box Sink(data.clone()));
     let _bomb = Bomb(data, old.unwrap_or(box io::stdout()));
 
     // Compile the code
-    let codemap = CodeMap::new();
-    let diagnostic_handler = diagnostic::Handler::with_emitter(true, box emitter);
-    let span_diagnostic_handler =
-        diagnostic::SpanHandler::new(diagnostic_handler, codemap);
+    let diagnostic_handler = errors::Handler::with_emitter(true, false, box emitter);
 
     let cstore = Rc::new(CStore::new(token::get_ident_interner()));
-    let cstore_ = ::rustc_driver::cstore_to_cratestore(cstore.clone());
     let sess = session::build_session_(sessopts,
                                        None,
-                                       span_diagnostic_handler,
-                                       cstore_);
+                                       diagnostic_handler,
+                                       codemap,
+                                       cstore.clone());
     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
 
     let outdir = TempDir::new("rustdoctest").ok().expect("rustdoc needs a tempdir");
@@ -316,6 +319,7 @@ pub fn maketest(s: &str, cratename: Option<&str>, dont_insert_main: bool,
     } else {
         prog.push_str("fn main() {\n    ");
         prog.push_str(&everything_else.replace("\n", "\n    "));
+        prog = prog.trim().into();
         prog.push_str("\n}");
     }
 
index 17291233e114aa3ceedb60551fdc80b5cc27e3af..ba389bc42b78cd30df66543a03e688ef923a9827 100644 (file)
@@ -38,7 +38,7 @@ use doctree::*;
 
 pub struct RustdocVisitor<'a, 'tcx: 'a> {
     pub module: Module,
-    pub attrs: Vec<ast::Attribute>,
+    pub attrs: hir::HirVec<ast::Attribute>,
     pub cx: &'a core::DocContext<'a, 'tcx>,
     pub analysis: Option<&'a core::CrateAnalysis>,
     view_item_stack: HashSet<ast::NodeId>,
@@ -53,7 +53,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         stack.insert(ast::CRATE_NODE_ID);
         RustdocVisitor {
             module: Module::new(None),
-            attrs: Vec::new(),
+            attrs: hir::HirVec::new(),
             cx: cx,
             analysis: analysis,
             view_item_stack: stack,
@@ -64,11 +64,18 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
     fn stability(&self, id: ast::NodeId) -> Option<attr::Stability> {
         self.cx.tcx_opt().and_then(|tcx| {
             self.cx.map.opt_local_def_id(id)
-                       .and_then(|def_id| stability::lookup(tcx, def_id))
+                       .and_then(|def_id| stability::lookup_stability(tcx, def_id))
                        .cloned()
         })
     }
 
+    fn deprecation(&self, id: ast::NodeId) -> Option<attr::Deprecation> {
+        self.cx.tcx_opt().and_then(|tcx| {
+            self.cx.map.opt_local_def_id(id)
+                       .and_then(|def_id| stability::lookup_deprecation(tcx, def_id))
+        })
+    }
+
     pub fn visit(&mut self, krate: &hir::Crate) {
         self.attrs = krate.attrs.clone();
 
@@ -95,6 +102,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             name: name,
             vis: item.vis,
             stab: self.stability(item.id),
+            depr: self.deprecation(item.id),
             attrs: item.attrs.clone(),
             generics: generics.clone(),
             fields: sd.fields().iter().cloned().collect(),
@@ -112,11 +120,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                 name: v.node.name,
                 attrs: v.node.attrs.clone(),
                 stab: self.stability(v.node.data.id()),
+                depr: self.deprecation(v.node.data.id()),
                 def: v.node.data.clone(),
                 whence: v.span,
             }).collect(),
             vis: it.vis,
             stab: self.stability(it.id),
+            depr: self.deprecation(it.id),
             generics: params.clone(),
             attrs: it.attrs.clone(),
             id: it.id,
@@ -135,6 +145,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             id: item.id,
             vis: item.vis,
             stab: self.stability(item.id),
+            depr: self.deprecation(item.id),
             attrs: item.attrs.clone(),
             decl: fd.clone(),
             name: name,
@@ -146,7 +157,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         }
     }
 
-    pub fn visit_mod_contents(&mut self, span: Span, attrs: Vec<ast::Attribute> ,
+    pub fn visit_mod_contents(&mut self, span: Span, attrs: hir::HirVec<ast::Attribute>,
                               vis: hir::Visibility, id: ast::NodeId,
                               m: &hir::Mod,
                               name: Option<ast::Name>) -> Module {
@@ -156,6 +167,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         om.attrs = attrs;
         om.vis = vis;
         om.stab = self.stability(id);
+        om.depr = self.deprecation(id);
         om.id = id;
         for i in &m.item_ids {
             let item = self.cx.map.expect_item(i.id);
@@ -180,7 +192,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                 let mine = paths.into_iter().filter(|path| {
                     !self.resolve_id(path.node.id(), None, false, om,
                                      please_inline)
-                }).collect::<Vec<hir::PathListItem>>();
+                }).collect::<hir::HirVec<hir::PathListItem>>();
 
                 if mine.is_empty() {
                     None
@@ -314,6 +326,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                     whence: item.span,
                     vis: item.vis,
                     stab: self.stability(item.id),
+                    depr: self.deprecation(item.id),
                 };
                 om.typedefs.push(t);
             },
@@ -328,6 +341,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                     whence: item.span,
                     vis: item.vis,
                     stab: self.stability(item.id),
+                    depr: self.deprecation(item.id),
                 };
                 om.statics.push(s);
             },
@@ -341,6 +355,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                     whence: item.span,
                     vis: item.vis,
                     stab: self.stability(item.id),
+                    depr: self.deprecation(item.id),
                 };
                 om.constants.push(s);
             },
@@ -356,6 +371,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                     whence: item.span,
                     vis: item.vis,
                     stab: self.stability(item.id),
+                    depr: self.deprecation(item.id),
                 };
                 om.traits.push(t);
             },
@@ -372,6 +388,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                     whence: item.span,
                     vis: item.vis,
                     stab: self.stability(item.id),
+                    depr: self.deprecation(item.id),
                 };
                 // Don't duplicate impls when inlining glob imports, we'll pick
                 // them up regardless of where they're located.
@@ -410,6 +427,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             whence: def.span,
             matchers: matchers,
             stab: self.stability(def.id),
+            depr: self.deprecation(def.id),
             imported_from: def.imported_from,
         }
     }
index cb949940b6d2bddfb687c4b10eb687173a54a9b5..804e1af19aba2816f8b42baf9f13a1822484f861 100644 (file)
 
 //! Implementations of serialization for structures found in libcollections
 
-use std::usize;
-use std::default::Default;
-use std::hash::Hash;
-use std::collections::hash_state::HashState;
+use std::hash::{Hash, BuildHasher};
+use std::mem;
 
 use {Decodable, Encodable, Decoder, Encoder};
 use std::collections::{LinkedList, VecDeque, BTreeMap, BTreeSet, HashMap, HashSet};
@@ -148,7 +146,7 @@ impl<
     fn decode<D: Decoder>(d: &mut D) -> Result<EnumSet<T>, D::Error> {
         let bits = try!(d.read_uint());
         let mut set = EnumSet::new();
-        for bit in 0..usize::BITS {
+        for bit in 0..(mem::size_of::<usize>()*8) {
             if bits & (1 << bit) != 0 {
                 set.insert(CLike::from_usize(1 << bit));
             }
@@ -160,7 +158,7 @@ impl<
 impl<K, V, S> Encodable for HashMap<K, V, S>
     where K: Encodable + Hash + Eq,
           V: Encodable,
-          S: HashState,
+          S: BuildHasher,
 {
     fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
         e.emit_map(self.len(), |e| {
@@ -178,12 +176,12 @@ impl<K, V, S> Encodable for HashMap<K, V, S>
 impl<K, V, S> Decodable for HashMap<K, V, S>
     where K: Decodable + Hash + Eq,
           V: Decodable,
-          S: HashState + Default,
+          S: BuildHasher + Default,
 {
     fn decode<D: Decoder>(d: &mut D) -> Result<HashMap<K, V, S>, D::Error> {
         d.read_map(|d, len| {
             let state = Default::default();
-            let mut map = HashMap::with_capacity_and_hash_state(len, state);
+            let mut map = HashMap::with_capacity_and_hasher(len, state);
             for i in 0..len {
                 let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
                 let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
@@ -196,7 +194,7 @@ impl<K, V, S> Decodable for HashMap<K, V, S>
 
 impl<T, S> Encodable for HashSet<T, S>
     where T: Encodable + Hash + Eq,
-          S: HashState,
+          S: BuildHasher,
 {
     fn encode<E: Encoder>(&self, s: &mut E) -> Result<(), E::Error> {
         s.emit_seq(self.len(), |s| {
@@ -212,12 +210,12 @@ impl<T, S> Encodable for HashSet<T, S>
 
 impl<T, S> Decodable for HashSet<T, S>
     where T: Decodable + Hash + Eq,
-          S: HashState + Default,
+          S: BuildHasher + Default,
 {
     fn decode<D: Decoder>(d: &mut D) -> Result<HashSet<T, S>, D::Error> {
         d.read_seq(|d, len| {
             let state = Default::default();
-            let mut set = HashSet::with_capacity_and_hash_state(len, state);
+            let mut set = HashSet::with_capacity_and_hasher(len, state);
             for i in 0..len {
                 set.insert(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
             }
index c919c335640050aec636ee8e1a7a549b50a15200..8bb596c8bb2485b95f579a659a92de484e76c64d 100644 (file)
 Core encoding and decoding interfaces.
 */
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "serialize"]
 #![unstable(feature = "rustc_private",
             reason = "deprecated in favor of rustc-serialize on crates.io",
             issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -32,8 +29,6 @@ Core encoding and decoding interfaces.
 #![feature(box_syntax)]
 #![feature(collections)]
 #![feature(enumset)]
-#![feature(hashmap_hasher)]
-#![feature(num_bits_bytes)]
 #![feature(rustc_private)]
 #![feature(staged_api)]
 #![feature(str_char)]
index ac21ae0f0aa146ca98e2e15a084b160281cfcb1c..9fae9af2d54d4c3a7be8a6c2533a48fb087ffb6f 100644 (file)
@@ -14,7 +14,6 @@ extern crate test;
 use prelude::v1::*;
 
 use self::test::Bencher;
-use iter::range_inclusive;
 
 #[bench]
 fn new_drop(b : &mut Bencher) {
@@ -43,7 +42,7 @@ fn grow_by_insertion(b: &mut Bencher) {
 
     let mut m = HashMap::new();
 
-    for i in range_inclusive(1, 1000) {
+    for i in 1..1001 {
         m.insert(i, i);
     }
 
@@ -61,12 +60,12 @@ fn find_existing(b: &mut Bencher) {
 
     let mut m = HashMap::new();
 
-    for i in range_inclusive(1, 1000) {
+    for i in 1..1001 {
         m.insert(i, i);
     }
 
     b.iter(|| {
-        for i in range_inclusive(1, 1000) {
+        for i in 1..1001 {
             m.contains_key(&i);
         }
     });
@@ -78,12 +77,12 @@ fn find_nonexisting(b: &mut Bencher) {
 
     let mut m = HashMap::new();
 
-    for i in range_inclusive(1, 1000) {
+    for i in 1..1001 {
         m.insert(i, i);
     }
 
     b.iter(|| {
-        for i in range_inclusive(1001, 2000) {
+        for i in 1001..2001 {
             m.contains_key(&i);
         }
     });
@@ -95,7 +94,7 @@ fn hashmap_as_queue(b: &mut Bencher) {
 
     let mut m = HashMap::new();
 
-    for i in range_inclusive(1, 1000) {
+    for i in 1..1001 {
         m.insert(i, i);
     }
 
@@ -114,7 +113,7 @@ fn get_remove_insert(b: &mut Bencher) {
 
     let mut m = HashMap::new();
 
-    for i in range_inclusive(1, 1000) {
+    for i in 1..1001 {
         m.insert(i, i);
     }
 
index 77c4149f9926ea8084bec0a2809860f323e245b3..509964cd29b888b988cc60083e9625ce40c449e2 100644 (file)
@@ -13,18 +13,13 @@ use self::SearchResult::*;
 use self::VacantEntryState::*;
 
 use borrow::Borrow;
-use clone::Clone;
-use cmp::{max, Eq, PartialEq};
-use default::Default;
+use cmp::max;
 use fmt::{self, Debug};
-use hash::{Hash, SipHasher};
-use iter::{self, Iterator, ExactSizeIterator, IntoIterator, FromIterator, Extend, Map};
-use marker::Sized;
+use hash::{Hash, SipHasher, BuildHasher};
+use iter::{self, Map, FromIterator};
 use mem::{self, replace};
-use ops::{Deref, FnMut, FnOnce, Index};
-use option::Option::{self, Some, None};
+use ops::{Deref, Index};
 use rand::{self, Rng};
-use result::Result;
 
 use super::table::{
     self,
@@ -40,7 +35,6 @@ use super::table::BucketState::{
     Empty,
     Full,
 };
-use super::state::HashState;
 
 const INITIAL_LOG2_CAP: usize = 5;
 const INITIAL_CAPACITY: usize = 1 << INITIAL_LOG2_CAP; // 2^5
@@ -308,7 +302,7 @@ fn test_resize_policy() {
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct HashMap<K, V, S = RandomState> {
     // All hashes are keyed on these values, to prevent hash collision attacks.
-    hash_state: S,
+    hash_builder: S,
 
     table: RawTable<K, V>,
 
@@ -456,10 +450,10 @@ impl<K, V, M> SearchResult<K, V, M> {
 }
 
 impl<K, V, S> HashMap<K, V, S>
-    where K: Eq + Hash, S: HashState
+    where K: Eq + Hash, S: BuildHasher
 {
     fn make_hash<X: ?Sized>(&self, x: &X) -> SafeHash where X: Hash {
-        table::make_hash(&self.hash_state, x)
+        table::make_hash(&self.hash_builder, x)
     }
 
     /// Search for a key, yielding the index if it's found in the hashtable.
@@ -529,40 +523,52 @@ impl<K: Hash + Eq, V> HashMap<K, V, RandomState> {
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn with_capacity(capacity: usize) -> HashMap<K, V, RandomState> {
-        HashMap::with_capacity_and_hash_state(capacity, Default::default())
+        HashMap::with_capacity_and_hasher(capacity, Default::default())
     }
 }
 
 impl<K, V, S> HashMap<K, V, S>
-    where K: Eq + Hash, S: HashState
+    where K: Eq + Hash, S: BuildHasher
 {
-    /// Creates an empty hashmap which will use the given hasher to hash keys.
+    /// Creates an empty hashmap which will use the given hash builder to hash
+    /// keys.
     ///
     /// The created map has the default initial capacity.
     ///
+    /// Warning: `hash_builder` is normally randomly generated, and
+    /// is designed to allow HashMaps to be resistant to attacks that
+    /// cause many collisions and very poor performance. Setting it
+    /// manually using this function can expose a DoS attack vector.
+    ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(hashmap_hasher)]
-    ///
     /// use std::collections::HashMap;
     /// use std::collections::hash_map::RandomState;
     ///
     /// let s = RandomState::new();
-    /// let mut map = HashMap::with_hash_state(s);
+    /// let mut map = HashMap::with_hasher(s);
     /// map.insert(1, 2);
     /// ```
     #[inline]
-    #[unstable(feature = "hashmap_hasher", reason = "hasher stuff is unclear",
-               issue = "27713")]
-    pub fn with_hash_state(hash_state: S) -> HashMap<K, V, S> {
+    #[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
+    pub fn with_hasher(hash_builder: S) -> HashMap<K, V, S> {
         HashMap {
-            hash_state:    hash_state,
+            hash_builder: hash_builder,
             resize_policy: DefaultResizePolicy::new(),
-            table:         RawTable::new(0),
+            table: RawTable::new(0),
         }
     }
 
+    /// Deprecated, renamed to `with_hasher`
+    #[inline]
+    #[unstable(feature = "hashmap_hasher", reason = "hasher stuff is unclear",
+               issue = "27713")]
+    #[rustc_deprecated(since = "1.7.0", reason = "renamed to with_hasher")]
+    pub fn with_hash_state(hash_state: S) -> HashMap<K, V, S> {
+        HashMap::with_hasher(hash_state)
+    }
+
     /// Creates an empty HashMap with space for at least `capacity`
     /// elements, using `hasher` to hash the keys.
     ///
@@ -574,31 +580,39 @@ impl<K, V, S> HashMap<K, V, S>
     /// # Examples
     ///
     /// ```
-    /// #![feature(hashmap_hasher)]
-    ///
     /// use std::collections::HashMap;
     /// use std::collections::hash_map::RandomState;
     ///
     /// let s = RandomState::new();
-    /// let mut map = HashMap::with_capacity_and_hash_state(10, s);
+    /// let mut map = HashMap::with_capacity_and_hasher(10, s);
     /// map.insert(1, 2);
     /// ```
     #[inline]
-    #[unstable(feature = "hashmap_hasher", reason = "hasher stuff is unclear",
-               issue = "27713")]
-    pub fn with_capacity_and_hash_state(capacity: usize, hash_state: S)
-                                        -> HashMap<K, V, S> {
+    #[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
+    pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S)
+                                    -> HashMap<K, V, S> {
         let resize_policy = DefaultResizePolicy::new();
         let min_cap = max(INITIAL_CAPACITY, resize_policy.min_capacity(capacity));
         let internal_cap = min_cap.checked_next_power_of_two().expect("capacity overflow");
         assert!(internal_cap >= capacity, "capacity overflow");
         HashMap {
-            hash_state:    hash_state,
+            hash_builder: hash_builder,
             resize_policy: resize_policy,
-            table:         RawTable::new(internal_cap),
+            table: RawTable::new(internal_cap),
         }
     }
 
+    /// Deprecated, renamed to `with_capacity_and_hasher`
+    #[inline]
+    #[unstable(feature = "hashmap_hasher", reason = "hasher stuff is unclear",
+               issue = "27713")]
+    #[rustc_deprecated(since = "1.7.0",
+                       reason = "renamed to with_capacity_and_hasher")]
+    pub fn with_capacity_and_hash_state(capacity: usize, hash_state: S)
+                                        -> HashMap<K, V, S> {
+        HashMap::with_capacity_and_hasher(capacity, hash_state)
+    }
+
     /// Returns the number of elements the map can hold without reallocating.
     ///
     /// This number is a lower bound; the `HashMap<K, V>` might be able to hold
@@ -1213,7 +1227,7 @@ fn search_entry_hashed<'a, K: Eq, V>(table: &'a mut RawTable<K,V>, hash: SafeHas
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<K, V, S> PartialEq for HashMap<K, V, S>
-    where K: Eq + Hash, V: PartialEq, S: HashState
+    where K: Eq + Hash, V: PartialEq, S: BuildHasher
 {
     fn eq(&self, other: &HashMap<K, V, S>) -> bool {
         if self.len() != other.len() { return false; }
@@ -1226,12 +1240,12 @@ impl<K, V, S> PartialEq for HashMap<K, V, S>
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<K, V, S> Eq for HashMap<K, V, S>
-    where K: Eq + Hash, V: Eq, S: HashState
+    where K: Eq + Hash, V: Eq, S: BuildHasher
 {}
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<K, V, S> Debug for HashMap<K, V, S>
-    where K: Eq + Hash + Debug, V: Debug, S: HashState
+    where K: Eq + Hash + Debug, V: Debug, S: BuildHasher
 {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.debug_map().entries(self.iter()).finish()
@@ -1241,10 +1255,10 @@ impl<K, V, S> Debug for HashMap<K, V, S>
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<K, V, S> Default for HashMap<K, V, S>
     where K: Eq + Hash,
-          S: HashState + Default,
+          S: BuildHasher + Default,
 {
     fn default() -> HashMap<K, V, S> {
-        HashMap::with_hash_state(Default::default())
+        HashMap::with_hasher(Default::default())
     }
 }
 
@@ -1252,7 +1266,7 @@ impl<K, V, S> Default for HashMap<K, V, S>
 impl<'a, K, Q: ?Sized, V, S> Index<&'a Q> for HashMap<K, V, S>
     where K: Eq + Hash + Borrow<Q>,
           Q: Eq + Hash,
-          S: HashState,
+          S: BuildHasher,
 {
     type Output = V;
 
@@ -1347,11 +1361,15 @@ pub struct VacantEntry<'a, K: 'a, V: 'a> {
 pub enum Entry<'a, K: 'a, V: 'a> {
     /// An occupied Entry.
     #[stable(feature = "rust1", since = "1.0.0")]
-    Occupied(OccupiedEntry<'a, K, V>),
+    Occupied(
+        #[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] OccupiedEntry<'a, K, V>
+    ),
 
     /// A vacant Entry.
     #[stable(feature = "rust1", since = "1.0.0")]
-    Vacant(VacantEntry<'a, K, V>),
+    Vacant(
+        #[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] VacantEntry<'a, K, V>
+    ),
 }
 
 /// Possible states of a VacantEntry.
@@ -1365,7 +1383,7 @@ enum VacantEntryState<K, V, M> {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S>
-    where K: Eq + Hash, S: HashState
+    where K: Eq + Hash, S: BuildHasher
 {
     type Item = (&'a K, &'a V);
     type IntoIter = Iter<'a, K, V>;
@@ -1377,7 +1395,7 @@ impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S>
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, K, V, S> IntoIterator for &'a mut HashMap<K, V, S>
-    where K: Eq + Hash, S: HashState
+    where K: Eq + Hash, S: BuildHasher
 {
     type Item = (&'a K, &'a mut V);
     type IntoIter = IterMut<'a, K, V>;
@@ -1389,7 +1407,7 @@ impl<'a, K, V, S> IntoIterator for &'a mut HashMap<K, V, S>
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<K, V, S> IntoIterator for HashMap<K, V, S>
-    where K: Eq + Hash, S: HashState
+    where K: Eq + Hash, S: BuildHasher
 {
     type Item = (K, V);
     type IntoIter = IntoIter<K, V>;
@@ -1568,13 +1586,12 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S>
-    where K: Eq + Hash, S: HashState + Default
+    where K: Eq + Hash, S: BuildHasher + Default
 {
     fn from_iter<T: IntoIterator<Item=(K, V)>>(iterable: T) -> HashMap<K, V, S> {
         let iter = iterable.into_iter();
         let lower = iter.size_hint().0;
-        let mut map = HashMap::with_capacity_and_hash_state(lower,
-                                                            Default::default());
+        let mut map = HashMap::with_capacity_and_hasher(lower, Default::default());
         map.extend(iter);
         map
     }
@@ -1582,7 +1599,7 @@ impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S>
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<K, V, S> Extend<(K, V)> for HashMap<K, V, S>
-    where K: Eq + Hash, S: HashState
+    where K: Eq + Hash, S: BuildHasher
 {
     fn extend<T: IntoIterator<Item=(K, V)>>(&mut self, iter: T) {
         for (k, v) in iter {
@@ -1593,7 +1610,7 @@ impl<K, V, S> Extend<(K, V)> for HashMap<K, V, S>
 
 #[stable(feature = "hash_extend_copy", since = "1.4.0")]
 impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap<K, V, S>
-    where K: Eq + Hash + Copy, V: Copy, S: HashState
+    where K: Eq + Hash + Copy, V: Copy, S: BuildHasher
 {
     fn extend<T: IntoIterator<Item=(&'a K, &'a V)>>(&mut self, iter: T) {
         self.extend(iter.into_iter().map(|(&key, &value)| (key, value)));
@@ -1606,34 +1623,28 @@ impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap<K, V, S>
 /// `Hasher`, but the hashers created by two different `RandomState`
 /// instances are unlikely to produce the same result for the same values.
 #[derive(Clone)]
-#[unstable(feature = "hashmap_hasher",
-           reason = "hashing an hash maps may be altered",
-           issue = "27713")]
+#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
 pub struct RandomState {
     k0: u64,
     k1: u64,
 }
 
-#[unstable(feature = "hashmap_hasher",
-           reason = "hashing an hash maps may be altered",
-           issue = "27713")]
 impl RandomState {
     /// Constructs a new `RandomState` that is initialized with random keys.
     #[inline]
     #[allow(deprecated)] // rand
+    #[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
     pub fn new() -> RandomState {
         let mut r = rand::thread_rng();
         RandomState { k0: r.gen(), k1: r.gen() }
     }
 }
 
-#[unstable(feature = "hashmap_hasher",
-           reason = "hashing an hash maps may be altered",
-           issue = "27713")]
-impl HashState for RandomState {
+#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
+impl BuildHasher for RandomState {
     type Hasher = SipHasher;
     #[inline]
-    fn hasher(&self) -> SipHasher {
+    fn build_hasher(&self) -> SipHasher {
         SipHasher::new_with_keys(self.k0, self.k1)
     }
 }
@@ -1647,7 +1658,7 @@ impl Default for RandomState {
 }
 
 impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S>
-    where K: Eq + Hash + Borrow<Q>, S: HashState, Q: Eq + Hash
+    where K: Eq + Hash + Borrow<Q>, S: BuildHasher, Q: Eq + Hash
 {
     type Key = K;
 
@@ -1681,7 +1692,6 @@ mod test_map {
 
     use super::HashMap;
     use super::Entry::{Occupied, Vacant};
-    use iter::range_inclusive;
     use cell::RefCell;
     use rand::{thread_rng, Rng};
 
@@ -1877,42 +1887,42 @@ mod test_map {
         for _ in 0..10 {
             assert!(m.is_empty());
 
-            for i in range_inclusive(1, 1000) {
+            for i in 1..1001 {
                 assert!(m.insert(i, i).is_none());
 
-                for j in range_inclusive(1, i) {
+                for j in 1..i+1 {
                     let r = m.get(&j);
                     assert_eq!(r, Some(&j));
                 }
 
-                for j in range_inclusive(i+1, 1000) {
+                for j in i+1..1001 {
                     let r = m.get(&j);
                     assert_eq!(r, None);
                 }
             }
 
-            for i in range_inclusive(1001, 2000) {
+            for i in 1001..2001 {
                 assert!(!m.contains_key(&i));
             }
 
             // remove forwards
-            for i in range_inclusive(1, 1000) {
+            for i in 1..1001 {
                 assert!(m.remove(&i).is_some());
 
-                for j in range_inclusive(1, i) {
+                for j in 1..i+1 {
                     assert!(!m.contains_key(&j));
                 }
 
-                for j in range_inclusive(i+1, 1000) {
+                for j in i+1..1001 {
                     assert!(m.contains_key(&j));
                 }
             }
 
-            for i in range_inclusive(1, 1000) {
+            for i in 1..1001 {
                 assert!(!m.contains_key(&i));
             }
 
-            for i in range_inclusive(1, 1000) {
+            for i in 1..1001 {
                 assert!(m.insert(i, i).is_none());
             }
 
@@ -1920,11 +1930,11 @@ mod test_map {
             for i in (1..1001).rev() {
                 assert!(m.remove(&i).is_some());
 
-                for j in range_inclusive(i, 1000) {
+                for j in i..1001 {
                     assert!(!m.contains_key(&j));
                 }
 
-                for j in range_inclusive(1, i-1) {
+                for j in 1..i {
                     assert!(m.contains_key(&j));
                 }
             }
index 08f356463bc965dd0cf82ceab9a767ba710a079f..b5f47853afd4ef0bebbcd254f2ee5949a47d8734 100644 (file)
@@ -9,20 +9,13 @@
 // except according to those terms.
 
 use borrow::Borrow;
-use clone::Clone;
-use cmp::{Eq, PartialEq};
-use core::marker::Sized;
-use default::Default;
-use fmt::Debug;
 use fmt;
-use hash::Hash;
-use iter::{Iterator, IntoIterator, ExactSizeIterator, FromIterator, Map, Chain, Extend};
+use hash::{Hash, BuildHasher};
+use iter::{Map, Chain, FromIterator};
 use ops::{BitOr, BitAnd, BitXor, Sub};
-use option::Option::{Some, None, self};
 
 use super::Recover;
 use super::map::{self, HashMap, Keys, RandomState};
-use super::state::HashState;
 
 const INITIAL_CAPACITY: usize = 32;
 
@@ -145,30 +138,32 @@ impl<T: Hash + Eq> HashSet<T, RandomState> {
 }
 
 impl<T, S> HashSet<T, S>
-    where T: Eq + Hash, S: HashState
+    where T: Eq + Hash, S: BuildHasher
 {
     /// Creates a new empty hash set which will use the given hasher to hash
     /// keys.
     ///
     /// The hash set is also created with the default initial capacity.
     ///
+    /// Warning: `hasher` is normally randomly generated, and
+    /// is designed to allow `HashSet`s to be resistant to attacks that
+    /// cause many collisions and very poor performance. Setting it
+    /// manually using this function can expose a DoS attack vector.
+    ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(hashmap_hasher)]
-    ///
     /// use std::collections::HashSet;
     /// use std::collections::hash_map::RandomState;
     ///
     /// let s = RandomState::new();
-    /// let mut set = HashSet::with_hash_state(s);
+    /// let mut set = HashSet::with_hasher(s);
     /// set.insert(2);
     /// ```
     #[inline]
-    #[unstable(feature = "hashmap_hasher", reason = "hasher stuff is unclear",
-               issue = "27713")]
-    pub fn with_hash_state(hash_state: S) -> HashSet<T, S> {
-        HashSet::with_capacity_and_hash_state(INITIAL_CAPACITY, hash_state)
+    #[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
+    pub fn with_hasher(hasher: S) -> HashSet<T, S> {
+        HashSet::with_capacity_and_hasher(INITIAL_CAPACITY, hasher)
     }
 
     /// Creates an empty HashSet with space for at least `capacity`
@@ -182,23 +177,40 @@ impl<T, S> HashSet<T, S>
     /// # Examples
     ///
     /// ```
-    /// #![feature(hashmap_hasher)]
-    ///
     /// use std::collections::HashSet;
     /// use std::collections::hash_map::RandomState;
     ///
     /// let s = RandomState::new();
-    /// let mut set = HashSet::with_capacity_and_hash_state(10, s);
+    /// let mut set = HashSet::with_capacity_and_hasher(10, s);
     /// set.insert(1);
     /// ```
     #[inline]
+    #[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
+    pub fn with_capacity_and_hasher(capacity: usize, hasher: S)
+                                    -> HashSet<T, S> {
+        HashSet {
+            map: HashMap::with_capacity_and_hasher(capacity, hasher),
+        }
+    }
+
+    /// Deprecated, renamed to `with_hasher`
+    #[inline]
     #[unstable(feature = "hashmap_hasher", reason = "hasher stuff is unclear",
                issue = "27713")]
+    #[rustc_deprecated(since = "1.7.0", reason = "renamed to with_hasher")]
+    pub fn with_hash_state(hash_state: S) -> HashSet<T, S> {
+        HashSet::with_hasher(hash_state)
+    }
+
+    /// Deprecated, renamed to `with_capacity_and_hasher`
+    #[inline]
+    #[unstable(feature = "hashmap_hasher", reason = "hasher stuff is unclear",
+               issue = "27713")]
+    #[rustc_deprecated(since = "1.7.0",
+                       reason = "renamed to with_capacity_and_hasher")]
     pub fn with_capacity_and_hash_state(capacity: usize, hash_state: S)
                                         -> HashSet<T, S> {
-        HashSet {
-            map: HashMap::with_capacity_and_hash_state(capacity, hash_state),
-        }
+        HashSet::with_capacity_and_hasher(capacity, hash_state)
     }
 
     /// Returns the number of elements the set can hold without reallocating.
@@ -605,7 +617,7 @@ impl<T, S> HashSet<T, S>
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T, S> PartialEq for HashSet<T, S>
-    where T: Eq + Hash, S: HashState
+    where T: Eq + Hash, S: BuildHasher
 {
     fn eq(&self, other: &HashSet<T, S>) -> bool {
         if self.len() != other.len() { return false; }
@@ -616,13 +628,13 @@ impl<T, S> PartialEq for HashSet<T, S>
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T, S> Eq for HashSet<T, S>
-    where T: Eq + Hash, S: HashState
+    where T: Eq + Hash, S: BuildHasher
 {}
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T, S> fmt::Debug for HashSet<T, S>
     where T: Eq + Hash + fmt::Debug,
-          S: HashState
+          S: BuildHasher
 {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.debug_set().entries(self.iter()).finish()
@@ -632,12 +644,12 @@ impl<T, S> fmt::Debug for HashSet<T, S>
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T, S> FromIterator<T> for HashSet<T, S>
     where T: Eq + Hash,
-          S: HashState + Default,
+          S: BuildHasher + Default,
 {
     fn from_iter<I: IntoIterator<Item=T>>(iterable: I) -> HashSet<T, S> {
         let iter = iterable.into_iter();
         let lower = iter.size_hint().0;
-        let mut set = HashSet::with_capacity_and_hash_state(lower, Default::default());
+        let mut set = HashSet::with_capacity_and_hasher(lower, Default::default());
         set.extend(iter);
         set
     }
@@ -646,7 +658,7 @@ impl<T, S> FromIterator<T> for HashSet<T, S>
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T, S> Extend<T> for HashSet<T, S>
     where T: Eq + Hash,
-          S: HashState,
+          S: BuildHasher,
 {
     fn extend<I: IntoIterator<Item=T>>(&mut self, iter: I) {
         for k in iter {
@@ -658,7 +670,7 @@ impl<T, S> Extend<T> for HashSet<T, S>
 #[stable(feature = "hash_extend_copy", since = "1.4.0")]
 impl<'a, T, S> Extend<&'a T> for HashSet<T, S>
     where T: 'a + Eq + Hash + Copy,
-          S: HashState,
+          S: BuildHasher,
 {
     fn extend<I: IntoIterator<Item=&'a T>>(&mut self, iter: I) {
         self.extend(iter.into_iter().cloned());
@@ -668,17 +680,17 @@ impl<'a, T, S> Extend<&'a T> for HashSet<T, S>
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T, S> Default for HashSet<T, S>
     where T: Eq + Hash,
-          S: HashState + Default,
+          S: BuildHasher + Default,
 {
     fn default() -> HashSet<T, S> {
-        HashSet::with_hash_state(Default::default())
+        HashSet::with_hasher(Default::default())
     }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, 'b, T, S> BitOr<&'b HashSet<T, S>> for &'a HashSet<T, S>
     where T: Eq + Hash + Clone,
-          S: HashState + Default,
+          S: BuildHasher + Default,
 {
     type Output = HashSet<T, S>;
 
@@ -710,7 +722,7 @@ impl<'a, 'b, T, S> BitOr<&'b HashSet<T, S>> for &'a HashSet<T, S>
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, 'b, T, S> BitAnd<&'b HashSet<T, S>> for &'a HashSet<T, S>
     where T: Eq + Hash + Clone,
-          S: HashState + Default,
+          S: BuildHasher + Default,
 {
     type Output = HashSet<T, S>;
 
@@ -742,7 +754,7 @@ impl<'a, 'b, T, S> BitAnd<&'b HashSet<T, S>> for &'a HashSet<T, S>
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, 'b, T, S> BitXor<&'b HashSet<T, S>> for &'a HashSet<T, S>
     where T: Eq + Hash + Clone,
-          S: HashState + Default,
+          S: BuildHasher + Default,
 {
     type Output = HashSet<T, S>;
 
@@ -774,7 +786,7 @@ impl<'a, 'b, T, S> BitXor<&'b HashSet<T, S>> for &'a HashSet<T, S>
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, 'b, T, S> Sub<&'b HashSet<T, S>> for &'a HashSet<T, S>
     where T: Eq + Hash + Clone,
-          S: HashState + Default,
+          S: BuildHasher + Default,
 {
     type Output = HashSet<T, S>;
 
@@ -853,7 +865,7 @@ pub struct Union<'a, T: 'a, S: 'a> {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, T, S> IntoIterator for &'a HashSet<T, S>
-    where T: Eq + Hash, S: HashState
+    where T: Eq + Hash, S: BuildHasher
 {
     type Item = &'a T;
     type IntoIter = Iter<'a, T>;
@@ -866,7 +878,7 @@ impl<'a, T, S> IntoIterator for &'a HashSet<T, S>
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T, S> IntoIterator for HashSet<T, S>
     where T: Eq + Hash,
-          S: HashState
+          S: BuildHasher
 {
     type Item = T;
     type IntoIter = IntoIter<T>;
@@ -948,7 +960,7 @@ impl<'a, T, S> Clone for Intersection<'a, T, S> {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, T, S> Iterator for Intersection<'a, T, S>
-    where T: Eq + Hash, S: HashState
+    where T: Eq + Hash, S: BuildHasher
 {
     type Item = &'a T;
 
@@ -978,7 +990,7 @@ impl<'a, T, S> Clone for Difference<'a, T, S> {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, T, S> Iterator for Difference<'a, T, S>
-    where T: Eq + Hash, S: HashState
+    where T: Eq + Hash, S: BuildHasher
 {
     type Item = &'a T;
 
@@ -1008,7 +1020,7 @@ impl<'a, T, S> Clone for SymmetricDifference<'a, T, S> {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, T, S> Iterator for SymmetricDifference<'a, T, S>
-    where T: Eq + Hash, S: HashState
+    where T: Eq + Hash, S: BuildHasher
 {
     type Item = &'a T;
 
@@ -1023,7 +1035,7 @@ impl<'a, T, S> Clone for Union<'a, T, S> {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, T, S> Iterator for Union<'a, T, S>
-    where T: Eq + Hash, S: HashState
+    where T: Eq + Hash, S: BuildHasher
 {
     type Item = &'a T;
 
index 1790eeb00b701d397d4258490d51d566d119137d..167aca083038ef948caeb368239c3620ddb81715 100644 (file)
 
 #![unstable(feature = "hashmap_hasher", reason = "hasher stuff is unclear",
             issue = "27713")]
+#![rustc_deprecated(since = "1.7.0", reason = "support moved to std::hash")]
+#![allow(deprecated)]
 
 use clone::Clone;
 use default::Default;
 use hash;
 use marker;
 
-/// A trait representing stateful hashes which can be used to hash keys in a
-/// `HashMap`.
-///
-/// A HashState is used as a factory for instances of `Hasher` which a `HashMap`
-/// can then use to hash keys independently. A `HashMap` by default uses a state
-/// which will create instances of a `SipHasher`, but a custom state factory can
-/// be provided to the `with_hash_state` function.
-///
-/// If a hashing algorithm has no initial state, then the `Hasher` type for that
-/// algorithm can implement the `Default` trait and create hash maps with the
-/// `DefaultState` structure. This state is 0-sized and will simply delegate
-/// to `Default` when asked to create a hasher.
-pub trait HashState {
-    /// Type of the hasher that will be created.
-    type Hasher: hash::Hasher;
-
-    /// Creates a new hasher based on the given state of this object.
-    fn hasher(&self) -> Self::Hasher;
-}
+pub use hash::HashState;
 
 /// A structure which is a factory for instances of `Hasher` which implement the
 /// default trait.
index e8796dd10b4f866cbf3b87e69bd23104430fe663..316c75952667c9e621e91da24e2bc40657c78bab 100644 (file)
 use alloc::heap::{allocate, deallocate, EMPTY};
 
 use cmp;
-use hash::{Hash, Hasher};
+use hash::{Hash, Hasher, BuildHasher};
 use marker;
 use mem::{align_of, size_of};
 use mem;
-use num::wrapping::OverflowingOps;
 use ops::{Deref, DerefMut};
 use ptr::{self, Unique};
-use collections::hash_state::HashState;
 
 use self::BucketState::*;
 
@@ -123,7 +121,7 @@ pub enum BucketState<K, V, M> {
 // A GapThenFull encapsulates the state of two consecutive buckets at once.
 // The first bucket, called the gap, is known to be empty.
 // The second bucket is full.
-struct GapThenFull<K, V, M> {
+pub struct GapThenFull<K, V, M> {
     gap: EmptyBucket<K, V, ()>,
     full: FullBucket<K, V, M>,
 }
@@ -145,9 +143,9 @@ impl SafeHash {
 /// 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: HashState
+    where T: Hash, S: BuildHasher
 {
-    let mut state = hash_state.hasher();
+    let mut state = hash_state.build_hasher();
     t.hash(&mut state);
     // We need to avoid 0 in order to prevent collisions with
     // EMPTY_HASH. We can maintain our precious uniform distribution
index 07ddfe237be93f91dcba8f71c5b7a60ffa737b72..417261cf4c304a3073aa624c773692b302e6ac1b 100644 (file)
@@ -444,6 +444,8 @@ pub mod hash_set {
 /// HashSet.
 #[unstable(feature = "hashmap_hasher", reason = "module was recently added",
            issue = "27713")]
+#[rustc_deprecated(since = "1.7.0", reason = "support moved to std::hash")]
+#[allow(deprecated)]
 pub mod hash_state {
     pub use super::hash::state::*;
 }
index 62ec23ccb20002624809c82d1153f6dd5bf8e504..41001153c3cc47e7de8c0036b9cc873a87e83435 100644 (file)
@@ -16,7 +16,7 @@
             reason = "API has not been scrutinized and is highly likely to \
                       either disappear or change",
             issue = "27810")]
-#![rustc_deprecated(since = "1.5.0", reason = "replaced with crates.io crates")]
+#![rustc_deprecated(since = "1.5.0", reason = "replaced with 'dylib' on crates.io")]
 #![allow(missing_docs)]
 #![allow(deprecated)]
 
@@ -132,6 +132,7 @@ mod tests {
     #[cfg_attr(any(windows,
                    target_os = "android",  // FIXME #10379
                    target_env = "musl"), ignore)]
+    #[allow(deprecated)]
     fn test_loading_cosine() {
         // The math library does not need to be loaded since it is already
         // statically linked in
@@ -164,6 +165,7 @@ mod tests {
               target_os = "bitrig",
               target_os = "netbsd",
               target_os = "openbsd"))]
+    #[allow(deprecated)]
     fn test_errors_do_not_crash() {
         // Open /dev/null as a library to get an error, and make sure
         // that only causes an error, and not a crash.
index 760733872ea190c8d2498e9f1f9f2641ac2ca6ac..db136190082b9b4bfed5c2c5ea3553dcbd9a2276 100644 (file)
@@ -218,7 +218,7 @@ pub enum VarError {
     /// valid unicode data. The found data is returned as a payload of this
     /// variant.
     #[stable(feature = "env", since = "1.0.0")]
-    NotUnicode(OsString),
+    NotUnicode(#[cfg_attr(not(stage0), stable(feature = "env", since = "1.0.0"))] OsString),
 }
 
 #[stable(feature = "env", since = "1.0.0")]
@@ -615,6 +615,8 @@ pub mod consts {
     /// - mips
     /// - mipsel
     /// - powerpc
+    /// - powerpc64
+    /// - powerpc64le
     #[stable(feature = "env", since = "1.0.0")]
     pub const ARCH: &'static str = super::arch::ARCH;
 
@@ -867,6 +869,16 @@ mod arch {
     pub const ARCH: &'static str = "powerpc";
 }
 
+#[cfg(target_arch = "powerpc64")]
+mod arch {
+    pub const ARCH: &'static str = "powerpc64";
+}
+
+#[cfg(target_arch = "powerpc64le")]
+mod arch {
+    pub const ARCH: &'static str = "powerpc64le";
+}
+
 #[cfg(target_arch = "le32")]
 mod arch {
     pub const ARCH: &'static str = "le32";
index 40fb450bea195425c04c2acb576a9d484fbc3a6c..d6aa746f4cb5333ed1eec65adf8dd0cb1872be13 100644 (file)
@@ -19,7 +19,8 @@ use io;
 use iter::Iterator;
 use libc;
 use mem;
-use ops::Deref;
+use memchr;
+use ops;
 use option::Option::{self, Some, None};
 use os::raw::c_char;
 use result::Result::{self, Ok, Err};
@@ -149,7 +150,7 @@ pub struct NulError(usize, Vec<u8>);
 /// An error returned from `CString::into_string` to indicate that a UTF-8 error
 /// was encountered during the conversion.
 #[derive(Clone, PartialEq, Debug)]
-#[unstable(feature = "cstring_into", reason = "recently added", issue = "29157")]
+#[stable(feature = "cstring_into", since = "1.7.0")]
 pub struct IntoStringError {
     inner: CString,
     error: Utf8Error,
@@ -188,7 +189,7 @@ impl CString {
     }
 
     fn _new(bytes: Vec<u8>) -> Result<CString, NulError> {
-        match bytes.iter().position(|x| *x == 0) {
+        match memchr::memchr(0, &bytes) {
             Some(i) => Err(NulError(i, bytes)),
             None => Ok(unsafe { CString::from_vec_unchecked(bytes) }),
         }
@@ -206,18 +207,6 @@ impl CString {
         CString { inner: v.into_boxed_slice() }
     }
 
-    /// Retakes ownership of a CString that was transferred to C.
-    ///
-    /// The only appropriate argument is a pointer obtained by calling
-    /// `into_raw`. The length of the string will be recalculated
-    /// using the pointer.
-    #[unstable(feature = "cstr_memory2", reason = "recently added",
-               issue = "27769")]
-    #[rustc_deprecated(since = "1.4.0", reason = "renamed to from_raw")]
-    pub unsafe fn from_ptr(ptr: *const c_char) -> CString {
-        CString::from_raw(ptr as *mut _)
-    }
-
     /// Retakes ownership of a CString that was transferred to C.
     ///
     /// The only appropriate argument is a pointer obtained by calling
@@ -230,21 +219,6 @@ impl CString {
         CString { inner: mem::transmute(slice) }
     }
 
-    /// Transfers ownership of the string to a C caller.
-    ///
-    /// The pointer must be returned to Rust and reconstituted using
-    /// `from_raw` to be properly deallocated. Specifically, one
-    /// should *not* use the standard C `free` function to deallocate
-    /// this string.
-    ///
-    /// Failure to call `from_raw` will lead to a memory leak.
-    #[unstable(feature = "cstr_memory2", reason = "recently added",
-               issue = "27769")]
-    #[rustc_deprecated(since = "1.4.0", reason = "renamed to into_raw")]
-    pub fn into_ptr(self) -> *const c_char {
-        self.into_raw() as *const _
-    }
-
     /// Transfers ownership of the string to a C caller.
     ///
     /// The pointer must be returned to Rust and reconstituted using
@@ -261,7 +235,7 @@ impl CString {
     /// Converts the `CString` into a `String` if it contains valid Unicode data.
     ///
     /// On failure, ownership of the original `CString` is returned.
-    #[unstable(feature = "cstring_into", reason = "recently added", issue = "29157")]
+    #[stable(feature = "cstring_into", since = "1.7.0")]
     pub fn into_string(self) -> Result<String, IntoStringError> {
         String::from_utf8(self.into_bytes())
             .map_err(|e| IntoStringError {
@@ -274,9 +248,8 @@ impl CString {
     ///
     /// The returned buffer does **not** contain the trailing nul separator and
     /// it is guaranteed to not have any interior nul bytes.
-    #[unstable(feature = "cstring_into", reason = "recently added", issue = "29157")]
+    #[stable(feature = "cstring_into", since = "1.7.0")]
     pub fn into_bytes(self) -> Vec<u8> {
-        // FIXME: Once this method becomes stable, add an `impl Into<Vec<u8>> for CString`
         let mut vec = self.inner.into_vec();
         let _nul = vec.pop();
         debug_assert_eq!(_nul, Some(0u8));
@@ -285,7 +258,7 @@ impl CString {
 
     /// Equivalent to the `into_bytes` function except that the returned vector
     /// includes the trailing nul byte.
-    #[unstable(feature = "cstring_into", reason = "recently added", issue = "29157")]
+    #[stable(feature = "cstring_into", since = "1.7.0")]
     pub fn into_bytes_with_nul(self) -> Vec<u8> {
         self.inner.into_vec()
     }
@@ -308,7 +281,7 @@ impl CString {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl Deref for CString {
+impl ops::Deref for CString {
     type Target = CStr;
 
     fn deref(&self) -> &CStr {
@@ -323,6 +296,13 @@ impl fmt::Debug for CString {
     }
 }
 
+#[stable(feature = "cstring_into", since = "1.7.0")]
+impl From<CString> for Vec<u8> {
+    fn from(s: CString) -> Vec<u8> {
+        s.into_bytes()
+    }
+}
+
 #[stable(feature = "cstr_debug", since = "1.3.0")]
 impl fmt::Debug for CStr {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -374,29 +354,33 @@ impl From<NulError> for io::Error {
 impl IntoStringError {
     /// Consumes this error, returning original `CString` which generated the
     /// error.
-    #[unstable(feature = "cstring_into", reason = "recently added", issue = "29157")]
+    #[stable(feature = "cstring_into", since = "1.7.0")]
     pub fn into_cstring(self) -> CString {
         self.inner
     }
 
     /// Access the underlying UTF-8 error that was the cause of this error.
-    #[unstable(feature = "cstring_into", reason = "recently added", issue = "29157")]
+    #[stable(feature = "cstring_into", since = "1.7.0")]
     pub fn utf8_error(&self) -> Utf8Error {
         self.error
     }
 }
 
-#[unstable(feature = "cstring_into", reason = "recently added", issue = "29157")]
+#[stable(feature = "cstring_into", since = "1.7.0")]
 impl Error for IntoStringError {
     fn description(&self) -> &str {
-        Error::description(&self.error)
+        "C string contained non-utf8 bytes"
+    }
+
+    fn cause(&self) -> Option<&Error> {
+        Some(&self.error)
     }
 }
 
-#[unstable(feature = "cstring_into", reason = "recently added", issue = "29157")]
+#[stable(feature = "cstring_into", since = "1.7.0")]
 impl fmt::Display for IntoStringError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.error, f)
+        self.description().fmt(f)
     }
 }
 
@@ -548,6 +532,37 @@ impl ToOwned for CStr {
     }
 }
 
+#[stable(feature = "cstring_asref", since = "1.7.0")]
+impl<'a> From<&'a CStr> for CString {
+    fn from(s: &'a CStr) -> CString {
+        s.to_owned()
+    }
+}
+
+#[stable(feature = "cstring_asref", since = "1.7.0")]
+impl ops::Index<ops::RangeFull> for CString {
+    type Output = CStr;
+
+    #[inline]
+    fn index(&self, _index: ops::RangeFull) -> &CStr {
+        self
+    }
+}
+
+#[stable(feature = "cstring_asref", since = "1.7.0")]
+impl AsRef<CStr> for CStr {
+    fn as_ref(&self) -> &CStr {
+        self
+    }
+}
+
+#[stable(feature = "cstring_asref", since = "1.7.0")]
+impl AsRef<CStr> for CString {
+    fn as_ref(&self) -> &CStr {
+        self
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use prelude::v1::*;
index 90b108e67707269638963d7ecc397245a7a15daa..eb5ddecbd054d090700585fc3c9ef971efeaf893 100644 (file)
@@ -8,27 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! A type that can represent all platform-native strings, but is cheaply
-//! interconvertable with Rust strings.
-//!
-//! The need for this type arises from the fact that:
-//!
-//! * On Unix systems, strings are often arbitrary sequences of non-zero
-//!   bytes, in many cases interpreted as UTF-8.
-//!
-//! * On Windows, strings are often arbitrary sequences of non-zero 16-bit
-//!   values, interpreted as UTF-16 when it is valid to do so.
-//!
-//! * In Rust, strings are always valid UTF-8, but may contain zeros.
-//!
-//! The types in this module bridge this gap by simultaneously representing Rust
-//! and platform-native string values, and in particular allowing a Rust string
-//! to be converted into an "OS" string with no cost.
-//!
-//! **Note**: At the moment, these types are extremely bare-bones, usable only
-//! for conversion to/from various other string types. Eventually these types
-//! will offer a full-fledged string API.
-
 use borrow::{Borrow, Cow, ToOwned};
 use ffi::CString;
 use fmt::{self, Debug};
@@ -42,14 +21,29 @@ use vec::Vec;
 use sys::os_str::{Buf, Slice};
 use sys_common::{AsInner, IntoInner, FromInner};
 
-/// Owned, mutable OS strings.
+/// A type that can represent owned, mutable platform-native strings, but is
+/// cheaply interconvertable with Rust strings.
+///
+/// The need for this type arises from the fact that:
+///
+/// * On Unix systems, strings are often arbitrary sequences of non-zero
+///   bytes, in many cases interpreted as UTF-8.
+///
+/// * On Windows, strings are often arbitrary sequences of non-zero 16-bit
+///   values, interpreted as UTF-16 when it is valid to do so.
+///
+/// * In Rust, strings are always valid UTF-8, but may contain zeros.
+///
+/// `OsString` and `OsStr` bridge this gap by simultaneously representing Rust
+/// and platform-native string values, and in particular allowing a Rust string
+/// to be converted into an "OS" string with no cost.
 #[derive(Clone)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct OsString {
     inner: Buf
 }
 
-/// Slices into OS strings.
+/// Slices into OS strings (see `OsString`).
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct OsStr {
     inner: Slice
index bfad224835932ef591f7054931cad74255e23884..635ed91f35da41c5335c36d4c5cd57e569de3d53 100644 (file)
@@ -715,7 +715,7 @@ impl DirEntry {
     /// This function will not traverse symlinks if this entry points at a
     /// symlink.
     ///
-    /// # Platform behavior
+    /// # Platform-specific behavior
     ///
     /// On Windows this function is cheap to call (no extra system calls
     /// needed), but on Unix platforms this function is the equivalent of
@@ -730,7 +730,7 @@ impl DirEntry {
     /// This function will not traverse symlinks if this entry points at a
     /// symlink.
     ///
-    /// # Platform behavior
+    /// # Platform-specific behavior
     ///
     /// On Windows and most Unix platforms this function is free (no extra
     /// system calls needed), but some Unix platforms may require the equivalent
@@ -758,11 +758,20 @@ impl AsInner<fs_imp::DirEntry> for DirEntry {
 /// guarantee that the file is immediately deleted (e.g. depending on
 /// platform, other open file descriptors may prevent immediate removal).
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `unlink` function on Unix
+/// and the `DeleteFile` function on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
 /// # Errors
 ///
-/// This function will return an error if `path` points to a directory, if the
-/// user lacks permissions to remove the file, or if some other filesystem-level
-/// error occurs.
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * `path` points to a directory.
+/// * The user lacks permissions to remove the file.
 ///
 /// # Examples
 ///
@@ -785,6 +794,21 @@ pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// This function will traverse symbolic links to query information about the
 /// destination file.
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `stat` function on Unix
+/// and the `GetFileAttributesEx` function on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
+/// # Errors
+///
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * The user lacks permissions to perform `metadata` call on `path`.
+/// * `path` does not exist.
+///
 /// # Examples
 ///
 /// ```rust
@@ -796,12 +820,6 @@ pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// # Ok(())
 /// # }
 /// ```
-///
-/// # Errors
-///
-/// This function will return an error if the user lacks the requisite
-/// permissions to perform a `metadata` call on the given `path` or if there
-/// is no entry in the filesystem at the provided path.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
     fs_imp::stat(path.as_ref()).map(Metadata)
@@ -809,6 +827,21 @@ pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
 
 /// Query the metadata about a file without following symlinks.
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `lstat` function on Unix
+/// and the `GetFileAttributesEx` function on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
+/// # Errors
+///
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * The user lacks permissions to perform `metadata` call on `path`.
+/// * `path` does not exist.
+///
 /// # Examples
 ///
 /// ```rust
@@ -829,12 +862,21 @@ pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
 ///
 /// This will not work if the new name is on a different mount point.
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `rename` function on Unix
+/// and the `MoveFileEx` function with the `MOVEFILE_REPLACE_EXISTING` flag on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
 /// # Errors
 ///
-/// This function will return an error if the provided `from` doesn't exist, if
-/// the process lacks permissions to view the contents, if `from` and `to`
-/// reside on separate filesystems, or if some other intermittent I/O error
-/// occurs.
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * `from` does not exist.
+/// * The user lacks permissions to view contents.
+/// * `from` and `to` are on separate filesystems.
 ///
 /// # Examples
 ///
@@ -842,7 +884,7 @@ pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
 /// use std::fs;
 ///
 /// # fn foo() -> std::io::Result<()> {
-/// try!(fs::rename("a.txt", "b.txt"));
+/// try!(fs::rename("a.txt", "b.txt")); // Rename a.txt to b.txt
 /// # Ok(())
 /// # }
 /// ```
@@ -861,15 +903,24 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
 ///
 /// On success, the total number of bytes copied is returned.
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `open` function in Unix
+/// with `O_RDONLY` for `from` and `O_WRONLY`, `O_CREAT`, and `O_TRUNC` for `to`.
+/// `O_CLOEXEC` is set for returned file descriptors.
+/// On Windows, this function currently corresponds to `CopyFileEx`.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
 /// # Errors
 ///
 /// This function will return an error in the following situations, but is not
 /// limited to just these cases:
 ///
-/// * The `from` path is not a file
-/// * The `from` file does not exist
+/// * The `from` path is not a file.
+/// * The `from` file does not exist.
 /// * The current process does not have the permission rights to access
-///   `from` or write `to`
+///   `from` or write `to`.
 ///
 /// # Examples
 ///
@@ -877,7 +928,7 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
 /// use std::fs;
 ///
 /// # fn foo() -> std::io::Result<()> {
-/// try!(fs::copy("foo.txt", "bar.txt"));
+/// try!(fs::copy("foo.txt", "bar.txt"));  // Copy foo.txt to bar.txt
 /// # Ok(()) }
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -890,13 +941,27 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
 /// The `dst` path will be a link pointing to the `src` path. Note that systems
 /// often require these two paths to both be located on the same filesystem.
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `link` function on Unix
+/// and the `CreateHardLink` function on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
+/// # Errors
+///
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * The `src` path is not a file or doesn't exist.
+///
 /// # Examples
 ///
 /// ```
 /// use std::fs;
 ///
 /// # fn foo() -> std::io::Result<()> {
-/// try!(fs::hard_link("a.txt", "b.txt"));
+/// try!(fs::hard_link("a.txt", "b.txt")); // Hard link a.txt to b.txt
 /// # Ok(())
 /// # }
 /// ```
@@ -923,21 +988,31 @@ pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<(
 /// # Ok(())
 /// # }
 /// ```
+#[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_deprecated(since = "1.1.0",
              reason = "replaced with std::os::unix::fs::symlink and \
                        std::os::windows::fs::{symlink_file, symlink_dir}")]
-#[stable(feature = "rust1", since = "1.0.0")]
 pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
     fs_imp::symlink(src.as_ref(), dst.as_ref())
 }
 
 /// Reads a symbolic link, returning the file that the link points to.
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `readlink` function on Unix
+/// and the `CreateFile` function with `FILE_FLAG_OPEN_REPARSE_POINT` and
+/// `FILE_FLAG_BACKUP_SEMANTICS` flags on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
 /// # Errors
 ///
-/// This function will return an error on failure. Failure conditions include
-/// reading a file that does not exist or reading a file that is not a symbolic
-/// link.
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * `path` is not a symbolic link.
+/// * `path` does not exist.
 ///
 /// # Examples
 ///
@@ -957,8 +1032,20 @@ pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
 /// Returns the canonical form of a path with all intermediate components
 /// normalized and symbolic links resolved.
 ///
-/// This function may return an error in situations like where the path does not
-/// exist, a component in the path is not a directory, or an I/O error happens.
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `realpath` function on Unix
+/// and the `CreateFile` and `GetFinalPathNameByHandle` functions on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
+/// # Errors
+///
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * `path` does not exist.
+/// * A component in path is not a directory.
 ///
 /// # Examples
 ///
@@ -977,10 +1064,20 @@ pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
 
 /// Creates a new, empty directory at the provided path
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `mkdir` function on Unix
+/// and the `CreateDirectory` function on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
 /// # Errors
 ///
-/// This function will return an error if the user lacks permissions to make a
-/// new directory at the provided `path`, or if the directory already exists.
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * User lacks permissions to create directory at `path`.
+/// * `path` already exists.
 ///
 /// # Examples
 ///
@@ -1000,9 +1097,19 @@ pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// Recursively create a directory and all of its parent components if they
 /// are missing.
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `mkdir` function on Unix
+/// and the `CreateDirectory` function on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
 /// # Errors
 ///
-/// This function will fail if any directory in the path specified by `path`
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * If any directory in the path specified by `path`
 /// does not already exist and it could not be created otherwise. The specific
 /// error conditions for when a directory is being created (after it is
 /// determined to not exist) are outlined by `fs::create_dir`.
@@ -1024,10 +1131,20 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
 
 /// Removes an existing, empty directory.
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `rmdir` function on Unix
+/// and the `RemoveDirectory` function on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
 /// # Errors
 ///
-/// This function will return an error if the user lacks permissions to remove
-/// the directory at the provided `path`, or if the directory isn't empty.
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * The user lacks permissions to remove the directory at the provided `path`.
+/// * The directory isn't empty.
 ///
 /// # Examples
 ///
@@ -1050,6 +1167,14 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// This function does **not** follow symbolic links and it will simply remove the
 /// symbolic link itself.
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to `opendir`, `lstat`, `rm` and `rmdir` functions on Unix
+/// and the `FindFirstFile`, `GetFileAttributesEx`, `DeleteFile`, and `RemoveDirectory` functions
+/// on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
 /// # Errors
 ///
 /// See `file::remove_file` and `fs::remove_dir`.
@@ -1087,6 +1212,22 @@ fn _remove_dir_all(path: &Path) -> io::Result<()> {
 /// The iterator will yield instances of `io::Result<DirEntry>`. New errors may
 /// be encountered after an iterator is initially constructed.
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `opendir` function on Unix
+/// and the `FindFirstFile` function on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
+/// # Errors
+///
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * The provided `path` doesn't exist.
+/// * The process lacks permissions to view the contents.
+/// * The `path` points at a non-directory file.
+///
 /// # Examples
 ///
 /// ```
@@ -1109,12 +1250,6 @@ fn _remove_dir_all(path: &Path) -> io::Result<()> {
 ///     Ok(())
 /// }
 /// ```
-///
-/// # Errors
-///
-/// This function will return an error if the provided `path` doesn't exist, if
-/// the process lacks permissions to view the contents or if the `path` points
-/// at a non-directory file
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
     fs_imp::readdir(path.as_ref()).map(ReadDir)
@@ -1178,87 +1313,23 @@ impl Iterator for WalkDir {
     }
 }
 
-/// Utility methods for paths.
-#[unstable(feature = "path_ext_deprecated",
-           reason = "The precise set of methods exposed on this trait may \
-                     change and some methods may be removed.  For stable code, \
-                     see the std::fs::metadata function.",
-           issue = "27725")]
-#[rustc_deprecated(since = "1.5.0", reason = "replaced with inherent methods")]
-pub trait PathExt {
-    /// Gets information on the file, directory, etc at this path.
-    ///
-    /// Consult the `fs::metadata` documentation for more info.
-    ///
-    /// This call preserves identical runtime/error semantics with
-    /// `fs::metadata`.
-    fn metadata(&self) -> io::Result<Metadata>;
-
-    /// Gets information on the file, directory, etc at this path.
-    ///
-    /// Consult the `fs::symlink_metadata` documentation for more info.
-    ///
-    /// This call preserves identical runtime/error semantics with
-    /// `fs::symlink_metadata`.
-    fn symlink_metadata(&self) -> io::Result<Metadata>;
-
-    /// Returns the canonical form of a path, normalizing all components and
-    /// eliminate all symlinks.
-    ///
-    /// This call preserves identical runtime/error semantics with
-    /// `fs::canonicalize`.
-    fn canonicalize(&self) -> io::Result<PathBuf>;
-
-    /// Reads the symlink at this path.
-    ///
-    /// For more information see `fs::read_link`.
-    fn read_link(&self) -> io::Result<PathBuf>;
-
-    /// Reads the directory at this path.
-    ///
-    /// For more information see `fs::read_dir`.
-    fn read_dir(&self) -> io::Result<ReadDir>;
-
-    /// Boolean value indicator whether the underlying file exists on the local
-    /// filesystem. Returns false in exactly the cases where `fs::metadata`
-    /// fails.
-    fn exists(&self) -> bool;
-
-    /// Whether the underlying implementation (be it a file path, or something
-    /// else) points at a "regular file" on the FS. Will return false for paths
-    /// to non-existent locations or directories or other non-regular files
-    /// (named pipes, etc). Follows links when making this determination.
-    fn is_file(&self) -> bool;
-
-    /// Whether the underlying implementation (be it a file path, or something
-    /// else) is pointing at a directory in the underlying FS. Will return
-    /// false for paths to non-existent locations or if the item is not a
-    /// directory (eg files, named pipes, etc). Follows links when making this
-    /// determination.
-    fn is_dir(&self) -> bool;
-}
-
-#[allow(deprecated)]
-#[unstable(feature = "path_ext_deprecated", issue = "27725")]
-impl PathExt for Path {
-    fn metadata(&self) -> io::Result<Metadata> { metadata(self) }
-    fn symlink_metadata(&self) -> io::Result<Metadata> { symlink_metadata(self) }
-    fn canonicalize(&self) -> io::Result<PathBuf> { canonicalize(self) }
-    fn read_link(&self) -> io::Result<PathBuf> { read_link(self) }
-    fn read_dir(&self) -> io::Result<ReadDir> { read_dir(self) }
-    fn exists(&self) -> bool { metadata(self).is_ok() }
-
-    fn is_file(&self) -> bool {
-        metadata(self).map(|s| s.is_file()).unwrap_or(false)
-    }
-
-    fn is_dir(&self) -> bool {
-        metadata(self).map(|s| s.is_dir()).unwrap_or(false)
-    }
-}
-
 /// Changes the permissions found on a file or a directory.
 ///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `chmod` function on Unix
+/// and the `SetFileAttributes` function on Windows.
+/// Note that, this [may change in the future][changes].
+/// [changes]: ../io/index.html#platform-specific-behavior
+///
+/// # Errors
+///
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * `path` does not exist.
+/// * The user lacks the permission to change attributes of the file.
+///
 /// # Examples
 ///
 /// ```
@@ -1271,12 +1342,6 @@ impl PathExt for Path {
 /// # Ok(())
 /// # }
 /// ```
-///
-/// # Errors
-///
-/// This function will return an error if the provided `path` doesn't exist, if
-/// the process lacks permissions to change the attributes of the file, or if
-/// some other I/O error is encountered.
 #[stable(feature = "set_permissions", since = "1.1.0")]
 pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions)
                                        -> io::Result<()> {
@@ -1360,7 +1425,6 @@ mod tests {
     use io::{ErrorKind, SeekFrom};
     use path::PathBuf;
     use path::Path as Path2;
-    use os;
     use rand::{self, StdRng, Rng};
     use str;
 
@@ -1489,8 +1553,8 @@ mod tests {
         let message = "ten-four";
         let mut read_mem = [0; 4];
         let set_cursor = 4 as u64;
-        let mut tell_pos_pre_read;
-        let mut tell_pos_post_read;
+        let tell_pos_pre_read;
+        let tell_pos_post_read;
         let tmpdir = tmpdir();
         let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt");
         {
index 90a79da34835443e7dbfc03b53f221207a014082..a9a79fe2c7733f3afc9332c2f99f8f62b4724791 100644 (file)
@@ -18,6 +18,7 @@ use cmp;
 use error;
 use fmt;
 use io::{self, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom};
+use memchr;
 
 /// The `BufReader` struct adds buffering to any reader.
 ///
@@ -746,7 +747,7 @@ impl<W: Write> LineWriter<W> {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<W: Write> Write for LineWriter<W> {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        match buf.iter().rposition(|b| *b == b'\n') {
+        match memchr::memrchr(b'\n', buf) {
             Some(i) => {
                 let n = try!(self.inner.write(&buf[..i + 1]));
                 if n != i + 1 { return Ok(n) }
@@ -771,26 +772,11 @@ impl<W: Write> fmt::Debug for LineWriter<W> where W: fmt::Debug {
     }
 }
 
-struct InternalBufWriter<W: Write>(BufWriter<W>);
-
-impl<W: Read + Write> InternalBufWriter<W> {
-    fn get_mut(&mut self) -> &mut BufWriter<W> {
-        let InternalBufWriter(ref mut w) = *self;
-        return w;
-    }
-}
-
-impl<W: Read + Write> Read for InternalBufWriter<W> {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        self.get_mut().inner.as_mut().unwrap().read(buf)
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use prelude::v1::*;
     use io::prelude::*;
-    use io::{self, BufReader, BufWriter, Cursor, LineWriter, SeekFrom};
+    use io::{self, BufReader, BufWriter, LineWriter, SeekFrom};
     use test;
 
     /// A dummy reader intended at testing short-reads propagation.
index de09451e7c0f61c9599268fd58fb0935ddcdca26..4573f46581969f767a2b4edbf528d3354b858aab 100644 (file)
@@ -252,10 +252,13 @@ impl Write for Cursor<Vec<u8>> {
 
         // Figure out what bytes will be used to overwrite what's currently
         // there (left), and what will be appended on the end (right)
-        let space = self.inner.len() - pos as usize;
-        let (left, right) = buf.split_at(cmp::min(space, buf.len()));
-        self.inner[(pos as usize)..].clone_from_slice(left);
-        self.inner.extend_from_slice(right);
+        {
+            let pos = pos as usize;
+            let space = self.inner.len() - pos;
+            let (left, right) = buf.split_at(cmp::min(space, buf.len()));
+            self.inner[pos..pos + left.len()].clone_from_slice(left);
+            self.inner.extend_from_slice(right);
+        }
 
         // Bump us forward
         self.set_position(pos + buf.len() as u64);
index 1ff8f572a7f571cf179e9a9b3412b74d7a624a13..e3f17c839f135be9c4ef4c26848f9bde3d89a071 100644 (file)
@@ -311,9 +311,31 @@ impl fmt::Display for Error {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl error::Error for Error {
+    #[allow(deprecated)] // remove with UnexpectedEOF
     fn description(&self) -> &str {
         match self.repr {
-            Repr::Os(..) => "os error",
+            Repr::Os(..) => match self.kind() {
+                ErrorKind::NotFound => "entity not found",
+                ErrorKind::PermissionDenied => "permission denied",
+                ErrorKind::ConnectionRefused => "connection refused",
+                ErrorKind::ConnectionReset => "connection reset",
+                ErrorKind::ConnectionAborted => "connection aborted",
+                ErrorKind::NotConnected => "not connected",
+                ErrorKind::AddrInUse => "address in use",
+                ErrorKind::AddrNotAvailable => "address not available",
+                ErrorKind::BrokenPipe => "broken pipe",
+                ErrorKind::AlreadyExists => "entity already exists",
+                ErrorKind::WouldBlock => "operation would block",
+                ErrorKind::InvalidInput => "invalid input parameter",
+                ErrorKind::InvalidData => "invalid data",
+                ErrorKind::TimedOut => "timed out",
+                ErrorKind::WriteZero => "write zero",
+                ErrorKind::Interrupted => "operation interrupted",
+                ErrorKind::Other => "other os error",
+                ErrorKind::UnexpectedEOF => "unexpected end of file",
+                ErrorKind::UnexpectedEof => "unexpected end of file",
+                ErrorKind::__Nonexhaustive => unreachable!()
+            },
             Repr::Custom(ref c) => c.error.description(),
         }
     }
@@ -355,7 +377,7 @@ mod test {
         struct TestError;
 
         impl fmt::Display for TestError {
-            fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+            fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
                 Ok(())
             }
         }
index 95f630c965898c90c3f33fc7256f28b40815cc0f..592e16b0a3c090f54d456276864d5c0ee646c164 100644 (file)
@@ -156,7 +156,7 @@ impl<'a> Read for &'a [u8] {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         let amt = cmp::min(buf.len(), self.len());
         let (a, b) = self.split_at(amt);
-        buf.clone_from_slice(a);
+        buf[..amt].clone_from_slice(a);
         *self = b;
         Ok(amt)
     }
index efe40cf07c130981514329179759805502c9a57e..abb47b694184c2b1ee3c8901bfe5d62df1619fd8 100644 (file)
 //! ```
 //!
 //! `BufWriter` doesn't add any new ways of writing; it just buffers every call
-//! to [`write()`][write]:
+//! to [`write()`][write()]:
 //!
 //! ```
 //! use std::io;
 //! # }
 //! ```
 //!
-//! [write]: trait.Write.html#tymethod.write
+//! [write()]: trait.Write.html#tymethod.write
 //!
 //! ## Standard input and output
 //!
 //! to read the line and print it, so we use `()`.
 //!
 //! [result]: type.Result.html
-//! [try]: macro.try!.html
+//! [try]: ../macro.try!.html
+//!
+//! ## Platform-specific behavior
+//!
+//! Many I/O functions throughout the standard library are documented to indicate
+//! what various library or syscalls they are delegated to. This is done to help
+//! applications both understand what's happening under the hood as well as investigate
+//! any possibly unclear semantics. Note, however, that this is informative, not a binding
+//! contract. The implementation of many of these functions are subject to change over
+//! time and may call fewer or more syscalls/library functions.
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
@@ -254,6 +263,7 @@ use result;
 use string::String;
 use str;
 use vec::Vec;
+use memchr;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::buffered::{BufReader, BufWriter, LineWriter};
@@ -389,7 +399,7 @@ fn read_to_end<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<usize>
 ///
 /// [`File`][file]s implement `Read`:
 ///
-/// [file]: ../std/fs/struct.File.html
+/// [file]: ../fs/struct.File.html
 ///
 /// ```
 /// use std::io;
@@ -449,7 +459,7 @@ pub trait Read {
     ///
     /// [`File`][file]s implement `Read`:
     ///
-    /// [file]: ../std/fs/struct.File.html
+    /// [file]: ../fs/struct.File.html
     ///
     /// ```
     /// use std::io;
@@ -491,7 +501,7 @@ pub trait Read {
     ///
     /// [`File`][file]s implement `Read`:
     ///
-    /// [file]: ../std/fs/struct.File.html
+    /// [file]: ../fs/struct.File.html
     ///
     /// ```
     /// use std::io;
@@ -530,7 +540,7 @@ pub trait Read {
     ///
     /// [`File`][file]s implement `Read`:
     ///
-    /// [file]: ../std/fs/struct.File.html
+    /// [file]: ../fs/struct.File.html
     ///
     /// ```
     /// use std::io;
@@ -590,7 +600,7 @@ pub trait Read {
     ///
     /// [`File`][file]s implement `Read`:
     ///
-    /// [file]: ../std/fs/struct.File.html
+    /// [file]: ../fs/struct.File.html
     ///
     /// ```
     /// use std::io;
@@ -633,7 +643,7 @@ pub trait Read {
     ///
     /// [`File`][file]s implement `Read`:
     ///
-    /// [file]: ../std/fs/struct.File.html
+    /// [file]: ../fs/struct.File.html
     ///
     /// ```
     /// use std::io;
@@ -672,7 +682,7 @@ pub trait Read {
     ///
     /// [`File`][file]s implement `Read`:
     ///
-    /// [file]: ../std/fs/struct.File.html
+    /// [file]: ../fs/struct.File.html
     ///
     /// ```
     /// use std::io;
@@ -708,7 +718,7 @@ pub trait Read {
     ///
     /// [`File`][file]s implement `Read`:
     ///
-    /// [file]: ../std/fs/struct.File.html
+    /// [file]: ../fs/struct.File.html
     ///
     /// ```
     /// #![feature(io)]
@@ -743,7 +753,7 @@ pub trait Read {
     ///
     /// [`File`][file]s implement `Read`:
     ///
-    /// [file]: ../std/fs/struct.File.html
+    /// [file]: ../fs/struct.File.html
     ///
     /// ```
     /// use std::io;
@@ -779,7 +789,7 @@ pub trait Read {
     ///
     /// [`File`][file]s implement `Read`:
     ///
-    /// [file]: ../std/fs/struct.File.html
+    /// [file]: ../fs/struct.File.html
     ///
     /// ```
     /// use std::io;
@@ -813,7 +823,7 @@ pub trait Read {
     ///
     /// [`File`][file]s implement `Read`:
     ///
-    /// [file]: ../std/fs/struct.File.html
+    /// [file]: ../fs/struct.File.html
     ///
     /// ```
     /// #![feature(io)]
@@ -991,8 +1001,8 @@ pub trait Write {
     /// explicitly be called. The [`write!`][write] macro should be favored to
     /// invoke this method instead.
     ///
-    /// [formatargs]: ../std/macro.format_args!.html
-    /// [write]: ../std/macro.write!.html
+    /// [formatargs]: ../macro.format_args!.html
+    /// [write]: ../macro.write!.html
     ///
     /// This function internally uses the [`write_all`][writeall] method on
     /// this trait and hence will continuously write data so long as no errors
@@ -1125,7 +1135,7 @@ pub trait Write {
 ///
 /// [`File`][file]s implement `Seek`:
 ///
-/// [file]: ../std/fs/struct.File.html
+/// [file]: ../fs/struct.File.html
 ///
 /// ```
 /// use std::io;
@@ -1165,23 +1175,23 @@ pub trait Seek {
 pub enum SeekFrom {
     /// Set the offset to the provided number of bytes.
     #[stable(feature = "rust1", since = "1.0.0")]
-    Start(u64),
+    Start(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] u64),
 
     /// Set the offset to the size of this object plus the specified number of
     /// bytes.
     ///
-    /// It is possible to seek beyond the end of an object, but is an error to
+    /// It is possible to seek beyond the end of an object, but it's an error to
     /// seek before byte 0.
     #[stable(feature = "rust1", since = "1.0.0")]
-    End(i64),
+    End(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] i64),
 
     /// Set the offset to the current position plus the specified number of
     /// bytes.
     ///
-    /// It is possible to seek beyond the end of an object, but is an error to
+    /// It is possible to seek beyond the end of an object, but it's an error to
     /// seek before byte 0.
     #[stable(feature = "rust1", since = "1.0.0")]
-    Current(i64),
+    Current(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] i64),
 }
 
 fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>)
@@ -1194,7 +1204,7 @@ fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>)
                 Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
                 Err(e) => return Err(e)
             };
-            match available.iter().position(|x| *x == delim) {
+            match memchr::memchr(delim, available) {
                 Some(i) => {
                     buf.extend_from_slice(&available[..i + 1]);
                     (true, i + 1)
@@ -1982,7 +1992,7 @@ mod tests {
         b.iter(|| {
             let mut lr = repeat(1).take(10000000);
             let mut vec = Vec::with_capacity(1024);
-            super::read_to_end(&mut lr, &mut vec);
+            super::read_to_end(&mut lr, &mut vec)
         });
     }
 }
index f588ec60589e915d1332d970221d205574725b16..8772d0f5b099bef112772371d9cb0dd0f3f28353 100644 (file)
@@ -22,6 +22,3 @@
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use super::{Read, Write, BufRead, Seek};
-#[allow(deprecated)]
-#[unstable(feature = "path_ext_deprecated", issue = "27725")]
-pub use fs::PathExt;
index 985dbdd895f847b1da6d464534fa82f47af0bd1c..79091fd3d6b9d07bd227f935d4d541d2dec67e25 100644 (file)
@@ -133,14 +133,17 @@ fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> {
 /// A handle to the standard input stream of a process.
 ///
 /// Each handle is a shared reference to a global buffer of input data to this
-/// process. A handle can be `lock`'d to gain full access to `BufRead` methods
+/// process. A handle can be `lock`'d to gain full access to [`BufRead`] methods
 /// (e.g. `.lines()`). Writes to this handle are otherwise locked with respect
 /// to other writes.
 ///
 /// This handle implements the `Read` trait, but beware that concurrent reads
 /// of `Stdin` must be executed with care.
 ///
-/// Created by the function `io::stdin()`.
+/// Created by the [`io::stdin`] method.
+///
+/// [`io::stdin`]: fn.stdin.html
+/// [`BufRead`]: trait.BufRead.html
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Stdin {
     inner: Arc<Mutex<BufReader<Maybe<StdinRaw>>>>,
@@ -148,8 +151,12 @@ pub struct Stdin {
 
 /// A locked reference to the `Stdin` handle.
 ///
-/// This handle implements both the `Read` and `BufRead` traits and is
-/// constructed via the `lock` method on `Stdin`.
+/// This handle implements both the [`Read`] and [`BufRead`] traits, and
+/// is constructed via the [`Stdin::lock`] method.
+///
+/// [`Read`]: trait.Read.html
+/// [`BufRead`]: trait.BufRead.html
+/// [`Stdin::lock`]: struct.Stdin.html#method.lock
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct StdinLock<'a> {
     inner: MutexGuard<'a, BufReader<Maybe<StdinRaw>>>,
@@ -159,7 +166,7 @@ pub struct StdinLock<'a> {
 ///
 /// Each handle returned is a reference to a shared global buffer whose access
 /// is synchronized via a mutex. If you need more explicit control over
-/// locking, see the [lock() method][lock].
+/// locking, see the [`lock() method`][lock].
 ///
 /// [lock]: struct.Stdin.html#method.lock
 ///
@@ -221,8 +228,11 @@ impl Stdin {
     /// guard.
     ///
     /// The lock is released when the returned lock goes out of scope. The
-    /// returned guard also implements the `Read` and `BufRead` traits for
+    /// returned guard also implements the [`Read`] and [`BufRead`] traits for
     /// accessing the underlying data.
+    ///
+    /// [`Read`]: trait.Read.html
+    /// [`BufRead`]: trait.BufRead.html
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn lock(&self) -> StdinLock {
         StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) }
@@ -231,7 +241,9 @@ impl Stdin {
     /// Locks this handle and reads a line of input into the specified buffer.
     ///
     /// For detailed semantics of this method, see the documentation on
-    /// `BufRead::read_line`.
+    /// [`BufRead::read_line`].
+    ///
+    /// [`BufRead::read_line`]: trait.BufRead.html#method.read_line
     ///
     /// # Examples
     ///
@@ -252,7 +264,7 @@ impl Stdin {
     ///
     /// - Pipe some text to it, e.g. `printf foo | path/to/executable`
     /// - Give it text interactively by running the executable directly,
-    //    in which case it will wait for the Enter key to be pressed before
+    ///   in which case it will wait for the Enter key to be pressed before
     ///   continuing
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn read_line(&self, buf: &mut String) -> io::Result<usize> {
@@ -314,7 +326,9 @@ const OUT_MAX: usize = ::usize::MAX;
 /// output stream. Access is also synchronized via a lock and explicit control
 /// over locking is available via the `lock` method.
 ///
-/// Created by the function `io::stdout()`.
+/// Created by the [`io::stdout`] method.
+///
+/// [`io::stdout`]: fn.stdout.html
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Stdout {
     // FIXME: this should be LineWriter or BufWriter depending on the state of
@@ -325,8 +339,11 @@ pub struct Stdout {
 
 /// A locked reference to the `Stdout` handle.
 ///
-/// This handle implements the `Write` trait and is constructed via the `lock`
-/// method on `Stdout`.
+/// This handle implements the [`Write`] trait, and is constructed via
+/// the [`Stdout::lock`] method.
+///
+/// [`Write`]: trait.Write.html
+/// [`Stdout::lock`]: struct.Stdout.html#method.lock
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct StdoutLock<'a> {
     inner: ReentrantMutexGuard<'a, RefCell<LineWriter<Maybe<StdoutRaw>>>>,
@@ -336,9 +353,9 @@ pub struct StdoutLock<'a> {
 ///
 /// Each handle returned is a reference to a shared global buffer whose access
 /// is synchronized via a mutex. If you need more explicit control over
-/// locking, see the [lock() method][lock].
+/// locking, see the [Stdout::lock] method.
 ///
-/// [lock]: struct.Stdout.html#method.lock
+/// [Stdout::lock]: struct.Stdout.html#method.lock
 ///
 /// # Examples
 ///
@@ -424,7 +441,9 @@ impl<'a> Write for StdoutLock<'a> {
 
 /// A handle to the standard error stream of a process.
 ///
-/// For more information, see `stderr`
+/// For more information, see the [`io::stderr`] method.
+///
+/// [`io::stderr`]: fn.stderr.html
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Stderr {
     inner: Arc<ReentrantMutex<RefCell<Maybe<StderrRaw>>>>,
@@ -432,8 +451,10 @@ pub struct Stderr {
 
 /// A locked reference to the `Stderr` handle.
 ///
-/// This handle implements the `Write` trait and is constructed via the `lock`
-/// method on `Stderr`.
+/// This handle implements the `Write` trait and is constructed via
+/// the [`Stderr::lock`] method.
+///
+/// [`Stderr::lock`]: struct.Stderr.html#method.lock
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct StderrLock<'a> {
     inner: ReentrantMutexGuard<'a, RefCell<Maybe<StderrRaw>>>,
index 07a9548224aca8c8508166c03eddd896ce8a8e72..e05a0d577ff895b6c366c5c7f8d577c7e56d8e8b 100644 (file)
@@ -199,6 +199,7 @@ mod tests {
     }
 
     #[test]
+    #[allow(deprecated)]
     fn tee() {
         let mut buf = [0; 10];
         {
@@ -209,6 +210,7 @@ mod tests {
     }
 
     #[test]
+    #[allow(deprecated)]
     fn broadcast() {
         let mut buf1 = [0; 10];
         let mut buf2 = [0; 10];
index 0385dff65d14aef6bf33b5ca5952707169dd96cd..e7bcdcc785f22a35c3fcccfc14d63ec5030282e3 100644 (file)
@@ -25,7 +25,7 @@
 //!
 //! # How to read this documentation
 //!
-//! If you already know the name of what you are looking for the fastest way to
+//! If you already know the name of what you are looking for, the fastest way to
 //! find it is to use the <a href="#" onclick="focusSearchBar();">search
 //! bar</a> at the top of the page.
 //!
 //! [`mpsc`], which contains the channel types for message passing.
 //!
 //! [I/O]: io/index.html
-//! [MIN]: i32/constant.MIN.html
+//! [`MIN`]: i32/constant.MIN.html
 //! [TCP]: net/struct.TcpStream.html
 //! [The Rust Prelude]: prelude/index.html
 //! [UDP]: net/struct.UdpSocket.html
 //! [other]: #what-is-in-the-standard-library-documentation
 //! [primitive types]: ../book/primitive-types.html
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "std"]
 #![stable(feature = "rust1", since = "1.0.0")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
        test(no_crate_inject, attr(deny(warnings))),
        test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))))]
 
-#![cfg_attr(stage0, allow(unused_attributes))]
-#![cfg_attr(stage0, allow(improper_ctypes))]
-
-#![cfg_attr(stage0, feature(rustc_attrs))]
-#![cfg_attr(stage0, feature(no_std))]
-#![cfg_attr(stage0, allow(unused_attributes))]
 #![feature(alloc)]
 #![feature(allow_internal_unstable)]
 #![feature(asm)]
 #![feature(borrow_state)]
 #![feature(box_syntax)]
 #![feature(cfg_target_vendor)]
+#![feature(cfg_target_thread_local)]
 #![feature(char_internals)]
-#![feature(clone_from_slice)]
 #![feature(collections)]
 #![feature(collections_bound)]
 #![feature(const_fn)]
 #![feature(core_float)]
 #![feature(core_intrinsics)]
-#![feature(core_simd)]
 #![feature(decode_utf16)]
 #![feature(drop_in_place)]
 #![feature(dropck_parametricity)]
 #![feature(float_from_str_radix)]
 #![feature(fnbox)]
 #![feature(heap_api)]
+#![feature(hashmap_hasher)]
 #![feature(int_error_internals)]
 #![feature(into_cow)]
 #![feature(lang_items)]
 #![feature(link_args)]
 #![feature(linkage)]
 #![feature(macro_reexport)]
+#![feature(num_bits_bytes)]
+#![feature(old_wrapping)]
+#![feature(on_unimplemented)]
 #![feature(oom)]
 #![feature(optin_builtin_traits)]
 #![feature(placement_in_syntax)]
 #![feature(rand)]
 #![feature(range_inclusive)]
 #![feature(raw)]
+#![feature(repr_simd)]
 #![feature(reflect_marker)]
+#![feature(shared)]
 #![feature(slice_bytes)]
 #![feature(slice_concat_ext)]
 #![feature(slice_patterns)]
 #![feature(staged_api)]
+#![feature(stmt_expr_attributes)]
 #![feature(str_char)]
 #![feature(str_internals)]
 #![feature(str_utf16)]
 #![feature(unsafe_no_drop_flag, filling_drop)]
 #![feature(unwind_attributes)]
 #![feature(vec_push_all)]
-#![feature(wrapping)]
 #![feature(zero_one)]
 
 // Don't link to std. We are std.
@@ -332,9 +328,6 @@ pub use core::ptr;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::raw;
 #[stable(feature = "rust1", since = "1.0.0")]
-#[allow(deprecated)]
-pub use core::simd;
-#[stable(feature = "rust1", since = "1.0.0")]
 pub use core::result;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::option;
@@ -424,10 +417,12 @@ pub mod fs;
 pub mod io;
 pub mod net;
 pub mod os;
+pub mod panic;
 pub mod path;
 pub mod process;
 pub mod sync;
 pub mod time;
+mod memchr;
 
 #[macro_use]
 #[path = "sys/common/mod.rs"] mod sys_common;
diff --git a/src/libstd/memchr.rs b/src/libstd/memchr.rs
new file mode 100644 (file)
index 0000000..c043b41
--- /dev/null
@@ -0,0 +1,388 @@
+// 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.
+//
+// Original implementation taken from rust-memchr
+// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
+
+
+
+/// A safe interface to `memchr`.
+///
+/// Returns the index corresponding to the first occurrence of `needle` in
+/// `haystack`, or `None` if one is not found.
+///
+/// memchr reduces to super-optimized machine code at around an order of
+/// magnitude faster than `haystack.iter().position(|&b| b == needle)`.
+/// (See benchmarks.)
+///
+/// # Example
+///
+/// This shows how to find the first position of a byte in a byte string.
+///
+/// ```rust,ignore
+/// use memchr::memchr;
+///
+/// let haystack = b"the quick brown fox";
+/// assert_eq!(memchr(b'k', haystack), Some(8));
+/// ```
+pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+    // libc memchr
+    #[cfg(not(target_os = "windows"))]
+    fn memchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
+        use libc;
+
+        let p = unsafe {
+            libc::memchr(
+                haystack.as_ptr() as *const libc::c_void,
+                needle as libc::c_int,
+                haystack.len() as libc::size_t)
+        };
+        if p.is_null() {
+            None
+        } else {
+            Some(p as usize - (haystack.as_ptr() as usize))
+        }
+    }
+
+    // use fallback on windows, since it's faster
+    #[cfg(target_os = "windows")]
+    fn memchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
+        fallback::memchr(needle, haystack)
+    }
+
+    memchr_specific(needle, haystack)
+}
+
+/// A safe interface to `memrchr`.
+///
+/// Returns the index corresponding to the last occurrence of `needle` in
+/// `haystack`, or `None` if one is not found.
+///
+/// # Example
+///
+/// This shows how to find the last position of a byte in a byte string.
+///
+/// ```rust,ignore
+/// use memchr::memrchr;
+///
+/// let haystack = b"the quick brown fox";
+/// assert_eq!(memrchr(b'o', haystack), Some(17));
+/// ```
+pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+
+    #[cfg(target_os = "linux")]
+    fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
+        use libc;
+
+        // GNU's memrchr() will - unlike memchr() - error if haystack is empty.
+        if haystack.is_empty() {return None}
+        let p = unsafe {
+            libc::memrchr(
+                haystack.as_ptr() as *const libc::c_void,
+                needle as libc::c_int,
+                haystack.len() as libc::size_t)
+        };
+        if p.is_null() {
+            None
+        } else {
+            Some(p as usize - (haystack.as_ptr() as usize))
+        }
+    }
+
+    #[cfg(not(target_os = "linux"))]
+    fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
+        haystack.iter().rposition(|&b| b == needle)
+    }
+
+    memrchr_specific(needle, haystack)
+}
+
+#[allow(dead_code)]
+mod fallback {
+    use cmp;
+    use mem;
+
+    const LO_U64: u64 = 0x0101010101010101;
+    const HI_U64: u64 = 0x8080808080808080;
+
+    // use truncation
+    const LO_USIZE: usize = LO_U64 as usize;
+    const HI_USIZE: usize = HI_U64 as usize;
+
+    /// Return `true` if `x` contains any zero byte.
+    ///
+    /// From *Matters Computational*, J. Arndt
+    ///
+    /// "The idea is to subtract one from each of the bytes and then look for
+    /// bytes where the borrow propagated all the way to the most significant
+    /// bit."
+    #[inline]
+    fn contains_zero_byte(x: usize) -> bool {
+        x.wrapping_sub(LO_USIZE) & !x & HI_USIZE != 0
+    }
+
+    #[cfg(target_pointer_width = "32")]
+    #[inline]
+    fn repeat_byte(b: u8) -> usize {
+        let mut rep = (b as usize) << 8 | b as usize;
+        rep = rep << 16 | rep;
+        rep
+    }
+
+    #[cfg(target_pointer_width = "64")]
+    #[inline]
+    fn repeat_byte(b: u8) -> usize {
+        let mut rep = (b as usize) << 8 | b as usize;
+        rep = rep << 16 | rep;
+        rep = rep << 32 | rep;
+        rep
+    }
+
+    /// Return the first index matching the byte `a` in `text`.
+    pub fn memchr(x: u8, text: &[u8]) -> Option<usize> {
+        // Scan for a single byte value by reading two `usize` words at a time.
+        //
+        // Split `text` in three parts
+        // - unaligned inital part, before the first word aligned address in text
+        // - body, scan by 2 words at a time
+        // - the last remaining part, < 2 word size
+        let len = text.len();
+        let ptr = text.as_ptr();
+        let usize_bytes = mem::size_of::<usize>();
+
+        // search up to an aligned boundary
+        let align = (ptr as usize) & (usize_bytes- 1);
+        let mut offset;
+        if align > 0 {
+            offset = cmp::min(usize_bytes - align, len);
+            if let Some(index) = text[..offset].iter().position(|elt| *elt == x) {
+                return Some(index);
+            }
+        } else {
+            offset = 0;
+        }
+
+        // search the body of the text
+        let repeated_x = repeat_byte(x);
+
+        if len >= 2 * usize_bytes {
+            while offset <= len - 2 * usize_bytes {
+                unsafe {
+                    let u = *(ptr.offset(offset as isize) as *const usize);
+                    let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize);
+
+                    // break if there is a matching byte
+                    let zu = contains_zero_byte(u ^ repeated_x);
+                    let zv = contains_zero_byte(v ^ repeated_x);
+                    if zu || zv {
+                        break;
+                    }
+                }
+                offset += usize_bytes * 2;
+            }
+        }
+
+        // find the byte after the point the body loop stopped
+        text[offset..].iter().position(|elt| *elt == x).map(|i| offset + i)
+    }
+
+    /// Return the last index matching the byte `a` in `text`.
+    pub fn memrchr(x: u8, text: &[u8]) -> Option<usize> {
+        // Scan for a single byte value by reading two `usize` words at a time.
+        //
+        // Split `text` in three parts
+        // - unaligned tail, after the last word aligned address in text
+        // - body, scan by 2 words at a time
+        // - the first remaining bytes, < 2 word size
+        let len = text.len();
+        let ptr = text.as_ptr();
+        let usize_bytes = mem::size_of::<usize>();
+
+        // search to an aligned boundary
+        let end_align = (ptr as usize + len) & (usize_bytes - 1);
+        let mut offset;
+        if end_align > 0 {
+            offset = len - cmp::min(usize_bytes - end_align, len);
+            if let Some(index) = text[offset..].iter().rposition(|elt| *elt == x) {
+                return Some(offset + index);
+            }
+        } else {
+            offset = len;
+        }
+
+        // search the body of the text
+        let repeated_x = repeat_byte(x);
+
+        while offset >= 2 * usize_bytes {
+            unsafe {
+                let u = *(ptr.offset(offset as isize - 2 * usize_bytes as isize) as *const usize);
+                let v = *(ptr.offset(offset as isize - usize_bytes as isize) as *const usize);
+
+                // break if there is a matching byte
+                let zu = contains_zero_byte(u ^ repeated_x);
+                let zv = contains_zero_byte(v ^ repeated_x);
+                if zu || zv {
+                    break;
+                }
+            }
+            offset -= 2 * usize_bytes;
+        }
+
+        // find the byte before the point the body loop stopped
+        text[..offset].iter().rposition(|elt| *elt == x)
+    }
+
+    // test fallback implementations on all plattforms
+    #[test]
+    fn matches_one() {
+        assert_eq!(Some(0), memchr(b'a', b"a"));
+    }
+
+    #[test]
+    fn matches_begin() {
+        assert_eq!(Some(0), memchr(b'a', b"aaaa"));
+    }
+
+    #[test]
+    fn matches_end() {
+        assert_eq!(Some(4), memchr(b'z', b"aaaaz"));
+    }
+
+    #[test]
+    fn matches_nul() {
+        assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00"));
+    }
+
+    #[test]
+    fn matches_past_nul() {
+        assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z"));
+    }
+
+    #[test]
+    fn no_match_empty() {
+        assert_eq!(None, memchr(b'a', b""));
+    }
+
+    #[test]
+    fn no_match() {
+        assert_eq!(None, memchr(b'a', b"xyz"));
+    }
+
+    #[test]
+    fn matches_one_reversed() {
+        assert_eq!(Some(0), memrchr(b'a', b"a"));
+    }
+
+    #[test]
+    fn matches_begin_reversed() {
+        assert_eq!(Some(3), memrchr(b'a', b"aaaa"));
+    }
+
+    #[test]
+    fn matches_end_reversed() {
+        assert_eq!(Some(0), memrchr(b'z', b"zaaaa"));
+    }
+
+    #[test]
+    fn matches_nul_reversed() {
+        assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00"));
+    }
+
+    #[test]
+    fn matches_past_nul_reversed() {
+        assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa"));
+    }
+
+    #[test]
+    fn no_match_empty_reversed() {
+        assert_eq!(None, memrchr(b'a', b""));
+    }
+
+    #[test]
+    fn no_match_reversed() {
+        assert_eq!(None, memrchr(b'a', b"xyz"));
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    // test the implementations for the current plattform
+    use super::{memchr, memrchr};
+
+    #[test]
+    fn matches_one() {
+        assert_eq!(Some(0), memchr(b'a', b"a"));
+    }
+
+    #[test]
+    fn matches_begin() {
+        assert_eq!(Some(0), memchr(b'a', b"aaaa"));
+    }
+
+    #[test]
+    fn matches_end() {
+        assert_eq!(Some(4), memchr(b'z', b"aaaaz"));
+    }
+
+    #[test]
+    fn matches_nul() {
+        assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00"));
+    }
+
+    #[test]
+    fn matches_past_nul() {
+        assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z"));
+    }
+
+    #[test]
+    fn no_match_empty() {
+        assert_eq!(None, memchr(b'a', b""));
+    }
+
+    #[test]
+    fn no_match() {
+        assert_eq!(None, memchr(b'a', b"xyz"));
+    }
+
+    #[test]
+    fn matches_one_reversed() {
+        assert_eq!(Some(0), memrchr(b'a', b"a"));
+    }
+
+    #[test]
+    fn matches_begin_reversed() {
+        assert_eq!(Some(3), memrchr(b'a', b"aaaa"));
+    }
+
+    #[test]
+    fn matches_end_reversed() {
+        assert_eq!(Some(0), memrchr(b'z', b"zaaaa"));
+    }
+
+    #[test]
+    fn matches_nul_reversed() {
+        assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00"));
+    }
+
+    #[test]
+    fn matches_past_nul_reversed() {
+        assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa"));
+    }
+
+    #[test]
+    fn no_match_empty_reversed() {
+        assert_eq!(None, memrchr(b'a', b""));
+    }
+
+    #[test]
+    fn no_match_reversed() {
+        assert_eq!(None, memrchr(b'a', b"xyz"));
+    }
+}
index 9c4e2b1a54c30c17664bb5e1f18aa553ef89d271..320b2a0564d16725974f589f4c69de16a69f1c88 100644 (file)
@@ -14,9 +14,7 @@ use fmt;
 use hash;
 use io;
 use mem;
-use net::{lookup_host, ntoh, hton, Ipv4Addr, Ipv6Addr};
-#[allow(deprecated)]
-use net::IpAddr;
+use net::{lookup_host, ntoh, hton, IpAddr, Ipv4Addr, Ipv6Addr};
 use option;
 use sys::net::netc as c;
 use sys_common::{FromInner, AsInner, IntoInner};
@@ -32,10 +30,10 @@ use vec;
 pub enum SocketAddr {
     /// An IPv4 socket address which is a (ip, port) combination.
     #[stable(feature = "rust1", since = "1.0.0")]
-    V4(SocketAddrV4),
+    V4(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] SocketAddrV4),
     /// An IPv6 socket address
     #[stable(feature = "rust1", since = "1.0.0")]
-    V6(SocketAddrV6),
+    V6(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] SocketAddrV6),
 }
 
 /// An IPv4 socket address which is a (ip, port) combination.
@@ -50,10 +48,7 @@ pub struct SocketAddrV6 { inner: c::sockaddr_in6 }
 
 impl SocketAddr {
     /// Creates a new socket address from the (ip, port) pair.
-    #[unstable(feature = "ip_addr", reason = "recent addition", issue = "27801")]
-    #[rustc_deprecated(reason = "ip type too small a type to pull its weight",
-                       since = "1.6.0")]
-    #[allow(deprecated)]
+    #[stable(feature = "ip_addr", since = "1.7.0")]
     pub fn new(ip: IpAddr, port: u16) -> SocketAddr {
         match ip {
             IpAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a, port)),
@@ -62,10 +57,7 @@ impl SocketAddr {
     }
 
     /// Returns the IP address associated with this socket address.
-    #[unstable(feature = "ip_addr", reason = "recent addition", issue = "27801")]
-    #[rustc_deprecated(reason = "too small a type to pull its weight",
-                       since = "1.6.0")]
-    #[allow(deprecated)]
+    #[stable(feature = "ip_addr", since = "1.7.0")]
     pub fn ip(&self) -> IpAddr {
         match *self {
             SocketAddr::V4(ref a) => IpAddr::V4(*a.ip()),
@@ -140,13 +132,13 @@ impl SocketAddrV6 {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn port(&self) -> u16 { ntoh(self.inner.sin6_port) }
 
-    /// Returns scope ID associated with this address, corresponding to the
-    /// `sin6_flowinfo` field in C.
+    /// Returns the flow information associated with this address,
+    /// corresponding to the `sin6_flowinfo` field in C.
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn flowinfo(&self) -> u32 { ntoh(self.inner.sin6_flowinfo) }
 
-    /// Returns scope ID associated with this address, corresponding to the
-    /// `sin6_scope_id` field in C.
+    /// Returns the scope ID associated with this address,
+    /// corresponding to the `sin6_scope_id` field in C.
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn scope_id(&self) -> u32 { ntoh(self.inner.sin6_scope_id) }
 }
@@ -359,7 +351,6 @@ impl ToSocketAddrs for SocketAddrV6 {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-#[allow(deprecated)]
 impl ToSocketAddrs for (IpAddr, u16) {
     type Iter = option::IntoIter<SocketAddr>;
     fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
@@ -468,9 +459,7 @@ impl<'a, T: ToSocketAddrs + ?Sized> ToSocketAddrs for &'a T {
 #[cfg(test)]
 mod tests {
     use prelude::v1::*;
-    use io;
     use net::*;
-    use net::Ipv6MulticastScope::*;
     use net::test::{tsa, sa6, sa4};
 
     #[test]
index 00be17f7383561ff03319478176b551b6ef1dcc5..8a4d7e3e2c2a5dfa00beb52de633176e3d5c46da 100644 (file)
@@ -22,16 +22,15 @@ use sys::net::netc as c;
 use sys_common::{AsInner, FromInner};
 
 /// An IP address, either an IPv4 or IPv6 address.
-#[unstable(feature = "ip_addr", reason = "recent addition", issue = "27801")]
-#[rustc_deprecated(reason = "too small a type to pull its weight",
-                   since = "1.6.0")]
+#[stable(feature = "ip_addr", since = "1.7.0")]
 #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, PartialOrd, Ord)]
-#[allow(deprecated)]
 pub enum IpAddr {
     /// Representation of an IPv4 address.
-    V4(Ipv4Addr),
+    #[stable(feature = "ip_addr", since = "1.7.0")]
+    V4(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.7.0"))] Ipv4Addr),
     /// Representation of an IPv6 address.
-    V6(Ipv6Addr),
+    #[stable(feature = "ip_addr", since = "1.7.0")]
+    V6(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.7.0"))] Ipv6Addr),
 }
 
 /// Representation of an IPv4 address.
@@ -89,6 +88,9 @@ impl Ipv4Addr {
     }
 
     /// Returns true if this is a loopback address (127.0.0.0/8).
+    ///
+    /// This property is defined by RFC 6890
+    #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_loopback(&self) -> bool {
         self.octets()[0] == 127
     }
@@ -100,6 +102,7 @@ impl Ipv4Addr {
     ///  - 10.0.0.0/8
     ///  - 172.16.0.0/12
     ///  - 192.168.0.0/16
+    #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_private(&self) -> bool {
         match (self.octets()[0], self.octets()[1]) {
             (10, _) => true,
@@ -110,6 +113,9 @@ impl Ipv4Addr {
     }
 
     /// Returns true if the address is link-local (169.254.0.0/16).
+    ///
+    /// This property is defined by RFC 6890
+    #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_link_local(&self) -> bool {
         self.octets()[0] == 169 && self.octets()[1] == 254
     }
@@ -130,7 +136,9 @@ impl Ipv4Addr {
 
     /// Returns true if this is a multicast address.
     ///
-    /// Multicast addresses have a most significant octet between 224 and 239.
+    /// Multicast addresses have a most significant octet between 224 and 239,
+    /// and is defined by RFC 5771
+    #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_multicast(&self) -> bool {
         self.octets()[0] >= 224 && self.octets()[0] <= 239
     }
@@ -138,6 +146,7 @@ impl Ipv4Addr {
     /// Returns true if this is a broadcast address.
     ///
     /// A broadcast address has all octets set to 255 as defined in RFC 919.
+    #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_broadcast(&self) -> bool {
         self.octets()[0] == 255 && self.octets()[1] == 255 &&
         self.octets()[2] == 255 && self.octets()[3] == 255
@@ -150,6 +159,7 @@ impl Ipv4Addr {
     /// - 192.0.2.0/24 (TEST-NET-1)
     /// - 198.51.100.0/24 (TEST-NET-2)
     /// - 203.0.113.0/24 (TEST-NET-3)
+    #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_documentation(&self) -> bool {
         match(self.octets()[0], self.octets()[1], self.octets()[2], self.octets()[3]) {
             (192, 0, 2, _) => true,
@@ -302,11 +312,17 @@ impl Ipv6Addr {
     }
 
     /// Returns true for the special 'unspecified' address ::.
+    ///
+    /// This property is defined in RFC 6890.
+    #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_unspecified(&self) -> bool {
         self.segments() == [0, 0, 0, 0, 0, 0, 0, 0]
     }
 
     /// Returns true if this is a loopback address (::1).
+    ///
+    /// This property is defined in RFC 6890.
+    #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_loopback(&self) -> bool {
         self.segments() == [0, 0, 0, 0, 0, 0, 0, 1]
     }
@@ -378,7 +394,9 @@ impl Ipv6Addr {
 
     /// Returns true if this is a multicast address.
     ///
-    /// Multicast addresses have the form ff00::/8.
+    /// Multicast addresses have the form ff00::/8, and this property is defined
+    /// by RFC 3956.
+    #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_multicast(&self) -> bool {
         (self.segments()[0] & 0xff00) == 0xff00
     }
@@ -527,7 +545,6 @@ impl FromInner<c::in6_addr> for Ipv6Addr {
 #[cfg(test)]
 mod tests {
     use prelude::v1::*;
-    use io;
     use net::*;
     use net::Ipv6MulticastScope::*;
     use net::test::{tsa, sa6, sa4};
index 545302b83931f03318f9fe4407b6ce2173e24cf8..aac2e05c37c40e58586725e18632cf8c51b7ae35 100644 (file)
@@ -18,7 +18,6 @@ use io::{self, Error, ErrorKind};
 use sys_common::net as net_imp;
 
 #[stable(feature = "rust1", since = "1.0.0")]
-#[allow(deprecated)]
 pub use self::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
@@ -140,7 +139,6 @@ pub fn lookup_host(host: &str) -> io::Result<LookupHost> {
 ///
 /// ```no_run
 /// #![feature(lookup_addr)]
-/// #![feature(ip_addr)]
 ///
 /// use std::net::{self, Ipv4Addr, IpAddr};
 ///
index 79a269ff87c20689d0f6330652c7cdc1b093aa08..46a0309dbb509de503a154b8acf6f91e19e94112 100644 (file)
@@ -17,7 +17,6 @@ use prelude::v1::*;
 
 use error::Error;
 use fmt;
-#[allow(deprecated)]
 use net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
 use str::FromStr;
 
@@ -193,7 +192,7 @@ impl<'a> Parser<'a> {
         fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> Ipv6Addr {
             assert!(head.len() + tail.len() <= 8);
             let mut gs = [0; 8];
-            gs.clone_from_slice(head);
+            gs[..head.len()].clone_from_slice(head);
             gs[(8 - tail.len()) .. 8].clone_from_slice(tail);
             Ipv6Addr::new(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7])
         }
@@ -262,7 +261,6 @@ impl<'a> Parser<'a> {
         self.read_atomically(|p| p.read_ipv6_addr_impl())
     }
 
-    #[allow(deprecated)]
     fn read_ip_addr(&mut self) -> Option<IpAddr> {
         let ipv4_addr = |p: &mut Parser| p.read_ipv4_addr().map(IpAddr::V4);
         let ipv6_addr = |p: &mut Parser| p.read_ipv6_addr().map(IpAddr::V6);
@@ -308,7 +306,6 @@ impl<'a> Parser<'a> {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-#[allow(deprecated)]
 impl FromStr for IpAddr {
     type Err = AddrParseError;
     fn from_str(s: &str) -> Result<IpAddr, AddrParseError> {
index f54d4f804806de63309373fcea235e4e9a59e11d..f9c38c38458475661a87e298266849114991d7e7 100644 (file)
@@ -319,7 +319,7 @@ mod tests {
     use net::test::{next_test_ip4, next_test_ip6};
     use sync::mpsc::channel;
     use sys_common::AsInner;
-    use time::Duration;
+    use time::{Instant, Duration};
     use thread;
 
     fn each_ip(f: &mut FnMut(SocketAddr)) {
@@ -929,6 +929,7 @@ mod tests {
 
         t!(stream.set_write_timeout(None));
         assert_eq!(None, t!(stream.write_timeout()));
+        drop(listener);
     }
 
     #[test]
@@ -940,11 +941,11 @@ mod tests {
         t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
 
         let mut buf = [0; 10];
-        let wait = Duration::span(|| {
-            let kind = stream.read(&mut buf).err().expect("expected error").kind();
-            assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut);
-        });
-        assert!(wait > Duration::from_millis(400));
+        let start = Instant::now();
+        let kind = stream.read(&mut buf).err().expect("expected error").kind();
+        assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut);
+        assert!(start.elapsed() > Duration::from_millis(400));
+        drop(listener);
     }
 
     #[test]
@@ -962,10 +963,10 @@ mod tests {
         t!(stream.read(&mut buf));
         assert_eq!(b"hello world", &buf[..]);
 
-        let wait = Duration::span(|| {
-            let kind = stream.read(&mut buf).err().expect("expected error").kind();
-            assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut);
-        });
-        assert!(wait > Duration::from_millis(400));
+        let start = Instant::now();
+        let kind = stream.read(&mut buf).err().expect("expected error").kind();
+        assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut);
+        assert!(start.elapsed() > Duration::from_millis(400));
+        drop(listener);
     }
 }
index b212d4d32aadfcfa55e6034c8da055f83fb5ff73..29ac991a547dc9b4925b854c0b1ea1a0a316c96f 100644 (file)
@@ -27,18 +27,19 @@ use time::Duration;
 /// use std::net::UdpSocket;
 ///
 /// # fn foo() -> std::io::Result<()> {
-/// let mut socket = try!(UdpSocket::bind("127.0.0.1:34254"));
+/// {
+///     let mut socket = try!(UdpSocket::bind("127.0.0.1:34254"));
 ///
-/// let mut buf = [0; 10];
-/// let (amt, src) = try!(socket.recv_from(&mut buf));
+///     // read from the socket
+///     let mut buf = [0; 10];
+///     let (amt, src) = try!(socket.recv_from(&mut buf));
 ///
-/// // Send a reply to the socket we received data from
-/// let buf = &mut buf[..amt];
-/// buf.reverse();
-/// try!(socket.send_to(buf, &src));
-///
-/// drop(socket); // close the socket
-/// # Ok(())
+///     // send a reply to the socket we received data from
+///     let buf = &mut buf[..amt];
+///     buf.reverse();
+///     try!(socket.send_to(buf, &src));
+///     # Ok(())
+/// } // the socket is closed here
 /// # }
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -169,7 +170,7 @@ mod tests {
     use net::test::{next_test_ip4, next_test_ip6};
     use sync::mpsc::channel;
     use sys_common::AsInner;
-    use time::Duration;
+    use time::{Instant, Duration};
     use thread;
 
     fn each_ip(f: &mut FnMut(SocketAddr, SocketAddr)) {
@@ -370,22 +371,22 @@ mod tests {
     fn test_read_timeout() {
         let addr = next_test_ip4();
 
-        let mut stream = t!(UdpSocket::bind(&addr));
+        let stream = t!(UdpSocket::bind(&addr));
         t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
 
         let mut buf = [0; 10];
-        let wait = Duration::span(|| {
-            let kind = stream.recv_from(&mut buf).err().expect("expected error").kind();
-            assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut);
-        });
-        assert!(wait > Duration::from_millis(400));
+
+        let start = Instant::now();
+        let kind = stream.recv_from(&mut buf).err().expect("expected error").kind();
+        assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut);
+        assert!(start.elapsed() > Duration::from_millis(400));
     }
 
     #[test]
     fn test_read_with_timeout() {
         let addr = next_test_ip4();
 
-        let mut stream = t!(UdpSocket::bind(&addr));
+        let stream = t!(UdpSocket::bind(&addr));
         t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
 
         t!(stream.send_to(b"hello world", &addr));
@@ -394,10 +395,9 @@ mod tests {
         t!(stream.recv_from(&mut buf));
         assert_eq!(b"hello world", &buf[..]);
 
-        let wait = Duration::span(|| {
-            let kind = stream.recv_from(&mut buf).err().expect("expected error").kind();
-            assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut);
-        });
-        assert!(wait > Duration::from_millis(400));
+        let start = Instant::now();
+        let kind = stream.recv_from(&mut buf).err().expect("expected error").kind();
+        assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut);
+        assert!(start.elapsed() > Duration::from_millis(400));
     }
 }
index c87becd741eb1a08431684b3476ab7c3b6c4498f..7f57d6dc650bab145670458d28143328787cf85e 100644 (file)
 #![stable(feature = "rust1", since = "1.0.0")]
 #![allow(missing_docs)]
 
+#[cfg(not(test))]
 use core::num;
+#[cfg(not(test))]
 use intrinsics;
+#[cfg(not(test))]
 use libc::c_int;
-use num::{FpCategory, ParseFloatError};
+#[cfg(not(test))]
+use num::FpCategory;
+
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::f32::{RADIX, MANTISSA_DIGITS, DIGITS, EPSILON};
@@ -57,7 +62,7 @@ mod cmath {
         pub fn hypotf(x: c_float, y: c_float) -> c_float;
     }
 
-    // See the comments in `core::float::Float::floor` for why MSVC is special
+    // See the comments in the `floor` function for why MSVC is special
     // here.
     #[cfg(not(target_env = "msvc"))]
     extern {
@@ -79,44 +84,54 @@ mod cmath {
     mod shims {
         use libc::{c_float, c_int};
 
+        #[inline]
         pub unsafe fn acosf(n: c_float) -> c_float {
             f64::acos(n as f64) as c_float
         }
 
+        #[inline]
         pub unsafe fn asinf(n: c_float) -> c_float {
             f64::asin(n as f64) as c_float
         }
 
+        #[inline]
         pub unsafe fn atan2f(n: c_float, b: c_float) -> c_float {
             f64::atan2(n as f64, b as f64) as c_float
         }
 
+        #[inline]
         pub unsafe fn atanf(n: c_float) -> c_float {
             f64::atan(n as f64) as c_float
         }
 
+        #[inline]
         pub unsafe fn coshf(n: c_float) -> c_float {
             f64::cosh(n as f64) as c_float
         }
 
+        #[inline]
         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;
             a as c_float
         }
 
+        #[inline]
         pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float {
             f64::ldexp(x as f64, n as isize) as c_float
         }
 
+        #[inline]
         pub unsafe fn sinhf(n: c_float) -> c_float {
             f64::sinh(n as f64) as c_float
         }
 
+        #[inline]
         pub unsafe fn tanf(n: c_float) -> c_float {
             f64::tan(n as f64) as c_float
         }
 
+        #[inline]
         pub unsafe fn tanhf(n: c_float) -> c_float {
             f64::tanh(n as f64) as c_float
         }
@@ -126,16 +141,6 @@ mod cmath {
 #[cfg(not(test))]
 #[lang = "f32"]
 impl f32 {
-    /// Parses a float as with a given radix
-    #[unstable(feature = "float_from_str_radix", reason = "recently moved API",
-               issue = "27736")]
-    #[rustc_deprecated(since = "1.4.0",
-                 reason = "unclear how useful or correct this is")]
-    #[allow(deprecated)]
-    pub fn from_str_radix(s: &str, radix: u32) -> Result<f32, ParseFloatError> {
-        num::Float::from_str_radix(s, radix)
-    }
-
     /// Returns `true` if this value is `NaN` and false otherwise.
     ///
     /// ```
@@ -277,8 +282,6 @@ impl f32 {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn floor(self) -> f32 {
-        return floorf(self);
-
         // On MSVC LLVM will lower many math intrinsics to a call to the
         // corresponding function. On MSVC, however, many of these functions
         // aren't actually available as symbols to call, but rather they are all
@@ -293,9 +296,9 @@ impl f32 {
         // redirect to this comment, so `floorf` is just one case of a missing
         // function on MSVC, but there are many others elsewhere.
         #[cfg(target_env = "msvc")]
-        fn floorf(f: f32) -> f32 { (f as f64).floor() as f32 }
+        return (self as f64).floor() as f32;
         #[cfg(not(target_env = "msvc"))]
-        fn floorf(f: f32) -> f32 { unsafe { intrinsics::floorf32(f) } }
+        return unsafe { intrinsics::floorf32(self) };
     }
 
     /// Returns the smallest integer greater than or equal to a number.
@@ -310,13 +313,11 @@ impl f32 {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn ceil(self) -> f32 {
-        return ceilf(self);
-
         // see notes above in `floor`
         #[cfg(target_env = "msvc")]
-        fn ceilf(f: f32) -> f32 { (f as f64).ceil() as f32 }
+        return (self as f64).ceil() as f32;
         #[cfg(not(target_env = "msvc"))]
-        fn ceilf(f: f32) -> f32 { unsafe { intrinsics::ceilf32(f) } }
+        return unsafe { intrinsics::ceilf32(self) };
     }
 
     /// Returns the nearest integer to a number. Round half-way cases away from
@@ -511,13 +512,11 @@ impl f32 {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn powf(self, n: f32) -> f32 {
-        return powf(self, n);
-
         // see notes above in `floor`
         #[cfg(target_env = "msvc")]
-        fn powf(f: f32, n: f32) -> f32 { (f as f64).powf(n as f64) as f32 }
+        return (self as f64).powf(n as f64) as f32;
         #[cfg(not(target_env = "msvc"))]
-        fn powf(f: f32, n: f32) -> f32 { unsafe { intrinsics::powf32(f, n) } }
+        return unsafe { intrinsics::powf32(self, n) };
     }
 
     /// Takes the square root of a number.
@@ -562,13 +561,11 @@ impl f32 {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn exp(self) -> f32 {
-        return expf(self);
-
         // see notes above in `floor`
         #[cfg(target_env = "msvc")]
-        fn expf(f: f32) -> f32 { (f as f64).exp() as f32 }
+        return (self as f64).exp() as f32;
         #[cfg(not(target_env = "msvc"))]
-        fn expf(f: f32) -> f32 { unsafe { intrinsics::expf32(f) } }
+        return unsafe { intrinsics::expf32(self) };
     }
 
     /// Returns `2^(self)`.
@@ -606,13 +603,11 @@ impl f32 {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn ln(self) -> f32 {
-        return logf(self);
-
         // see notes above in `floor`
         #[cfg(target_env = "msvc")]
-        fn logf(f: f32) -> f32 { (f as f64).ln() as f32 }
+        return (self as f64).ln() as f32;
         #[cfg(not(target_env = "msvc"))]
-        fn logf(f: f32) -> f32 { unsafe { intrinsics::logf32(f) } }
+        return unsafe { intrinsics::logf32(self) };
     }
 
     /// Returns the logarithm of the number with respect to an arbitrary base.
@@ -669,20 +664,16 @@ impl f32 {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn log10(self) -> f32 {
-        return log10f(self);
-
         // see notes above in `floor`
         #[cfg(target_env = "msvc")]
-        fn log10f(f: f32) -> f32 { (f as f64).log10() as f32 }
+        return (self as f64).log10() as f32;
         #[cfg(not(target_env = "msvc"))]
-        fn log10f(f: f32) -> f32 { unsafe { intrinsics::log10f32(f) } }
+        return unsafe { intrinsics::log10f32(self) };
     }
 
     /// Converts radians to degrees.
     ///
     /// ```
-    /// #![feature(float_extras)]
-    ///
     /// use std::f32::{self, consts};
     ///
     /// let angle = consts::PI;
@@ -691,16 +682,13 @@ impl f32 {
     ///
     /// assert!(abs_difference <= f32::EPSILON);
     /// ```
-    #[unstable(feature = "float_extras", reason = "desirability is unclear",
-               issue = "27752")]
+    #[stable(feature = "f32_deg_rad_conversions", since="1.7.0")]
     #[inline]
     pub fn to_degrees(self) -> f32 { num::Float::to_degrees(self) }
 
     /// Converts degrees to radians.
     ///
     /// ```
-    /// #![feature(float_extras)]
-    ///
     /// use std::f32::{self, consts};
     ///
     /// let angle = 180.0f32;
@@ -709,8 +697,7 @@ impl f32 {
     ///
     /// assert!(abs_difference <= f32::EPSILON);
     /// ```
-    #[unstable(feature = "float_extras", reason = "desirability is unclear",
-               issue = "27752")]
+    #[stable(feature = "f32_deg_rad_conversions", since="1.7.0")]
     #[inline]
     pub fn to_radians(self) -> f32 { num::Float::to_radians(self) }
 
@@ -895,13 +882,11 @@ impl f32 {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn sin(self) -> f32 {
-        return sinf(self);
-
         // see notes in `core::f32::Float::floor`
         #[cfg(target_env = "msvc")]
-        fn sinf(f: f32) -> f32 { (f as f64).sin() as f32 }
+        return (self as f64).sin() as f32;
         #[cfg(not(target_env = "msvc"))]
-        fn sinf(f: f32) -> f32 { unsafe { intrinsics::sinf32(f) } }
+        return unsafe { intrinsics::sinf32(self) };
     }
 
     /// Computes the cosine of a number (in radians).
@@ -918,13 +903,11 @@ impl f32 {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn cos(self) -> f32 {
-        return cosf(self);
-
         // see notes in `core::f32::Float::floor`
         #[cfg(target_env = "msvc")]
-        fn cosf(f: f32) -> f32 { (f as f64).cos() as f32 }
+        return (self as f64).cos() as f32;
         #[cfg(not(target_env = "msvc"))]
-        fn cosf(f: f32) -> f32 { unsafe { intrinsics::cosf32(f) } }
+        return unsafe { intrinsics::cosf32(self) };
     }
 
     /// Computes the tangent of a number (in radians).
@@ -1712,11 +1695,9 @@ mod tests {
 
     #[test]
     fn test_ldexp() {
-        // We have to use from_str until base-2 exponents
-        // are supported in floating-point literals
-        let f1: f32 = f32::from_str_radix("1p-123", 16).unwrap();
-        let f2: f32 = f32::from_str_radix("1p-111", 16).unwrap();
-        let f3: f32 = f32::from_str_radix("1.Cp-12", 16).unwrap();
+        let f1 = 2.0f32.powi(-123);
+        let f2 = 2.0f32.powi(-111);
+        let f3 = 1.75 * 2.0f32.powi(-12);
         assert_eq!(f32::ldexp(1f32, -123), f1);
         assert_eq!(f32::ldexp(1f32, -111), f2);
         assert_eq!(f32::ldexp(1.75f32, -12), f3);
@@ -1734,11 +1715,9 @@ mod tests {
 
     #[test]
     fn test_frexp() {
-        // We have to use from_str until base-2 exponents
-        // are supported in floating-point literals
-        let f1: f32 = f32::from_str_radix("1p-123", 16).unwrap();
-        let f2: f32 = f32::from_str_radix("1p-111", 16).unwrap();
-        let f3: f32 = f32::from_str_radix("1.Cp-123", 16).unwrap();
+        let f1 = 2.0f32.powi(-123);
+        let f2 = 2.0f32.powi(-111);
+        let f3 = 1.75 * 2.0f32.powi(-123);
         let (x1, exp1) = f1.frexp();
         let (x2, exp2) = f2.frexp();
         let (x3, exp3) = f3.frexp();
index 6b9c753443b3c317462692de3f0df647405cd1ac..b6a85ee0e9f586d017ede8f78a48d660af2314bb 100644 (file)
 #![stable(feature = "rust1", since = "1.0.0")]
 #![allow(missing_docs)]
 
+#[cfg(not(test))]
 use core::num;
+#[cfg(not(test))]
 use intrinsics;
+#[cfg(not(test))]
 use libc::c_int;
-use num::{FpCategory, ParseFloatError};
+#[cfg(not(test))]
+use num::FpCategory;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::f64::{RADIX, MANTISSA_DIGITS, DIGITS, EPSILON};
@@ -83,16 +87,6 @@ mod cmath {
 #[cfg(not(test))]
 #[lang = "f64"]
 impl f64 {
-    /// Parses a float as with a given radix
-    #[unstable(feature = "float_from_str_radix", reason = "recently moved API",
-               issue = "27736")]
-    #[rustc_deprecated(since = "1.4.0",
-                 reason = "unclear how useful or correct this is")]
-    #[allow(deprecated)]
-    pub fn from_str_radix(s: &str, radix: u32) -> Result<f64, ParseFloatError> {
-        num::Float::from_str_radix(s, radix)
-    }
-
     /// Returns `true` if this value is `NaN` and false otherwise.
     ///
     /// ```
@@ -1569,11 +1563,9 @@ mod tests {
 
     #[test]
     fn test_ldexp() {
-        // We have to use from_str until base-2 exponents
-        // are supported in floating-point literals
-        let f1: f64 = f64::from_str_radix("1p-123", 16).unwrap();
-        let f2: f64 = f64::from_str_radix("1p-111", 16).unwrap();
-        let f3: f64 = f64::from_str_radix("1.Cp-12", 16).unwrap();
+        let f1 = 2.0f64.powi(-123);
+        let f2 = 2.0f64.powi(-111);
+        let f3 = 1.75 * 2.0f64.powi(-12);
         assert_eq!(f64::ldexp(1f64, -123), f1);
         assert_eq!(f64::ldexp(1f64, -111), f2);
         assert_eq!(f64::ldexp(1.75f64, -12), f3);
@@ -1591,11 +1583,9 @@ mod tests {
 
     #[test]
     fn test_frexp() {
-        // We have to use from_str until base-2 exponents
-        // are supported in floating-point literals
-        let f1: f64 = f64::from_str_radix("1p-123", 16).unwrap();
-        let f2: f64 = f64::from_str_radix("1p-111", 16).unwrap();
-        let f3: f64 = f64::from_str_radix("1.Cp-123", 16).unwrap();
+        let f1 = 2.0f64.powi(-123);
+        let f2 = 2.0f64.powi(-111);
+        let f3 = 1.75 * 2.0f64.powi(-123);
         let (x1, exp1) = f1.frexp();
         let (x2, exp2) = f2.frexp();
         let (x3, exp3) = f3.frexp();
index 8e207f5e6cfc5e5f4c002b5e3263f974921c8d2c..faaff494cab51133329e52d3bf99d1162e8ecf23 100644 (file)
@@ -47,11 +47,6 @@ pub fn test_num<T>(ten: T, two: T) where
 #[cfg(test)]
 mod tests {
     use super::*;
-    use i8;
-    use i16;
-    use i32;
-    use i64;
-    use isize;
     use u8;
     use u16;
     use u32;
index f44199f311bfa7298a5e86eb57c8e12365270360..953d0917141d10337b9b262009577691d889d31f 100644 (file)
@@ -205,7 +205,8 @@ mod arch {
     }
 }
 
-#[cfg(target_arch = "x86_64")]
+#[cfg(any(target_arch = "x86_64", target_arch = "powerpc64",
+          target_arch = "powerpc64le"))]
 mod arch {
     use super::{dev_t, mode_t};
     use os::raw::{c_long, c_int};
index 209546c4e4f5626f383907e88df398a64bad639e..b4d493953030e8e7f7e3ee8e168859aa0435934e 100644 (file)
@@ -16,7 +16,7 @@ use os::raw::c_long;
 use os::unix::raw::{uid_t, gid_t};
 
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = i64;
-#[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u32;
+#[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = i32;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = i32;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type fflags_t = u32;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64;
index 17d6b2605c62831c7300491b4a824f3f90bb79cd..62080fee48ec1178e62e0acecd44199ca47918b3 100644 (file)
 
 #[cfg(any(target_os = "android",
           all(target_os = "linux", any(target_arch = "aarch64",
-                                       target_arch = "arm"))))]
+                                       target_arch = "arm",
+                                       target_arch = "powerpc",
+                                       target_arch = "powerpc64",
+                                       target_arch = "powerpc64le"))))]
 #[stable(feature = "raw_os", since = "1.1.0")] pub type c_char = u8;
 #[cfg(not(any(target_os = "android",
               all(target_os = "linux", any(target_arch = "aarch64",
-                                           target_arch = "arm")))))]
+                                           target_arch = "arm",
+                                           target_arch = "powerpc",
+                                           target_arch = "powerpc64",
+                                           target_arch = "powerpc64le")))))]
 #[stable(feature = "raw_os", since = "1.1.0")] pub type c_char = i8;
 #[stable(feature = "raw_os", since = "1.1.0")] pub type c_schar = i8;
 #[stable(feature = "raw_os", since = "1.1.0")] pub type c_uchar = u8;
@@ -88,7 +94,8 @@ mod tests {
             c_longlong c_ulonglong c_float c_double);
     }
 
-    #[cfg(unix)]
+    #[cfg(all(unix, not(target_os = "android")))]
+    #[test]
     fn unix() {
         {
             use os::unix::raw;
@@ -101,6 +108,7 @@ mod tests {
     }
 
     #[cfg(windows)]
+    #[test]
     fn windows() {
         use os::windows::raw;
     }
diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs
new file mode 100644 (file)
index 0000000..3677bd2
--- /dev/null
@@ -0,0 +1,288 @@
+// 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.
+
+//! Panic support in the standard library
+
+#![unstable(feature = "std_panic", reason = "awaiting feedback",
+            issue = "27719")]
+
+use any::Any;
+use boxed::Box;
+use cell::UnsafeCell;
+use ops::{Deref, DerefMut};
+use ptr::{Unique, Shared};
+use rc::Rc;
+use sync::{Arc, Mutex, RwLock};
+use sys_common::unwind;
+use thread::Result;
+
+pub use panicking::{take_handler, set_handler, PanicInfo, Location};
+
+/// 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.
+///
+/// ## What is panic 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
+/// anticipated, and has the possibility of causing subtle bugs through a
+/// combination of two cricial components:
+///
+/// 1. A data structure is in a temporarily invalid state when the thread
+///    panics.
+/// 2. This broken invariant is then later observed.
+///
+/// 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
+/// 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++).
+///
+/// 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
+/// 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].
+///
+/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
+///
+/// ## What is `RecoverSafe`?
+///
+/// Now that we've got an idea of what panic 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
+/// module as it allows catching a panic and then re-using the environment of
+/// the closure.
+///
+/// Simply put, a type `T` implements `RecoverSafe` if it cannot easily allow
+/// witnessing a broken invariant through the use of `recover` (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).
+///
+/// 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
+/// witnessed and may need to be accounted for.
+///
+/// ## Who implements `RecoverSafe`?
+///
+/// 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
+/// simply accesed as usual.
+///
+/// Types like `&Mutex<T>`, however, are recover 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 `RecoverSafe` 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,
+/// the lack of `unsafe` means it is mostly an advisory. The `AssertRecoverSafe`
+/// 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
+/// (more on this below).
+#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
+#[rustc_on_unimplemented = "the type {Self} may not be safely transferred \
+                            across a recover boundary"]
+pub trait RecoverSafe {}
+
+/// A marker trait representing types where a shared reference is considered
+/// recover safe.
+///
+/// This trait is namely not implemented by `UnsafeCell`, the root of all
+/// interior mutability.
+///
+/// This is a "helper marker trait" used to provide impl blocks for the
+/// `RecoverSafe` trait, for more information see that documentation.
+#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
+#[rustc_on_unimplemented = "the type {Self} contains interior mutability \
+                            and a reference may not be safely transferrable \
+                            across a recover boundary"]
+pub trait RefRecoverSafe {}
+
+/// A simple wrapper around a type to assert that it is panic 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
+/// 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
+/// account. This wrapper struct is useful for a quick and lightweight
+/// annotation that a variable is indeed panic safe.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(recover, std_panic)]
+///
+/// use std::panic::{self, AssertRecoverSafe};
+///
+/// let mut variable = 4;
+///
+/// // This code will not compile because the closure captures `&mut variable`
+/// // which is not considered panic safe by default.
+///
+/// // panic::recover(|| {
+/// //     variable += 3;
+/// // });
+///
+/// // This, however, will compile due to the `AssertRecoverSafe` wrapper
+/// let result = {
+///     let mut wrapper = AssertRecoverSafe::new(&mut variable);
+///     panic::recover(move || {
+///         **wrapper += 3;
+///     })
+/// };
+/// // ...
+/// ```
+#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
+pub struct AssertRecoverSafe<T>(T);
+
+// Implementations of the `RecoverSafe` trait:
+//
+// * By default everything is recover safe
+// * pointers T contains mutability of some form are not recover safe
+// * Unique, an owning pointer, lifts an implementation
+// * Types like Mutex/RwLock which are explicilty poisoned are recover safe
+// * Our custom AssertRecoverSafe wrapper is indeed recover safe
+impl RecoverSafe for .. {}
+impl<'a, T: ?Sized> !RecoverSafe for &'a mut T {}
+impl<'a, T: RefRecoverSafe + ?Sized> RecoverSafe for &'a T {}
+impl<T: RefRecoverSafe + ?Sized> RecoverSafe for *const T {}
+impl<T: RefRecoverSafe + ?Sized> RecoverSafe for *mut T {}
+impl<T: RecoverSafe> RecoverSafe for Unique<T> {}
+impl<T: RefRecoverSafe + ?Sized> RecoverSafe for Shared<T> {}
+impl<T: ?Sized> RecoverSafe for Mutex<T> {}
+impl<T: ?Sized> RecoverSafe for RwLock<T> {}
+impl<T> RecoverSafe 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
+// impl up one level to Arc/Rc itself
+impl<T: RefRecoverSafe + ?Sized> RecoverSafe for Rc<T> {}
+impl<T: RefRecoverSafe + ?Sized> RecoverSafe for Arc<T> {}
+
+// Pretty simple implementations for the `RefRecoverSafe` marker trait,
+// basically just saying that this is a marker trait and `UnsafeCell` is the
+// only thing which doesn't implement it (which then transitively applies to
+// everything else).
+impl RefRecoverSafe for .. {}
+impl<T: ?Sized> !RefRecoverSafe for UnsafeCell<T> {}
+impl<T> RefRecoverSafe for AssertRecoverSafe<T> {}
+
+impl<T> AssertRecoverSafe<T> {
+    /// Creates a new `AssertRecoverSafe` wrapper around the provided type.
+    #[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
+    pub fn new(t: T) -> AssertRecoverSafe<T> {
+        AssertRecoverSafe(t)
+    }
+}
+
+impl<T> Deref for AssertRecoverSafe<T> {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        &self.0
+    }
+}
+
+impl<T> DerefMut for AssertRecoverSafe<T> {
+    fn deref_mut(&mut self) -> &mut T {
+        &mut self.0
+    }
+}
+
+/// Invokes a closure, capturing the cause of panic if one occurs.
+///
+/// This function will return `Ok` with the closure's result if the closure
+/// does not panic, and will return `Err(cause)` if the closure panics. The
+/// `cause` returned is the object with which panic was originally invoked.
+///
+/// It is currently undefined behavior to unwind from Rust code into foreign
+/// code, so this function is particularly useful when Rust is called from
+/// another language (normally C). This can run arbitrary Rust code, capturing a
+/// panic and allowing a graceful handling of the error.
+///
+/// It is **not** recommended to use this function for a general try/catch
+/// mechanism. The `Result` type is more appropriate to use for functions that
+/// can fail on a regular basis.
+///
+/// The closure provided is required to adhere to the `RecoverSafe` to ensure
+/// that all captured variables are safe to cross this recover 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
+/// becomes a problem the associated `AssertRecoverSafe` wrapper type in this
+/// module can be used to quickly assert that the usage here is indeed exception
+/// safe.
+///
+/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
+///
+/// # Examples
+///
+/// ```
+/// #![feature(recover, std_panic)]
+///
+/// use std::panic;
+///
+/// let result = panic::recover(|| {
+///     println!("hello!");
+/// });
+/// assert!(result.is_ok());
+///
+/// let result = panic::recover(|| {
+///     panic!("oh no!");
+/// });
+/// assert!(result.is_err());
+/// ```
+#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
+pub fn recover<F: FnOnce() -> R + RecoverSafe, R>(f: F) -> Result<R> {
+    let mut result = None;
+    unsafe {
+        let result = &mut result;
+        try!(unwind::try(move || *result = Some(f())))
+    }
+    Ok(result.unwrap())
+}
+
+/// Triggers a panic without invoking the panic handler.
+///
+/// This is designed to be used in conjunction with `recover` to, for example,
+/// carry a panic across a layer of C code.
+///
+/// # Examples
+///
+/// ```should_panic
+/// #![feature(std_panic, recover, panic_propagate)]
+///
+/// use std::panic;
+///
+/// let result = panic::recover(|| {
+///     panic!("oh no!");
+/// });
+///
+/// if let Err(err) = result {
+///     panic::propagate(err);
+/// }
+/// ```
+#[unstable(feature = "panic_propagate", reason = "awaiting feedback", issue = "30752")]
+pub fn propagate(payload: Box<Any + Send>) -> ! {
+    unwind::rust_panic(payload)
+}
index 2b2af350c992cc874737ccfe547e65ef9f7ffc2b..8561ecd9c4cb92d9e21952fe65167c03b9ad9ab5 100644 (file)
@@ -15,10 +15,12 @@ use any::Any;
 use cell::Cell;
 use cell::RefCell;
 use intrinsics;
+use sync::StaticRwLock;
 use sys::stdio::Stderr;
 use sys_common::backtrace;
 use sys_common::thread_info;
 use sys_common::util;
+use thread;
 
 thread_local! { pub static PANIC_COUNT: Cell<usize> = Cell::new(0) }
 
@@ -28,11 +30,138 @@ thread_local! {
     }
 }
 
-fn log_panic(obj: &(Any+Send), file: &'static str, line: u32,
-             log_backtrace: bool) {
-    let msg = match obj.downcast_ref::<&'static str>() {
+#[derive(Copy, Clone)]
+enum Handler {
+    Default,
+    Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)),
+}
+
+static HANDLER_LOCK: StaticRwLock = StaticRwLock::new();
+static mut HANDLER: Handler = Handler::Default;
+
+/// Registers a custom panic handler, replacing any that was previously
+/// registered.
+///
+/// The panic handler is invoked when a thread panics, but before it begins
+/// unwinding the stack. The default handler prints a message to standard error
+/// and generates a backtrace if requested, but this behavior can be customized
+/// with the `set_handler` and `take_handler` functions.
+///
+/// The handler is provided with a `PanicInfo` struct which contains information
+/// about the origin of the panic, including the payload passed to `panic!` and
+/// the source code location from which the panic originated.
+///
+/// The panic handler is a global resource.
+///
+/// # Panics
+///
+/// Panics if called from a panicking thread.
+#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
+pub fn set_handler<F>(handler: F) where F: Fn(&PanicInfo) + 'static + Sync + Send {
+    if thread::panicking() {
+        panic!("cannot modify the panic handler from a panicking thread");
+    }
+
+    let handler = Box::new(handler);
+    unsafe {
+        let lock = HANDLER_LOCK.write();
+        let old_handler = HANDLER;
+        HANDLER = Handler::Custom(Box::into_raw(handler));
+        drop(lock);
+
+        if let Handler::Custom(ptr) = old_handler {
+            Box::from_raw(ptr);
+        }
+    }
+}
+
+/// Unregisters the current panic handler, returning it.
+///
+/// If no custom handler is registered, the default handler will be returned.
+///
+/// # Panics
+///
+/// Panics if called from a panicking thread.
+#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
+pub fn take_handler() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
+    if thread::panicking() {
+        panic!("cannot modify the panic handler from a panicking thread");
+    }
+
+    unsafe {
+        let lock = HANDLER_LOCK.write();
+        let handler = HANDLER;
+        HANDLER = Handler::Default;
+        drop(lock);
+
+        match handler {
+            Handler::Default => Box::new(default_handler),
+            Handler::Custom(ptr) => {Box::from_raw(ptr)} // FIXME #30530
+        }
+    }
+}
+
+/// A struct providing information about a panic.
+#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
+pub struct PanicInfo<'a> {
+    payload: &'a (Any + Send),
+    location: Location<'a>,
+}
+
+impl<'a> PanicInfo<'a> {
+    /// Returns the payload associated with the panic.
+    ///
+    /// This will commonly, but not always, be a `&'static str` or `String`.
+    #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
+    pub fn payload(&self) -> &(Any + Send) {
+        self.payload
+    }
+
+    /// Returns information about the location from which the panic originated,
+    /// if available.
+    ///
+    /// This method will currently always return `Some`, but this may change
+    /// in future versions.
+    #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
+    pub fn location(&self) -> Option<&Location> {
+        Some(&self.location)
+    }
+}
+
+/// A struct containing information about the location of a panic.
+#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
+pub struct Location<'a> {
+    file: &'a str,
+    line: u32,
+}
+
+impl<'a> Location<'a> {
+    /// Returns the name of the source file from which the panic originated.
+    #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
+    pub fn file(&self) -> &str {
+        self.file
+    }
+
+    /// Returns the line number from which the panic originated.
+    #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
+    pub fn line(&self) -> u32 {
+        self.line
+    }
+}
+
+fn default_handler(info: &PanicInfo) {
+    let panics = PANIC_COUNT.with(|s| s.get());
+
+    // If this is a double panic, make sure that we print a backtrace
+    // for this panic. Otherwise only print it if logging is enabled.
+    let log_backtrace = panics >= 2 || backtrace::log_enabled();
+
+    let file = info.location.file;
+    let line = info.location.line;
+
+    let msg = match info.payload.downcast_ref::<&'static str>() {
         Some(s) => *s,
-        None => match obj.downcast_ref::<String>() {
+        None => match info.payload.downcast_ref::<String>() {
             Some(s) => &s[..],
             None => "Box<Any>",
         }
@@ -77,14 +206,25 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
     // debugger provides a useable stacktrace.
     if panics >= 3 {
         util::dumb_print(format_args!("thread panicked while processing \
-                                       panic. aborting."));
+                                       panic. aborting.\n"));
         unsafe { intrinsics::abort() }
     }
 
-    // If this is a double panic, make sure that we print a backtrace
-    // for this panic. Otherwise only print it if logging is enabled.
-    let log_backtrace = panics >= 2 || backtrace::log_enabled();
-    log_panic(obj, file, line, log_backtrace);
+    let info = PanicInfo {
+        payload: obj,
+        location: Location {
+            file: file,
+            line: line,
+        },
+    };
+
+    unsafe {
+        let _lock = HANDLER_LOCK.read();
+        match HANDLER {
+            Handler::Default => default_handler(&info),
+            Handler::Custom(ptr) => (*ptr)(&info),
+        }
+    }
 
     if panics >= 2 {
         // If a thread panics while it's already unwinding then we
@@ -92,7 +232,7 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
         // just abort. In the future we may consider resuming
         // unwinding or otherwise exiting the thread cleanly.
         util::dumb_print(format_args!("thread panicked while panicking. \
-                                       aborting."));
+                                       aborting.\n"));
         unsafe { intrinsics::abort() }
     }
 }
index d0b9cc4c4602fb9d3acc78d4480e5b9a5b5a6dd0..e398a5a28c9043de5c46fc4a859912d03b56c91e 100644 (file)
 #![stable(feature = "rust1", since = "1.0.0")]
 
 use ascii::*;
+#[allow(deprecated)]
 use borrow::{Borrow, IntoCow, ToOwned, Cow};
 use cmp;
+use error::Error;
 use fmt;
 use fs;
 use hash::{Hash, Hasher};
@@ -266,27 +268,33 @@ mod platform {
 pub enum Prefix<'a> {
     /// Prefix `\\?\`, together with the given component immediately following it.
     #[stable(feature = "rust1", since = "1.0.0")]
-    Verbatim(&'a OsStr),
+    Verbatim(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] &'a OsStr),
 
     /// Prefix `\\?\UNC\`, with the "server" and "share" components following it.
     #[stable(feature = "rust1", since = "1.0.0")]
-    VerbatimUNC(&'a OsStr, &'a OsStr),
+    VerbatimUNC(
+        #[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] &'a OsStr,
+        #[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] &'a OsStr,
+    ),
 
     /// Prefix like `\\?\C:\`, for the given drive letter
     #[stable(feature = "rust1", since = "1.0.0")]
-    VerbatimDisk(u8),
+    VerbatimDisk(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] u8),
 
     /// Prefix `\\.\`, together with the given component immediately following it.
     #[stable(feature = "rust1", since = "1.0.0")]
-    DeviceNS(&'a OsStr),
+    DeviceNS(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] &'a OsStr),
 
     /// Prefix `\\server\share`, with the given "server" and "share" components.
     #[stable(feature = "rust1", since = "1.0.0")]
-    UNC(&'a OsStr, &'a OsStr),
+    UNC(
+        #[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] &'a OsStr,
+        #[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] &'a OsStr,
+    ),
 
     /// Prefix `C:` for the given disk drive.
     #[stable(feature = "rust1", since = "1.0.0")]
-    Disk(u8),
+    Disk(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] u8),
 }
 
 impl<'a> Prefix<'a> {
@@ -528,7 +536,9 @@ pub enum Component<'a> {
     ///
     /// Does not occur on Unix.
     #[stable(feature = "rust1", since = "1.0.0")]
-    Prefix(PrefixComponent<'a>),
+    Prefix(
+        #[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] PrefixComponent<'a>
+    ),
 
     /// The root directory component, appears after any prefix and before anything else
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -544,7 +554,7 @@ pub enum Component<'a> {
 
     /// A normal component, i.e. `a` and `b` in `a/b`
     #[stable(feature = "rust1", since = "1.0.0")]
-    Normal(&'a OsStr),
+    Normal(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] &'a OsStr),
 }
 
 impl<'a> Component<'a> {
@@ -1035,6 +1045,7 @@ 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);
@@ -1211,6 +1222,7 @@ impl Borrow<Path> for PathBuf {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
+#[allow(deprecated)]
 impl IntoCow<'static, Path> for PathBuf {
     fn into_cow(self) -> Cow<'static, Path> {
         Cow::Owned(self)
@@ -1218,6 +1230,7 @@ impl IntoCow<'static, Path> for PathBuf {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
+#[allow(deprecated)]
 impl<'a> IntoCow<'a, Path> for &'a Path {
     fn into_cow(self) -> Cow<'a, Path> {
         Cow::Borrowed(self)
@@ -1320,6 +1333,12 @@ pub struct Path {
     inner: OsStr,
 }
 
+/// An error returned from the `Path::strip_prefix` method indicating that the
+/// prefix was not found in `self`.
+#[derive(Debug, Clone, PartialEq, Eq)]
+#[stable(since = "1.7.0", feature = "strip_prefix")]
+pub struct StripPrefixError(());
+
 impl Path {
     // The following (private!) function allows construction of a path from a u8
     // slice, which is only safe when it is known to follow the OsStr encoding.
@@ -1439,6 +1458,7 @@ impl Path {
     /// assert!(!Path::new("foo.txt").is_absolute());
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
+    #[allow(deprecated)]
     pub fn is_absolute(&self) -> bool {
         self.has_root() && (cfg!(unix) || self.prefix().is_some())
     }
@@ -1465,6 +1485,8 @@ impl Path {
     #[unstable(feature = "path_prefix",
                reason = "uncertain whether to expose this convenience",
                issue = "27722")]
+    #[rustc_deprecated(since = "1.7.0",
+                       reason = "inspect components().next() instead")]
     pub fn prefix(&self) -> Option<Prefix> {
         self.components().prefix
     }
@@ -1553,12 +1575,28 @@ impl Path {
     /// returns false), then `relative_from` returns `None`.
     #[unstable(feature = "path_relative_from", reason = "see #23284",
                issue = "23284")]
+    #[rustc_deprecated(since = "1.7.0", reason = "renamed to strip_prefix")]
     pub fn relative_from<'a, P: ?Sized + AsRef<Path>>(&'a self, base: &'a P) -> Option<&Path> {
-        self._relative_from(base.as_ref())
+        self._strip_prefix(base.as_ref()).ok()
+    }
+
+    /// Returns a path that, when joined onto `base`, yields `self`.
+    ///
+    /// If `base` is not a prefix of `self` (i.e. `starts_with`
+    /// returns false), then `relative_from` returns `None`.
+    #[stable(since = "1.7.0", feature = "path_strip_prefix")]
+    pub fn strip_prefix<'a, P: ?Sized>(&'a self, base: &'a P)
+                                       -> Result<&'a Path, StripPrefixError>
+        where P: AsRef<Path>
+    {
+        self._strip_prefix(base.as_ref())
     }
 
-    fn _relative_from<'a>(&'a self, base: &'a Path) -> Option<&'a Path> {
-        iter_after(self.components(), base.components()).map(|c| c.as_path())
+    fn _strip_prefix<'a>(&'a self, base: &'a Path)
+                         -> Result<&'a Path, StripPrefixError> {
+        iter_after(self.components(), base.components())
+            .map(|c| c.as_path())
+            .ok_or(StripPrefixError(()))
     }
 
     /// Determines whether `base` is a prefix of `self`.
@@ -2007,6 +2045,18 @@ impl_eq!(Cow<'a, Path>, Path);
 impl_eq!(Cow<'a, Path>, &'b Path);
 impl_eq!(Cow<'a, Path>, PathBuf);
 
+#[stable(since = "1.7.0", feature = "strip_prefix")]
+impl fmt::Display for StripPrefixError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.description().fmt(f)
+    }
+}
+
+#[stable(since = "1.7.0", feature = "strip_prefix")]
+impl Error for StripPrefixError {
+    fn description(&self) -> &str { "prefix not found" }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -2097,6 +2147,7 @@ mod tests {
     );
 
     #[test]
+    #[allow(deprecated)]
     fn into_cow() {
         use borrow::{Cow, IntoCow};
 
index bda4cdfb437334835d56084432a3cc612c155c3e..04568ad85bffea94096b82503f1b2e4356ec8d63 100644 (file)
@@ -43,7 +43,7 @@
 //!
 //! [`std::io::prelude`]: ../io/prelude/index.html
 //!
-//! The differece between 'the prelude' and these other preludes is that they
+//! The difference between 'the prelude' and these other preludes is that they
 //! are not automatically `use`'d, and must be imported manually. This is still
 //! easier than importing all of their consitutent components.
 //!
index afeb4231aba48136ae454e7059703ef59afd5468..ed59c51b0f0d843731995151cc84979d34eca6ac 100644 (file)
@@ -333,7 +333,7 @@ mod prim_slice { }
 /// let ptr = story.as_ptr();
 /// let len = story.len();
 ///
-/// // story has thirteen bytes
+/// // story has nineteen bytes
 /// assert_eq!(19, len);
 ///
 /// // We can re-build a str out of ptr and len. This is all unsafe becuase
index 91c3819307ffef652e1477421fb8ae60c385a0da..be1fe9b2a9bd7d27344379acf6638be59cbf88b3 100644 (file)
@@ -20,6 +20,7 @@ use ffi::OsStr;
 use fmt;
 use io::{self, Error, ErrorKind};
 use path;
+use str;
 use sys::pipe::{self, AnonPipe};
 use sys::process as imp;
 use sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
@@ -400,6 +401,32 @@ pub struct Output {
     pub stderr: Vec<u8>,
 }
 
+// If either stderr or stdout are valid utf8 strings it prints the valid
+// strings, otherwise it prints the byte sequence instead
+#[stable(feature = "process_output_debug", since = "1.7.0")]
+impl fmt::Debug for Output {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+
+        let stdout_utf8 = str::from_utf8(&self.stdout);
+        let stdout_debug: &fmt::Debug = match stdout_utf8 {
+            Ok(ref str) => str,
+            Err(_) => &self.stdout
+        };
+
+        let stderr_utf8 = str::from_utf8(&self.stderr);
+        let stderr_debug: &fmt::Debug = match stderr_utf8 {
+            Ok(ref str) => str,
+            Err(_) => &self.stderr
+        };
+
+        fmt.debug_struct("Output")
+            .field("status", &self.status)
+            .field("stdout", stdout_debug)
+            .field("stderr", stderr_debug)
+            .finish()
+    }
+}
+
 /// Describes what to do with a standard I/O stream for a child process.
 #[stable(feature = "process", since = "1.0.0")]
 pub struct Stdio(StdioImp);
index 7a4a0f96b3090caba206ab827eede372d3a830c9..21e60420c186adb48a19058884006bcd763f2455 100644 (file)
@@ -47,6 +47,7 @@
 //!     if the entropy pool is very small, such as immediately after first booting.
 //!     Linux 3.17 added the `getrandom(2)` system call which solves the issue: it blocks if entropy
 //!     pool is not initialized yet, but it does not block once initialized.
+//!     `getrandom(2)` was based on `getentropy(2)`, an existing system call in OpenBSD.
 //!     `OsRng` tries to use `getrandom(2)` if available, and use `/dev/urandom` fallback if not.
 //!     If an application does not have `getrandom` and likely to be run soon after first booting,
 //!     or on a system with very few entropy sources, one should consider using `/dev/random` via
index d1bd013e67b792a8be8cef92cbe7a46c297cd935..619f100f1a137ad9092498d19a68306915742c04 100644 (file)
@@ -13,7 +13,7 @@
 
 pub use self::imp::OsRng;
 
-#[cfg(all(unix, not(target_os = "ios")))]
+#[cfg(all(unix, not(target_os = "ios"), not(target_os = "openbsd")))]
 mod imp {
     use self::OsRngInner::*;
 
@@ -30,23 +30,24 @@ mod imp {
                   target_arch = "x86",
                   target_arch = "arm",
                   target_arch = "aarch64",
-                  target_arch = "powerpc")))]
+                  target_arch = "powerpc",
+                  target_arch = "powerpc64",
+                  target_arch = "powerpc64le")))]
     fn getrandom(buf: &mut [u8]) -> libc::c_long {
-        extern "C" {
-            fn syscall(number: libc::c_long, ...) -> libc::c_long;
-        }
-
         #[cfg(target_arch = "x86_64")]
         const NR_GETRANDOM: libc::c_long = 318;
         #[cfg(target_arch = "x86")]
         const NR_GETRANDOM: libc::c_long = 355;
-        #[cfg(any(target_arch = "arm", target_arch = "powerpc"))]
+        #[cfg(target_arch = "arm")]
         const NR_GETRANDOM: libc::c_long = 384;
-        #[cfg(any(target_arch = "aarch64"))]
+        #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64",
+                  target_arch = "powerpc64le"))]
+        const NR_GETRANDOM: libc::c_long = 359;
+        #[cfg(target_arch = "aarch64")]
         const NR_GETRANDOM: libc::c_long = 278;
 
         unsafe {
-            syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0)
+            libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0)
         }
     }
 
@@ -55,13 +56,14 @@ mod imp {
                       target_arch = "x86",
                       target_arch = "arm",
                       target_arch = "aarch64",
-                      target_arch = "powerpc"))))]
+                      target_arch = "powerpc",
+                      target_arch = "powerpc64",
+                      target_arch = "powerpc64le"))))]
     fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
 
     fn getrandom_fill_bytes(v: &mut [u8]) {
         let mut read = 0;
-        let len = v.len();
-        while read < len {
+        while read < v.len() {
             let result = getrandom(&mut v[read..]);
             if result == -1 {
                 let err = errno() as libc::c_int;
@@ -93,7 +95,9 @@ mod imp {
                   target_arch = "x86",
                   target_arch = "arm",
                   target_arch = "aarch64",
-                  target_arch = "powerpc")))]
+                  target_arch = "powerpc",
+                  target_arch = "powerpc64",
+                  target_arch = "powerpc64le")))]
     fn is_getrandom_available() -> bool {
         use sync::atomic::{AtomicBool, Ordering};
         use sync::Once;
@@ -121,7 +125,9 @@ mod imp {
                       target_arch = "x86",
                       target_arch = "arm",
                       target_arch = "aarch64",
-                      target_arch = "powerpc"))))]
+                      target_arch = "powerpc",
+                      target_arch = "powerpc64",
+                      target_arch = "powerpc64le"))))]
     fn is_getrandom_available() -> bool { false }
 
     /// A random number generator that retrieves randomness straight from
@@ -132,6 +138,7 @@ mod imp {
     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
     ///   service provider with the `PROV_RSA_FULL` type.
     /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
+    /// - OpenBSD: uses the `getentropy(2)` system call.
     ///
     /// This does not block.
     pub struct OsRng {
@@ -179,10 +186,65 @@ mod imp {
     }
 }
 
-#[cfg(target_os = "ios")]
+#[cfg(target_os = "openbsd")]
 mod imp {
-    #[cfg(stage0)] use prelude::v1::*;
+    use io;
+    use libc;
+    use mem;
+    use sys::os::errno;
+    use rand::Rng;
+
+    /// A random number generator that retrieves randomness straight from
+    /// the operating system. Platform sources:
+    ///
+    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
+    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
+    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
+    ///   service provider with the `PROV_RSA_FULL` type.
+    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
+    /// - OpenBSD: uses the `getentropy(2)` system call.
+    ///
+    /// This does not block.
+    pub struct OsRng {
+        // dummy field to ensure that this struct cannot be constructed outside
+        // of this module
+        _dummy: (),
+    }
+
+    impl OsRng {
+        /// Create a new `OsRng`.
+        pub fn new() -> io::Result<OsRng> {
+            Ok(OsRng { _dummy: () })
+        }
+    }
+
+    impl Rng for OsRng {
+        fn next_u32(&mut self) -> u32 {
+            let mut v = [0; 4];
+            self.fill_bytes(&mut v);
+            unsafe { mem::transmute(v) }
+        }
+        fn next_u64(&mut self) -> u64 {
+            let mut v = [0; 8];
+            self.fill_bytes(&mut v);
+            unsafe { mem::transmute(v) }
+        }
+        fn fill_bytes(&mut self, v: &mut [u8]) {
+            // getentropy(2) permits a maximum buffer size of 256 bytes
+            for s in v.chunks_mut(256) {
+                let ret = unsafe {
+                    libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
+                };
+                if ret == -1 {
+                    panic!("unexpected getentropy error: {}", errno());
+                }
+            }
+        }
+    }
+}
 
+#[cfg(target_os = "ios")]
+mod imp {
     use io;
     use mem;
     use ptr;
@@ -197,6 +259,7 @@ mod imp {
     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
     ///   service provider with the `PROV_RSA_FULL` type.
     /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
+    /// - OpenBSD: uses the `getentropy(2)` system call.
     ///
     /// This does not block.
     pub struct OsRng {
@@ -262,6 +325,7 @@ mod imp {
     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
     ///   service provider with the `PROV_RSA_FULL` type.
     /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
+    /// - OpenBSD: uses the `getentropy(2)` system call.
     ///
     /// This does not block.
     pub struct OsRng {
@@ -324,8 +388,6 @@ mod imp {
 
 #[cfg(test)]
 mod tests {
-    use prelude::v1::*;
-
     use sync::mpsc::channel;
     use rand::Rng;
     use super::OsRng;
index 36adf44cd3a3ea2913dbc72980fc97167641b677..08bc809ed4d4632ac29dc8f5422487ce2dacfa1a 100644 (file)
@@ -63,8 +63,6 @@ impl<R: Read> Rng for ReaderRng<R> {
 
 #[cfg(test)]
 mod tests {
-    use prelude::v1::*;
-
     use super::ReaderRng;
     use rand::Rng;
 
index 44961611b7bd78bf2e10c4e861de556ed2342666..fcd827e2a8b722e9b4e18d17299d90cf12192ee0 100644 (file)
             issue = "0")]
 #![doc(hidden)]
 
-use borrow::ToOwned;
-use mem;
-use sys;
-use sys_common::thread_info::{self, NewThread};
-use sys_common;
-use thread::{self, Thread};
+
 
 // Reexport some of our utilities which are expected by other crates.
 pub use sys_common::unwind::{begin_unwind, begin_unwind_fmt};
@@ -40,6 +35,14 @@ pub use sys_common::unwind::imp::eh_frame_registry::*;
 #[cfg(not(test))]
 #[lang = "start"]
 fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
+    use borrow::ToOwned;
+    use mem;
+    use panic;
+    use sys;
+    use sys_common;
+    use sys_common::thread_info::{self, NewThread};
+    use thread::Thread;
+
     sys::init();
 
     let failed = unsafe {
@@ -57,7 +60,7 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
         sys_common::args::init(argc, argv);
 
         // Let's run some code!
-        let res = thread::catch_panic(mem::transmute::<_, fn()>(main));
+        let res = panic::recover(mem::transmute::<_, fn()>(main));
         sys_common::cleanup();
         res.is_err()
     };
index 8383b3ec18972e0e0d3296cd542dff1ad352c940..9b1046f39a7b34d8ee5de98c2dcb681248bfec38 100644 (file)
 //! the standard library This varies per-platform, but these libraries are
 //! necessary for running libstd.
 
-// A few small shims in C that haven't been translated to Rust yet
-#[cfg(all(not(test), not(windows)))]
-#[link(name = "rust_builtin", kind = "static")]
-extern {}
-
 // LLVM implements the `frem` instruction as a call to `fmod`, which lives in
 // libm. Hence, we must explicitly link to it.
 //
index d817a261f7c9493c53a2d20f361942a65d689b68..1f7fe820bf86a37c7edd5dbdd240afabce4df954 100644 (file)
@@ -510,15 +510,15 @@ mod tests {
         static M: StaticMutex = StaticMutex::new();
 
         let g = M.lock().unwrap();
-        let (g, _no_timeout) = C.wait_timeout_ms(g, 1).unwrap();
+        let (g, _no_timeout) = C.wait_timeout(g, Duration::from_millis(1)).unwrap();
         // spurious wakeups mean this isn't necessarily true
         // assert!(!no_timeout);
         let _t = thread::spawn(move || {
             let _g = M.lock().unwrap();
             C.notify_one();
         });
-        let (g, no_timeout) = C.wait_timeout_ms(g, u32::MAX).unwrap();
-        assert!(no_timeout);
+        let (g, timeout_res) = C.wait_timeout(g, Duration::from_millis(u32::MAX as u64)).unwrap();
+        assert!(!timeout_res.timed_out());
         drop(g);
         unsafe { C.destroy(); M.destroy(); }
     }
index e1b7930b6d82efdadf80053489835e7f56fce6ce..9c9aa20eff5c0e292b95a6a368e8c63f9bceaaf1 100644 (file)
@@ -39,6 +39,7 @@ pub use self::rwlock::{RwLockReadGuard, RwLockWriteGuard};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::rwlock::{RwLock, StaticRwLock, RW_LOCK_INIT};
 #[stable(feature = "rust1", since = "1.0.0")]
+#[allow(deprecated)]
 pub use self::semaphore::{Semaphore, SemaphoreGuard};
 
 pub mod mpsc;
index e87ae19c583f761c5fd3e792707e0d29f889530f..3eb5db09bc0728332e045c38a829b91ef7b6e843 100644 (file)
@@ -385,12 +385,12 @@ pub enum TrySendError<T> {
     /// this is not a buffered channel, then there is no receiver available to
     /// acquire the data.
     #[stable(feature = "rust1", since = "1.0.0")]
-    Full(T),
+    Full(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] T),
 
     /// This channel's receiving half has disconnected, so the data could not be
     /// sent. The data is returned back to the callee in this case.
     #[stable(feature = "rust1", since = "1.0.0")]
-    Disconnected(T),
+    Disconnected(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] T),
 }
 
 enum Flavor<T> {
index 891f8775ff236d2eebfea67ee7b46ad2ad7d2583..8f08c840c218bca20e55765f5138924e90e24439 100644 (file)
             reason = "the interaction between semaphores and the acquisition/release \
                       of resources is currently unclear",
             issue = "27798")]
+#![rustc_deprecated(since = "1.7.0",
+                    reason = "easily confused with system sempahores and not \
+                              used enough to pull its weight")]
+#![allow(deprecated)]
 
 use ops::Drop;
 use sync::{Mutex, Condvar};
index 151d853fc9f7e033576df32ea0a093db9a0412c5..9f2f0df3a64705577537e4eaf90e7249924baa13 100644 (file)
@@ -133,7 +133,7 @@ mod tests {
         b.iter(|| {
             let mut lr = repeat(1).take(10000000);
             let mut vec = Vec::with_capacity(1024);
-            unsafe { read_to_end_uninitialized(&mut lr, &mut vec) };
+            unsafe { read_to_end_uninitialized(&mut lr, &mut vec) }
         });
     }
 }
index 75bb11216e1a2ad7bbd61e70724370494ad80548..77d1eed96231df0626e517e679c4657e59ab03c6 100644 (file)
@@ -83,7 +83,8 @@ pub const unwinder_private_data_size: usize = 2;
 #[cfg(any(target_arch = "mips", target_arch = "mipsel"))]
 pub const unwinder_private_data_size: usize = 2;
 
-#[cfg(target_arch = "powerpc")]
+#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64",
+          target_arch = "powerpc64le"))]
 pub const unwinder_private_data_size: usize = 2;
 
 #[repr(C)]
@@ -131,8 +132,6 @@ extern "C" {
 
     pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception);
 
-    // remove cfg after new snapshot
-    #[cfg(not(all(stage0, target_os="windows", target_arch="x86_64")))]
     #[unwind]
     pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
 }
index 446a4445b2d3a568a07068acb8756ab3fdc90542..2cfa04c843b6b55fc800a220d17966776f6c6825 100644 (file)
@@ -71,7 +71,7 @@ pub enum TryLockError<T> {
     /// The lock could not be acquired because another thread failed while holding
     /// the lock.
     #[stable(feature = "rust1", since = "1.0.0")]
-    Poisoned(PoisonError<T>),
+    Poisoned(#[cfg_attr(not(stage0), stable(feature = "rust1", since = "1.0.0"))] PoisonError<T>),
     /// The lock could not be acquired at this time because the operation would
     /// otherwise block.
     #[stable(feature = "rust1", since = "1.0.0")]
index f3f21e47a14066d76c37c1f0342b8a601f10faf7..31caa68c4b7ea70bcd89871e305a68076738eebf 100644 (file)
@@ -167,7 +167,6 @@ mod tests {
     use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
     use cell::RefCell;
     use sync::Arc;
-    use boxed;
     use thread;
 
     #[test]
@@ -208,13 +207,13 @@ mod tests {
     fn trylock_works() {
         let m = Arc::new(ReentrantMutex::new(()));
         let m2 = m.clone();
-        let lock = m.try_lock().unwrap();
-        let lock2 = m.try_lock().unwrap();
+        let _lock = m.try_lock().unwrap();
+        let _lock2 = m.try_lock().unwrap();
         thread::spawn(move || {
             let lock = m2.try_lock();
             assert!(lock.is_err());
         }).join().unwrap();
-        let lock3 = m.try_lock().unwrap();
+        let _lock3 = m.try_lock().unwrap();
     }
 
     pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
@@ -233,9 +232,8 @@ mod tests {
             *lock.borrow_mut() = 1;
             let lock2 = mc.lock().unwrap();
             *lock.borrow_mut() = 2;
-            let answer = Answer(lock2);
+            let _answer = Answer(lock2);
             panic!("What the answer to my lifetimes dilemma is?");
-            drop(answer);
         }).join();
         assert!(result.is_err());
         let r = m.lock().err().unwrap().into_inner();
index aea5acc90717645412a179590b990eb176356a08..666b2ed72adcec2ae7bfe58078c636397f2ac67f 100644 (file)
@@ -88,18 +88,8 @@ use sys_common::mutex::Mutex;
 #[path = "seh.rs"] #[doc(hidden)]
 pub mod imp;
 
-// stage0: i686-pc-windows-gnu
-#[cfg(all(stage0, windows, target_arch = "x86_64", target_env = "gnu"))]
-#[path = "seh64_gnu.rs"] #[doc(hidden)]
-pub mod imp;
-
-// stage0: x86_64-pc-windows-msvc
-#[cfg(all(stage0, windows, target_arch = "x86_64", target_env = "msvc"))]
-#[path = "seh.rs"] #[doc(hidden)]
-pub mod imp;
-
 // x86_64-pc-windows-*
-#[cfg(all(not(stage0), windows, target_arch = "x86_64"))]
+#[cfg(all(windows, target_arch = "x86_64"))]
 #[path = "seh64_gnu.rs"] #[doc(hidden)]
 pub mod imp;
 
@@ -182,7 +172,7 @@ pub fn panicking() -> bool {
 #[inline(never)]
 #[no_mangle]
 #[allow(private_no_mangle_fns)]
-fn rust_panic(cause: Box<Any + Send + 'static>) -> ! {
+pub fn rust_panic(cause: Box<Any + Send + 'static>) -> ! {
     unsafe {
         imp::panic(cause)
     }
index 979f1f486698358f25a6e902877e097c56f874f2..b7a6b7650d54079de57495659ba7684dc697264a 100644 (file)
@@ -35,12 +35,12 @@ pub fn dumb_print(args: fmt::Arguments) {
 }
 
 pub fn abort(args: fmt::Arguments) -> ! {
-    dumb_print(format_args!("fatal runtime error: {}", args));
+    dumb_print(format_args!("fatal runtime error: {}\n", args));
     unsafe { intrinsics::abort(); }
 }
 
 #[allow(dead_code)] // stack overflow detection not enabled on all platforms
 pub unsafe fn report_overflow() {
-    dumb_print(format_args!("\nthread '{}' has overflowed its stack",
+    dumb_print(format_args!("\nthread '{}' has overflowed its stack\n",
                             thread::current().name().unwrap_or("<unknown>")));
 }
index 2e092d5f770c52c250ad4c52b9214fedd79b98e9..bc997af3a27e46ae2e8edd61d0b74876652bac28 100644 (file)
@@ -1067,7 +1067,7 @@ mod tests {
     #[test]
     fn wtf8buf_show_str() {
         let text = "a\té 💩\r";
-        let mut string = Wtf8Buf::from_str(text);
+        let string = Wtf8Buf::from_str(text);
         assert_eq!(format!("{:?}", text), format!("{:?}", string));
     }
 
index 16e1578296dfe99dfddee4a5ebefa636e968294c..8d21ba8f661c80f53c5e8d694b8f919c77c11ea2 100644 (file)
@@ -23,42 +23,61 @@ use sys;
 use sys_common::{FromInner, AsInner, AsInnerMut};
 
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const USER_READ: raw::mode_t = 0o400;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const USER_WRITE: raw::mode_t = 0o200;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const USER_EXECUTE: raw::mode_t = 0o100;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const USER_RWX: raw::mode_t = 0o700;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const GROUP_READ: raw::mode_t = 0o040;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const GROUP_WRITE: raw::mode_t = 0o020;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const GROUP_EXECUTE: raw::mode_t = 0o010;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const GROUP_RWX: raw::mode_t = 0o070;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const OTHER_READ: raw::mode_t = 0o004;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const OTHER_WRITE: raw::mode_t = 0o002;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const OTHER_EXECUTE: raw::mode_t = 0o001;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const OTHER_RWX: raw::mode_t = 0o007;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const ALL_READ: raw::mode_t = 0o444;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const ALL_WRITE: raw::mode_t = 0o222;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const ALL_EXECUTE: raw::mode_t = 0o111;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const ALL_RWX: raw::mode_t = 0o777;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const SETUID: raw::mode_t = 0o4000;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const SETGID: raw::mode_t = 0o2000;
 #[unstable(feature = "fs_mode", reason = "recently added API", issue = "27712")]
+#[rustc_deprecated(since = "1.7.0", reason = "moved to the libc crate instead")]
 pub const STICKY_BIT: raw::mode_t = 0o1000;
 
 /// Unix-specific extensions to `Permissions`
index 52ac37c6e334da4194ebbe0e3c1a1bb41110c0f8..4163ede46afedbd4cb3a150a85465dddd056aba4 100644 (file)
@@ -33,7 +33,7 @@ pub trait AsRawFd {
     /// Extracts the raw file descriptor.
     ///
     /// This method does **not** pass ownership of the raw file descriptor
-    /// to the caller. The descriptor is only guarantee to be valid while
+    /// to the caller. The descriptor is only guaranteed to be valid while
     /// the original object has not yet been destroyed.
     #[stable(feature = "rust1", since = "1.0.0")]
     fn as_raw_fd(&self) -> RawFd;
index d42c15a97ac8c30f43b09d430e68c9158c6a1d39..e09f4ca27bca604efdfa0f2760428529d3b14969 100644 (file)
@@ -60,8 +60,8 @@ impl FileDesc {
     #[cfg(target_env = "newlib")]
     pub fn set_cloexec(&self) {
         unsafe {
-            let previous = libc::fnctl(self.fd, libc::F_GETFD);
-            let ret = libc::fnctl(self.fd, libc::F_SETFD, previous | libc::FD_CLOEXEC);
+            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);
         }
     }
index 8ea8f0c6c771b4fb2b4ca01c5eff7c54a74bdfb1..10fda3fcd7fa5165ce82e70ea0fab45d8517559e 100644 (file)
@@ -14,7 +14,8 @@ use os::unix::prelude::*;
 use ffi::{CString, CStr, OsString, OsStr};
 use fmt;
 use io::{self, Error, ErrorKind, SeekFrom};
-use libc::{self, c_int, size_t, off_t, c_char, mode_t};
+use libc::{dirent, readdir_r};
+use libc::{self, c_int, off_t, mode_t};
 use mem;
 use path::{Path, PathBuf};
 use ptr;
@@ -43,7 +44,7 @@ unsafe impl Send for Dir {}
 unsafe impl Sync for Dir {}
 
 pub struct DirEntry {
-    buf: Vec<u8>, // actually *mut libc::dirent
+    entry: dirent,
     root: Arc<PathBuf>,
 }
 
@@ -126,32 +127,22 @@ impl Iterator for ReadDir {
     type Item = io::Result<DirEntry>;
 
     fn next(&mut self) -> Option<io::Result<DirEntry>> {
-        extern {
-            fn rust_dirent_t_size() -> c_int;
-        }
-
-        let mut buf: Vec<u8> = Vec::with_capacity(unsafe {
-            rust_dirent_t_size() as usize
-        });
-        let ptr = buf.as_mut_ptr() as *mut libc::dirent;
-
-        let mut entry_ptr = ptr::null_mut();
-        loop {
-            if unsafe { libc::readdir_r(self.dirp.0, ptr, &mut entry_ptr) != 0 } {
-                return Some(Err(Error::last_os_error()))
-            }
-            if entry_ptr.is_null() {
-                return None
-            }
-
-            let entry = DirEntry {
-                buf: buf,
+        unsafe {
+            let mut ret = DirEntry {
+                entry: mem::zeroed(),
                 root: self.root.clone()
             };
-            if entry.name_bytes() == b"." || entry.name_bytes() == b".." {
-                buf = entry.buf;
-            } else {
-                return Some(Ok(entry))
+            let mut entry_ptr = ptr::null_mut();
+            loop {
+                if readdir_r(self.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
+                    return Some(Err(Error::last_os_error()))
+                }
+                if entry_ptr.is_null() {
+                    return None
+                }
+                if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
+                    return Some(Ok(ret))
+                }
             }
         }
     }
@@ -166,7 +157,7 @@ impl Drop for Dir {
 
 impl DirEntry {
     pub fn path(&self) -> PathBuf {
-        self.root.join(<OsStr as OsStrExt>::from_bytes(self.name_bytes()))
+        self.root.join(OsStr::from_bytes(self.name_bytes()))
     }
 
     pub fn file_name(&self) -> OsString {
@@ -178,35 +169,64 @@ impl DirEntry {
     }
 
     pub fn file_type(&self) -> io::Result<FileType> {
-        extern {
-            fn rust_dir_get_mode(ptr: *mut libc::dirent) -> c_int;
-        }
-        unsafe {
-            match rust_dir_get_mode(self.dirent()) {
-                -1 => lstat(&self.path()).map(|m| m.file_type()),
-                n => Ok(FileType { mode: n as mode_t }),
-            }
+        match self.entry.d_type {
+            libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }),
+            libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }),
+            libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }),
+            libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }),
+            libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }),
+            libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }),
+            libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }),
+            _ => lstat(&self.path()).map(|m| m.file_type()),
         }
     }
 
+    #[cfg(any(target_os = "macos",
+              target_os = "ios",
+              target_os = "linux"))]
     pub fn ino(&self) -> raw::ino_t {
-        extern {
-            fn rust_dir_get_ino(ptr: *mut libc::dirent) -> raw::ino_t;
-        }
-        unsafe { rust_dir_get_ino(self.dirent()) }
+        self.entry.d_ino
+    }
+
+    #[cfg(target_os = "android")]
+    pub fn ino(&self) -> raw::ino_t {
+        self.entry.d_ino as raw::ino_t
+    }
+
+    #[cfg(any(target_os = "freebsd",
+              target_os = "openbsd",
+              target_os = "bitrig",
+              target_os = "netbsd",
+              target_os = "dragonfly"))]
+    pub fn ino(&self) -> raw::ino_t {
+        self.entry.d_fileno
     }
 
+    #[cfg(any(target_os = "macos",
+              target_os = "ios",
+              target_os = "netbsd",
+              target_os = "openbsd"))]
     fn name_bytes(&self) -> &[u8] {
-        extern {
-            fn rust_list_dir_val(ptr: *mut libc::dirent) -> *const c_char;
+        unsafe {
+            ::slice::from_raw_parts(self.entry.d_name.as_ptr() as *const u8,
+                                    self.entry.d_namlen as usize)
         }
+    }
+    #[cfg(any(target_os = "freebsd",
+              target_os = "dragonfly",
+              target_os = "bitrig"))]
+    fn name_bytes(&self) -> &[u8] {
         unsafe {
-            CStr::from_ptr(rust_list_dir_val(self.dirent())).to_bytes()
+            ::slice::from_raw_parts(self.entry.d_name.as_ptr() as *const u8,
+                                    self.entry.d_namelen as usize)
         }
     }
-
-    fn dirent(&self) -> *mut libc::dirent {
-        self.buf.as_ptr() as *mut _
+    #[cfg(any(target_os = "android",
+              target_os = "linux"))]
+    fn name_bytes(&self) -> &[u8] {
+        unsafe {
+            CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes()
+        }
     }
 }
 
index 68b0c3d6b0ee7392ce9d81919e3f1bd1e4d6c0e5..9771b057d8d21f5bfac77a38cbb71b5130e5c899 100644 (file)
@@ -15,6 +15,7 @@ use io::{self, ErrorKind};
 use libc;
 use num::One;
 use ops::Neg;
+use alloc::oom;
 
 #[cfg(target_os = "android")]   pub use os::android as platform;
 #[cfg(target_os = "bitrig")]    pub use os::bitrig as platform;
@@ -45,7 +46,23 @@ pub mod thread_local;
 pub mod time;
 pub mod stdio;
 
-#[cfg(not(target_os = "nacl"))]
+// A nicer handler for out-of-memory situations than the default one. This one
+// prints a message to stderr before aborting. It is critical that this code
+// does not allocate any memory since we are in an OOM situation. Any errors are
+// ignored while printing since there's nothing we can do about them and we are
+// about to exit anyways.
+fn oom_handler() -> ! {
+    use intrinsics;
+    let msg = "fatal runtime error: out of memory\n";
+    unsafe {
+        libc::write(libc::STDERR_FILENO,
+                    msg.as_ptr() as *const libc::c_void,
+                    msg.len() as libc::size_t);
+        intrinsics::abort();
+    }
+}
+
+#[cfg(not(any(target_os = "nacl", test)))]
 pub fn init() {
     use libc::signal;
     // By default, some platforms will send a *signal* when an EPIPE error
@@ -58,9 +75,14 @@ pub fn init() {
     unsafe {
         assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != !0);
     }
+
+    oom::set_oom_handler(oom_handler);
+}
+
+#[cfg(all(target_os = "nacl", not(test)))]
+pub fn init() {
+    oom::set_oom_handler(oom_handler);
 }
-#[cfg(target_os = "nacl")]
-pub fn init() { }
 
 pub fn decode_error_kind(errno: i32) -> ErrorKind {
     match errno as libc::c_int {
index c2bf0651cfff455a3250e4683a733180d5d43b71..c62960d74cb1ca290c4b1e74621a1cc93901457f 100644 (file)
@@ -22,6 +22,7 @@ use io;
 use iter;
 use libc::{self, c_int, c_char, c_void};
 use mem;
+use memchr;
 use path::{self, PathBuf};
 use ptr;
 use slice;
@@ -37,12 +38,17 @@ static ENV_LOCK: StaticMutex = StaticMutex::new();
 /// Returns the platform-specific value of errno
 pub fn errno() -> i32 {
     extern {
-        #[cfg_attr(any(target_os = "linux", target_os = "android"), link_name = "__errno_location")]
-        #[cfg_attr(any(target_os = "bitrig", target_os = "netbsd", target_os = "openbsd",
+        #[cfg_attr(any(target_os = "linux"), link_name = "__errno_location")]
+        #[cfg_attr(any(target_os = "bitrig",
+                       target_os = "netbsd",
+                       target_os = "openbsd",
+                       target_os = "android",
                        target_env = "newlib"),
                    link_name = "__errno")]
         #[cfg_attr(target_os = "dragonfly", link_name = "__dfly_error")]
-        #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "freebsd"),
+        #[cfg_attr(any(target_os = "macos",
+                       target_os = "ios",
+                       target_os = "freebsd"),
                    link_name = "__error")]
         fn errno_location() -> *const c_int;
     }
@@ -172,17 +178,19 @@ pub fn current_exe() -> io::Result<PathBuf> {
                        libc::KERN_PROC_PATHNAME as c_int,
                        -1 as c_int];
         let mut sz: libc::size_t = 0;
-        let err = libc::sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
-                               ptr::null_mut(), &mut sz, ptr::null_mut(),
-                               0 as libc::size_t);
-        if err != 0 { return Err(io::Error::last_os_error()); }
-        if sz == 0 { return Err(io::Error::last_os_error()); }
+        try!(cvt(libc::sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
+                              ptr::null_mut(), &mut sz, ptr::null_mut(),
+                              0 as libc::size_t)));
+        if sz == 0 {
+            return Err(io::Error::last_os_error())
+        }
         let mut v: Vec<u8> = Vec::with_capacity(sz as usize);
-        let err = libc::sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
-                               v.as_mut_ptr() as *mut libc::c_void, &mut sz,
-                               ptr::null_mut(), 0 as libc::size_t);
-        if err != 0 { return Err(io::Error::last_os_error()); }
-        if sz == 0 { return Err(io::Error::last_os_error()); }
+        try!(cvt(libc::sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
+                              v.as_mut_ptr() as *mut libc::c_void, &mut sz,
+                              ptr::null_mut(), 0 as libc::size_t)));
+        if sz == 0 {
+            return Err(io::Error::last_os_error());
+        }
         v.set_len(sz as usize - 1); // chop off trailing NUL
         Ok(PathBuf::from(OsString::from_vec(v)))
     }
@@ -200,22 +208,28 @@ pub fn current_exe() -> io::Result<PathBuf> {
 
 #[cfg(any(target_os = "bitrig", target_os = "openbsd"))]
 pub fn current_exe() -> io::Result<PathBuf> {
-    use sync::StaticMutex;
-    static LOCK: StaticMutex = StaticMutex::new();
-
-    extern {
-        fn rust_current_exe() -> *const c_char;
-    }
-
-    let _guard = LOCK.lock();
-
     unsafe {
-        let v = rust_current_exe();
-        if v.is_null() {
-            Err(io::Error::last_os_error())
+        let mut mib = [libc::CTL_KERN,
+                       libc::KERN_PROC_ARGS,
+                       libc::getpid(),
+                       libc::KERN_PROC_ARGV];
+        let mib = mib.as_mut_ptr();
+        let mut argv_len = 0;
+        try!(cvt(libc::sysctl(mib, 4, 0 as *mut _, &mut argv_len,
+                              0 as *mut _, 0)));
+        let mut argv = Vec::<*const libc::c_char>::with_capacity(argv_len as usize);
+        try!(cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _,
+                              &mut argv_len, 0 as *mut _, 0)));
+        argv.set_len(argv_len as usize);
+        if argv[0].is_null() {
+            return Err(io::Error::new(io::ErrorKind::Other,
+                                      "no current exe available"))
+        }
+        let argv0 = CStr::from_ptr(argv[0]).to_bytes();
+        if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') {
+            ::fs::canonicalize(OsStr::from_bytes(argv0))
         } else {
-            let vec = CStr::from_ptr(v).to_bytes().to_vec();
-            Ok(PathBuf::from(OsString::from_vec(vec)))
+            Ok(PathBuf::from(OsStr::from_bytes(argv0)))
         }
     }
 }
@@ -406,7 +420,7 @@ pub fn env() -> Env {
         if input.is_empty() {
             return None;
         }
-        let pos = input[1..].iter().position(|&b| b == b'=').map(|p| p + 1);
+        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
         pos.map(|p| (
             OsStringExt::from_vec(input[..p].to_vec()),
             OsStringExt::from_vec(input[p+1..].to_vec()),
index 407fcb0a1b8f176eecc3c945cddb79d251907141..bb9d37f93ab8b26b0217574a1cc40902703d4751 100644 (file)
@@ -288,6 +288,32 @@ impl Process {
             unsafe { libc::_exit(1) }
         }
 
+        // Make sure that the source descriptors are not an stdio descriptor,
+        // otherwise the order which we set the child's descriptors may blow
+        // away a descriptor which we are hoping to save. For example,
+        // suppose we want the child's stderr to be the parent's stdout, and
+        // the child's stdout to be the parent's stderr. No matter which we
+        // dup first, the second will get overwritten prematurely.
+        let maybe_migrate = |src: Stdio, output: &mut AnonPipe| {
+            match src {
+                Stdio::Raw(fd @ libc::STDIN_FILENO) |
+                Stdio::Raw(fd @ libc::STDOUT_FILENO) |
+                Stdio::Raw(fd @ libc::STDERR_FILENO) => {
+                    let fd = match cvt_r(|| libc::dup(fd)) {
+                        Ok(fd) => fd,
+                        Err(_) => fail(output),
+                    };
+                    let fd = FileDesc::new(fd);
+                    fd.set_cloexec();
+                    Stdio::Raw(fd.into_raw())
+                },
+
+                s @ Stdio::None |
+                s @ Stdio::Inherit |
+                s @ Stdio::Raw(_) => s,
+            }
+        };
+
         let setup = |src: Stdio, dst: c_int| {
             match src {
                 Stdio::Inherit => true,
@@ -313,6 +339,12 @@ impl Process {
             }
         };
 
+        // Make sure we migrate all source descriptors before
+        // we start overwriting them
+        let in_fd = maybe_migrate(in_fd, &mut output);
+        let out_fd = maybe_migrate(out_fd, &mut output);
+        let err_fd = maybe_migrate(err_fd, &mut output);
+
         if !setup(in_fd, libc::STDIN_FILENO) { fail(&mut output) }
         if !setup(out_fd, libc::STDOUT_FILENO) { fail(&mut output) }
         if !setup(err_fd, libc::STDERR_FILENO) { fail(&mut output) }
@@ -462,8 +494,7 @@ mod tests {
     use mem;
     use ptr;
     use libc;
-    use slice;
-    use sys::{self, cvt, pipe};
+    use sys::{self, cvt};
 
     macro_rules! t {
         ($e:expr) => {
@@ -482,6 +513,8 @@ mod tests {
 
     #[cfg(target_os = "android")]
     unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int {
+        use slice;
+
         let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::<libc::sigset_t>());
         let bit = (signum - 1) as usize;
         raw[bit / 8] |= 1 << (bit % 8);
index 9a7f98d24cd5df733617cc416f13f7223e1e7aff..fc49f4257be2ace4908917a38edd0278ce04b774 100644 (file)
@@ -7,11 +7,13 @@
 // <LICENSE-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(test, allow(dead_code))]
 
 use libc;
 use self::imp::{make_handler, drop_handler};
 
-pub use self::imp::{init, cleanup};
+pub use self::imp::cleanup;
+pub use self::imp::init;
 
 pub struct Handler {
     _data: *mut libc::c_void
@@ -40,12 +42,11 @@ impl Drop for Handler {
           target_os = "openbsd"))]
 mod imp {
     use super::Handler;
-    use sys_common::util::report_overflow;
     use mem;
     use ptr;
+    use libc::{sigaltstack, SIGSTKSZ};
     use libc::{sigaction, SIGBUS, SIG_DFL,
-               SA_SIGINFO, SA_ONSTACK, sigaltstack,
-               SIGSTKSZ, sighandler_t};
+               SA_SIGINFO, SA_ONSTACK, sighandler_t};
     use libc;
     use libc::{mmap, munmap};
     use libc::{SIGSEGV, PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON};
@@ -58,19 +59,19 @@ mod imp {
     static mut PAGE_SIZE: usize = 0;
 
     #[cfg(any(target_os = "linux", target_os = "android"))]
-    unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> *mut libc::c_void {
+    unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
         #[repr(C)]
         struct siginfo_t {
             a: [libc::c_int; 3], // si_signo, si_code, si_errno,
             si_addr: *mut libc::c_void,
         }
 
-        (*(info as *const siginfo_t)).si_addr
+        (*(info as *const siginfo_t)).si_addr as usize
     }
 
     #[cfg(not(any(target_os = "linux", target_os = "android")))]
-    unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> *mut libc::c_void {
-        (*info).si_addr
+    unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
+        (*info).si_addr as usize
     }
 
     // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
@@ -94,8 +95,10 @@ mod imp {
     unsafe extern fn signal_handler(signum: libc::c_int,
                                     info: *mut libc::siginfo_t,
                                     _data: *mut libc::c_void) {
+        use sys_common::util::report_overflow;
+
         let guard = thread_info::stack_guard().unwrap_or(0);
-        let addr = siginfo_si_addr(info) as usize;
+        let addr = siginfo_si_addr(info);
 
         // If the faulting address is within the guard page, then we print a
         // message saying so.
index 7ef504fba81e0d019fe893c2044a892cf0dcab19..34e32d0d5b62af67e27ef641967cc5514fb5ad88 100644 (file)
@@ -16,7 +16,10 @@ use os::raw::{c_int, c_uint, c_ulong, c_long, c_longlong, c_ushort};
 use os::raw::{c_char, c_short, c_ulonglong};
 use libc::{wchar_t, size_t, c_void};
 use ptr;
-use simd;
+
+#[repr(simd)]
+#[repr(C)]
+struct u64x2(u64, u64);
 
 pub use self::GET_FILEEX_INFO_LEVELS::*;
 pub use self::FILE_INFO_BY_HANDLE_CLASS::*;
@@ -783,7 +786,7 @@ pub struct FLOATING_SAVE_AREA {
 #[cfg(target_arch = "x86_64")]
 #[repr(C)]
 pub struct CONTEXT {
-    _align_hack: [simd::u64x2; 0], // FIXME align on 16-byte
+    _align_hack: [u64x2; 0], // FIXME align on 16-byte
     pub P1Home: DWORDLONG,
     pub P2Home: DWORDLONG,
     pub P3Home: DWORDLONG,
@@ -843,7 +846,7 @@ pub struct CONTEXT {
 #[cfg(target_arch = "x86_64")]
 #[repr(C)]
 pub struct M128A {
-    _align_hack: [simd::u64x2; 0], // FIXME align on 16-byte
+    _align_hack: [u64x2; 0], // FIXME align on 16-byte
     pub Low:  c_ulonglong,
     pub High: c_longlong
 }
@@ -851,7 +854,7 @@ pub struct M128A {
 #[cfg(target_arch = "x86_64")]
 #[repr(C)]
 pub struct FLOATING_SAVE_AREA {
-    _align_hack: [simd::u64x2; 0], // FIXME align on 16-byte
+    _align_hack: [u64x2; 0], // FIXME align on 16-byte
     _Dummy: [u8; 512] // FIXME: Fill this out
 }
 
index 7e5342a3fd4733d2308a60a7205d24e3fc0a5f18..16c4ae8257c132d7018e018fcab09c682f8f8db6 100644 (file)
@@ -20,6 +20,7 @@ use num::Zero;
 use os::windows::ffi::{OsStrExt, OsStringExt};
 use path::PathBuf;
 use time::Duration;
+use alloc::oom;
 
 #[macro_use] pub mod compat;
 
@@ -42,7 +43,26 @@ pub mod thread_local;
 pub mod time;
 pub mod stdio;
 
-pub fn init() {}
+// See comment in sys/unix/mod.rs
+fn oom_handler() -> ! {
+    use intrinsics;
+    use ptr;
+    let msg = "fatal runtime error: out of memory\n";
+    unsafe {
+        // WriteFile silently fails if it is passed an invalid handle, so there
+        // is no need to check the result of GetStdHandle.
+        c::WriteFile(c::GetStdHandle(c::STD_ERROR_HANDLE),
+                     msg.as_ptr() as c::LPVOID,
+                     msg.len() as c::DWORD,
+                     ptr::null_mut(),
+                     ptr::null_mut());
+        intrinsics::abort();
+    }
+}
+
+pub fn init() {
+    oom::set_oom_handler(oom_handler);
+}
 
 pub fn decode_error_kind(errno: i32) -> ErrorKind {
     match errno as c::DWORD {
index 119429cc584f762907d6586e60c524c6fe59eadf..ca0f10315625304313699bbf75ca67268cf68965 100644 (file)
 #![unstable(feature = "thread_local_internals", issue = "0")]
 
 use cell::UnsafeCell;
-
-// Sure wish we had macro hygiene, no?
-#[doc(hidden)]
-pub use self::imp::Key as __KeyInner;
+use mem;
 
 /// A thread local storage key which owns its contents.
 ///
@@ -60,41 +57,27 @@ pub use self::imp::Key as __KeyInner;
 /// });
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
-pub struct LocalKey<T:'static> {
-    // The key itself may be tagged with #[thread_local], and this `Key` is
-    // stored as a `static`, and it's not valid for a static to reference the
-    // address of another thread_local static. For this reason we kinda wonkily
-    // work around this by generating a shim function which will give us the
-    // address of the inner TLS key at runtime.
+pub struct LocalKey<T: 'static> {
+    // This outer `LocalKey<T>` type is what's going to be stored in statics,
+    // but actual data inside will sometimes be tagged with #[thread_local].
+    // It's not valid for a true static to reference a #[thread_local] static,
+    // so we get around that by exposing an accessor through a layer of function
+    // indirection (this thunk).
     //
-    // This is trivially devirtualizable by LLVM because we never store anything
-    // to this field and rustc can declare the `static` as constant as well.
-    inner: fn() -> &'static __KeyInner<T>,
+    // Note that the thunk is itself unsafe because the returned lifetime of the
+    // slot where data lives, `'static`, is not actually valid. The lifetime
+    // here is actually `'thread`!
+    //
+    // Although this is an extra layer of indirection, it should in theory be
+    // trivially devirtualizable by LLVM because the value of `inner` never
+    // changes and the constant should be readonly within a crate. This mainly
+    // only runs into problems when TLS statics are exported across crates.
+    inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>,
 
     // initialization routine to invoke to create a value
     init: fn() -> T,
 }
 
-// Macro pain #4586:
-//
-// When cross compiling, rustc will load plugins and macros from the *host*
-// platform before search for macros from the target platform. This is primarily
-// done to detect, for example, plugins. Ideally the macro below would be
-// defined once per module below, but unfortunately this means we have the
-// following situation:
-//
-// 1. We compile libstd for x86_64-unknown-linux-gnu, this thread_local!() macro
-//    will inject #[thread_local] statics.
-// 2. We then try to compile a program for arm-linux-androideabi
-// 3. The compiler has a host of linux and a target of android, so it loads
-//    macros from the *linux* libstd.
-// 4. The macro generates a #[thread_local] field, but the android libstd does
-//    not use #[thread_local]
-// 5. Compile error about structs with wrong fields.
-//
-// To get around this, we're forced to inject the #[cfg] logic into the macro
-// itself. Woohoo.
-
 /// Declare a new thread local storage key of type `std::thread::LocalKey`.
 ///
 /// See [LocalKey documentation](thread/struct.LocalKey.html) for more
@@ -102,36 +85,14 @@ pub struct LocalKey<T:'static> {
 #[macro_export]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[allow_internal_unstable]
-#[cfg(not(no_elf_tls))]
-macro_rules! thread_local {
-    (static $name:ident: $t:ty = $init:expr) => (
-        static $name: $crate::thread::LocalKey<$t> =
-            __thread_local_inner!($t, $init,
-                #[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
-                               not(target_arch = "aarch64")),
-                           thread_local)]);
-    );
-    (pub static $name:ident: $t:ty = $init:expr) => (
-        pub static $name: $crate::thread::LocalKey<$t> =
-            __thread_local_inner!($t, $init,
-                #[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
-                               not(target_arch = "aarch64")),
-                           thread_local)]);
-    );
-}
-
-#[macro_export]
-#[stable(feature = "rust1", since = "1.0.0")]
-#[allow_internal_unstable]
-#[cfg(no_elf_tls)]
 macro_rules! thread_local {
     (static $name:ident: $t:ty = $init:expr) => (
         static $name: $crate::thread::LocalKey<$t> =
-            __thread_local_inner!($t, $init, #[]);
+            __thread_local_inner!($t, $init);
     );
     (pub static $name:ident: $t:ty = $init:expr) => (
         pub static $name: $crate::thread::LocalKey<$t> =
-            __thread_local_inner!($t, $init, #[]);
+            __thread_local_inner!($t, $init);
     );
 }
 
@@ -142,12 +103,25 @@ macro_rules! thread_local {
 #[macro_export]
 #[allow_internal_unstable]
 macro_rules! __thread_local_inner {
-    ($t:ty, $init:expr, #[$($attr:meta),*]) => {{
-        $(#[$attr])*
-        static __KEY: $crate::thread::__LocalKeyInner<$t> =
-            $crate::thread::__LocalKeyInner::new();
+    ($t:ty, $init:expr) => {{
         fn __init() -> $t { $init }
-        fn __getit() -> &'static $crate::thread::__LocalKeyInner<$t> { &__KEY }
+
+        unsafe fn __getit() -> $crate::option::Option<
+            &'static $crate::cell::UnsafeCell<
+                $crate::option::Option<$t>>>
+        {
+            #[thread_local]
+            #[cfg(target_thread_local)]
+            static __KEY: $crate::thread::__ElfLocalKeyInner<$t> =
+                $crate::thread::__ElfLocalKeyInner::new();
+
+            #[cfg(not(target_thread_local))]
+            static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
+                $crate::thread::__OsLocalKeyInner::new();
+
+            __KEY.get()
+        }
+
         $crate::thread::LocalKey::new(__getit, __init)
     }}
 }
@@ -189,11 +163,11 @@ impl<T: 'static> LocalKey<T> {
     #[unstable(feature = "thread_local_internals",
                reason = "recently added to create a key",
                issue = "0")]
-    pub const fn new(inner: fn() -> &'static __KeyInner<T>,
+    pub const fn new(inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>,
                      init: fn() -> T) -> LocalKey<T> {
         LocalKey {
             inner: inner,
-            init: init
+            init: init,
         }
     }
 
@@ -210,10 +184,10 @@ impl<T: 'static> LocalKey<T> {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn with<F, R>(&'static self, f: F) -> R
                       where F: FnOnce(&T) -> R {
-        let slot = (self.inner)();
         unsafe {
-            let slot = slot.get().expect("cannot access a TLS value during or \
-                                          after it is destroyed");
+            let slot = (self.inner)();
+            let slot = slot.expect("cannot access a TLS value during or \
+                                    after it is destroyed");
             f(match *slot.get() {
                 Some(ref inner) => inner,
                 None => self.init(slot),
@@ -226,7 +200,21 @@ impl<T: 'static> LocalKey<T> {
         // just in case initialization fails.
         let value = (self.init)();
         let ptr = slot.get();
-        *ptr = Some(value);
+
+        // note that this can in theory just be `*ptr = Some(value)`, but due to
+        // the compiler will currently codegen that pattern with something like:
+        //
+        //      ptr::drop_in_place(ptr)
+        //      ptr::write(ptr, Some(value))
+        //
+        // Due to this pattern it's possible for the destructor of the value in
+        // `ptr` (e.g. if this is being recursively initialized) to re-access
+        // TLS, in which case there will be a `&` and `&mut` pointer to the same
+        // value (an aliasing violation). To avoid setting the "I'm running a
+        // destructor" flag we just use `mem::replace` which should sequence the
+        // operations a little differently and make this safe to call.
+        mem::replace(&mut *ptr, Some(value));
+
         (*ptr).as_ref().unwrap()
     }
 
@@ -255,7 +243,7 @@ impl<T: 'static> LocalKey<T> {
                issue = "27716")]
     pub fn state(&'static self) -> LocalKeyState {
         unsafe {
-            match (self.inner)().get() {
+            match (self.inner)() {
                 Some(cell) => {
                     match *cell.get() {
                         Some(..) => LocalKeyState::Valid,
@@ -268,11 +256,9 @@ impl<T: 'static> LocalKey<T> {
     }
 }
 
-#[cfg(all(any(target_os = "macos", target_os = "linux"),
-          not(target_arch = "aarch64"),
-          not(no_elf_tls)))]
+#[cfg(target_thread_local)]
 #[doc(hidden)]
-mod imp {
+pub mod elf {
     use cell::{Cell, UnsafeCell};
     use intrinsics;
     use ptr;
@@ -416,11 +402,8 @@ mod imp {
     }
 }
 
-#[cfg(any(not(any(target_os = "macos", target_os = "linux")),
-          target_arch = "aarch64",
-          no_elf_tls))]
 #[doc(hidden)]
-mod imp {
+pub mod os {
     use prelude::v1::*;
 
     use cell::{Cell, UnsafeCell};
index aef47aa57263b5b818fb56bb4488fa72489e714b..116cd5da2ce7b273a4cfc8ad07d0154e43b84dc0 100644 (file)
@@ -191,7 +191,10 @@ pub use self::local::{LocalKey, LocalKeyState};
 pub use self::scoped_tls::ScopedKey;
 
 #[unstable(feature = "libstd_thread_internals", issue = "0")]
-#[doc(hidden)] pub use self::local::__KeyInner as __LocalKeyInner;
+#[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;
 
@@ -355,26 +358,9 @@ pub fn panicking() -> bool {
 /// with exception safety. Furthermore, a `Send` bound is also required,
 /// providing the same safety guarantees as `thread::spawn` (ensuring the
 /// closure is properly isolated from the parent).
-///
-/// # Examples
-///
-/// ```
-/// #![feature(catch_panic)]
-///
-/// use std::thread;
-///
-/// let result = thread::catch_panic(|| {
-///     println!("hello!");
-/// });
-/// assert!(result.is_ok());
-///
-/// let result = thread::catch_panic(|| {
-///     panic!("oh no!");
-/// });
-/// assert!(result.is_err());
-/// ```
 #[unstable(feature = "catch_panic", reason = "recent API addition",
            issue = "27719")]
+#[rustc_deprecated(since = "1.6.0", reason = "renamed to std::panic::recover")]
 pub fn catch_panic<F, R>(f: F) -> Result<R>
     where F: FnOnce() -> R + Send + 'static
 {
@@ -844,14 +830,14 @@ mod tests {
     fn test_park_timeout_unpark_before() {
         for _ in 0..10 {
             thread::current().unpark();
-            thread::park_timeout_ms(u32::MAX);
+            thread::park_timeout(Duration::from_millis(u32::MAX as u64));
         }
     }
 
     #[test]
     fn test_park_timeout_unpark_not_called() {
         for _ in 0..10 {
-            thread::park_timeout_ms(10);
+            thread::park_timeout(Duration::from_millis(10));
         }
     }
 
@@ -861,17 +847,17 @@ mod tests {
             let th = thread::current();
 
             let _guard = thread::spawn(move || {
-                super::sleep_ms(50);
+                super::sleep(Duration::from_millis(50));
                 th.unpark();
             });
 
-            thread::park_timeout_ms(u32::MAX);
+            thread::park_timeout(Duration::from_millis(u32::MAX as u64));
         }
     }
 
     #[test]
     fn sleep_ms_smoke() {
-        thread::sleep_ms(2);
+        thread::sleep(Duration::from_millis(2));
     }
 
     // NOTE: the corresponding test for stderr is in run-pass/thread-stderr, due
index 2c92bc504c896bb5854139f1902a4098896b3c17..dc0bc6dfe024559ed6586ac04001950e4b732035 100644 (file)
@@ -87,32 +87,9 @@ macro_rules! scoped_thread_local {
            issue = "0")]
 #[macro_export]
 #[allow_internal_unstable]
-#[cfg(no_elf_tls)]
 macro_rules! __scoped_thread_local_inner {
     ($t:ty) => {{
-        static _KEY: $crate::thread::__ScopedKeyInner<$t> =
-            $crate::thread::__ScopedKeyInner::new();
-        fn _getit() -> &'static $crate::thread::__ScopedKeyInner<$t> { &_KEY }
-        $crate::thread::ScopedKey::new(_getit)
-    }}
-}
-
-#[doc(hidden)]
-#[unstable(feature = "thread_local_internals",
-           reason = "should not be necessary",
-           issue = "0")]
-#[macro_export]
-#[allow_internal_unstable]
-#[cfg(not(no_elf_tls))]
-macro_rules! __scoped_thread_local_inner {
-    ($t:ty) => {{
-        #[cfg_attr(not(any(windows,
-                           target_os = "android",
-                           target_os = "ios",
-                           target_os = "netbsd",
-                           target_os = "openbsd",
-                           target_arch = "aarch64")),
-                   thread_local)]
+        #[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 }
@@ -221,13 +198,7 @@ impl<T> ScopedKey<T> {
     }
 }
 
-#[cfg(not(any(windows,
-              target_os = "android",
-              target_os = "ios",
-              target_os = "netbsd",
-              target_os = "openbsd",
-              target_arch = "aarch64",
-              no_elf_tls)))]
+#[cfg(target_thread_local)]
 #[doc(hidden)]
 mod imp {
     use cell::Cell;
@@ -246,13 +217,7 @@ mod imp {
     }
 }
 
-#[cfg(any(windows,
-          target_os = "android",
-          target_os = "ios",
-          target_os = "netbsd",
-          target_os = "openbsd",
-          target_arch = "aarch64",
-          no_elf_tls))]
+#[cfg(not(target_thread_local))]
 #[doc(hidden)]
 mod imp {
     use cell::Cell;
index 0981a8c4a5bf33198d403aacf809a19f6e197c73..7ecb3920cc86c9ede483e4a79cf264de587ec15a 100644 (file)
@@ -40,7 +40,7 @@ const MILLIS_PER_SEC: u64 = 1_000;
 /// let ten_millis = Duration::from_millis(10);
 /// ```
 #[stable(feature = "duration", since = "1.3.0")]
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
 pub struct Duration {
     secs: u64,
     nanos: u32, // Always 0 <= nanos < NANOS_PER_SEC
@@ -173,7 +173,6 @@ impl Div<u32> for Duration {
 
 #[cfg(test)]
 mod tests {
-    use prelude::v1::*;
     use super::Duration;
 
     #[test]
index e3ce8e0de4b12793a6e12c01a39e7fd7c5647d0c..f885733c2d18ff9c1b556a65162dbf9f3c086249 100644 (file)
@@ -23,6 +23,7 @@ pub use self::duration::Duration;
 mod duration;
 
 /// A measurement of a monotonically increasing clock.
+///  Opaque and useful only with `Duration`.
 ///
 /// Instants are always guaranteed to be greater than any previously measured
 /// instant when created, and are often useful for tasks such as measuring
@@ -42,8 +43,8 @@ mod duration;
 #[unstable(feature = "time2", reason = "recently added", issue = "29866")]
 pub struct Instant(time::Instant);
 
-/// A measurement of the system clock appropriate for timestamps such as those
-/// on files on the filesystem.
+/// A measurement of the system clock, useful for talking to
+/// external entities like the file system or other processes.
 ///
 /// Distinct from the `Instant` type, this time measurement **is not
 /// monotonic**. This means that you can save a file to the file system, then
index ca44dec627e37177e0475436510c3fc4ecd693d0..a16f232f4a1c10dd89e8963cb813fb398e11e0a3 100644 (file)
@@ -39,6 +39,7 @@ pub enum Abi {
     Cdecl,
     Stdcall,
     Fastcall,
+    Vectorcall,
     Aapcs,
     Win64,
 
@@ -85,6 +86,7 @@ const AbiDatas: &'static [AbiData] = &[
     AbiData {abi: Cdecl, name: "cdecl" },
     AbiData {abi: Stdcall, name: "stdcall" },
     AbiData {abi: Fastcall, name: "fastcall" },
+    AbiData {abi: Vectorcall, name: "vectorcall"},
     AbiData {abi: Aapcs, name: "aapcs" },
     AbiData {abi: Win64, name: "win64" },
 
index f11291fc0f7e7a005124589df7fd3332e0605eba..e327adfaf892ce5a6e42d5c0ae9194d6a5b3c1dd 100644 (file)
@@ -10,7 +10,6 @@
 
 // The Rust abstract syntax tree.
 
-pub use self::BindingMode::*;
 pub use self::BinOp_::*;
 pub use self::BlockCheckMode::*;
 pub use self::CaptureClause::*;
@@ -48,11 +47,9 @@ pub use self::PathParameters::*;
 use attr::ThinAttributes;
 use codemap::{Span, Spanned, DUMMY_SP, ExpnId};
 use abi::Abi;
-use ast_util;
 use ext::base;
 use ext::tt::macro_parser;
-use owned_slice::OwnedSlice;
-use parse::token::{InternedString, str_to_ident};
+use parse::token::InternedString;
 use parse::token;
 use parse::lexer;
 use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
@@ -253,41 +250,41 @@ pub struct PathSegment {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum PathParameters {
     /// The `<'a, A,B,C>` in `foo::bar::baz::<'a, A,B,C>`
-    AngleBracketedParameters(AngleBracketedParameterData),
+    AngleBracketed(AngleBracketedParameterData),
     /// The `(A,B)` and `C` in `Foo(A,B) -> C`
-    ParenthesizedParameters(ParenthesizedParameterData),
+    Parenthesized(ParenthesizedParameterData),
 }
 
 impl PathParameters {
     pub fn none() -> PathParameters {
-        AngleBracketedParameters(AngleBracketedParameterData {
+        PathParameters::AngleBracketed(AngleBracketedParameterData {
             lifetimes: Vec::new(),
-            types: OwnedSlice::empty(),
-            bindings: OwnedSlice::empty(),
+            types: P::empty(),
+            bindings: P::empty(),
         })
     }
 
     pub fn is_empty(&self) -> bool {
         match *self {
-            AngleBracketedParameters(ref data) => data.is_empty(),
+            PathParameters::AngleBracketed(ref data) => data.is_empty(),
 
             // Even if the user supplied no types, something like
             // `X()` is equivalent to `X<(),()>`.
-            ParenthesizedParameters(..) => false,
+            PathParameters::Parenthesized(..) => false,
         }
     }
 
     pub fn has_lifetimes(&self) -> bool {
         match *self {
-            AngleBracketedParameters(ref data) => !data.lifetimes.is_empty(),
-            ParenthesizedParameters(_) => false,
+            PathParameters::AngleBracketed(ref data) => !data.lifetimes.is_empty(),
+            PathParameters::Parenthesized(_) => false,
         }
     }
 
     pub fn has_types(&self) -> bool {
         match *self {
-            AngleBracketedParameters(ref data) => !data.types.is_empty(),
-            ParenthesizedParameters(..) => true,
+            PathParameters::AngleBracketed(ref data) => !data.types.is_empty(),
+            PathParameters::Parenthesized(..) => true,
         }
     }
 
@@ -295,10 +292,10 @@ impl PathParameters {
     /// parameters in the parenthesized case.
     pub fn types(&self) -> Vec<&P<Ty>> {
         match *self {
-            AngleBracketedParameters(ref data) => {
+            PathParameters::AngleBracketed(ref data) => {
                 data.types.iter().collect()
             }
-            ParenthesizedParameters(ref data) => {
+            PathParameters::Parenthesized(ref data) => {
                 data.inputs.iter()
                     .chain(data.output.iter())
                     .collect()
@@ -308,10 +305,10 @@ impl PathParameters {
 
     pub fn lifetimes(&self) -> Vec<&Lifetime> {
         match *self {
-            AngleBracketedParameters(ref data) => {
+            PathParameters::AngleBracketed(ref data) => {
                 data.lifetimes.iter().collect()
             }
-            ParenthesizedParameters(_) => {
+            PathParameters::Parenthesized(_) => {
                 Vec::new()
             }
         }
@@ -319,10 +316,10 @@ impl PathParameters {
 
     pub fn bindings(&self) -> Vec<&P<TypeBinding>> {
         match *self {
-            AngleBracketedParameters(ref data) => {
+            PathParameters::AngleBracketed(ref data) => {
                 data.bindings.iter().collect()
             }
-            ParenthesizedParameters(_) => {
+            PathParameters::Parenthesized(_) => {
                 Vec::new()
             }
         }
@@ -335,10 +332,10 @@ pub struct AngleBracketedParameterData {
     /// The lifetime parameters for this path segment.
     pub lifetimes: Vec<Lifetime>,
     /// The type parameters for this path segment, if present.
-    pub types: OwnedSlice<P<Ty>>,
+    pub types: P<[P<Ty>]>,
     /// Bindings (equality constraints) on associated types, if present.
-    /// E.g., `Foo<A=Bar>`.
-    pub bindings: OwnedSlice<P<TypeBinding>>,
+    /// e.g., `Foo<A=Bar>`.
+    pub bindings: P<[P<TypeBinding>]>,
 }
 
 impl AngleBracketedParameterData {
@@ -395,7 +392,7 @@ pub enum TraitBoundModifier {
     Maybe,
 }
 
-pub type TyParamBounds = OwnedSlice<TyParamBound>;
+pub type TyParamBounds = P<[TyParamBound]>;
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct TyParam {
@@ -411,7 +408,7 @@ pub struct TyParam {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Generics {
     pub lifetimes: Vec<LifetimeDef>,
-    pub ty_params: OwnedSlice<TyParam>,
+    pub ty_params: P<[TyParam]>,
     pub where_clause: WhereClause,
 }
 
@@ -427,6 +424,19 @@ impl Generics {
     }
 }
 
+impl Default for Generics {
+    fn default() ->  Generics {
+        Generics {
+            lifetimes: Vec::new(),
+            ty_params: P::empty(),
+            where_clause: WhereClause {
+                id: DUMMY_NODE_ID,
+                predicates: Vec::new(),
+            }
+        }
+    }
+}
+
 /// A `where` clause in a definition
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct WhereClause {
@@ -437,7 +447,7 @@ pub struct WhereClause {
 /// A single predicate in a `where` clause
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum WherePredicate {
-    /// A type binding, eg `for<'c> Foo: Send+Clone+'c`
+    /// A type binding, e.g. `for<'c> Foo: Send+Clone+'c`
     BoundPredicate(WhereBoundPredicate),
     /// A lifetime predicate, e.g. `'a: 'b+'c`
     RegionPredicate(WhereRegionPredicate),
@@ -445,7 +455,7 @@ pub enum WherePredicate {
     EqPredicate(WhereEqPredicate),
 }
 
-/// A type bound, eg `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,
@@ -454,7 +464,7 @@ pub struct WhereBoundPredicate {
     /// The type being bounded
     pub bounded_ty: P<Ty>,
     /// Trait and lifetime bounds (`Clone+Send+'static`)
-    pub bounds: OwnedSlice<TyParamBound>,
+    pub bounds: TyParamBounds,
 }
 
 /// A lifetime predicate, e.g. `'a: 'b+'c`
@@ -563,8 +573,8 @@ pub struct FieldPat {
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
 pub enum BindingMode {
-    BindByRef(Mutability),
-    BindByValue(Mutability),
+    ByRef(Mutability),
+    ByValue(Mutability),
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -657,6 +667,57 @@ pub enum BinOp_ {
     BiGt,
 }
 
+impl BinOp_ {
+    pub fn to_string(&self) -> &'static str {
+        match *self {
+            BiAdd => "+",
+            BiSub => "-",
+            BiMul => "*",
+            BiDiv => "/",
+            BiRem => "%",
+            BiAnd => "&&",
+            BiOr => "||",
+            BiBitXor => "^",
+            BiBitAnd => "&",
+            BiBitOr => "|",
+            BiShl => "<<",
+            BiShr => ">>",
+            BiEq => "==",
+            BiLt => "<",
+            BiLe => "<=",
+            BiNe => "!=",
+            BiGe => ">=",
+            BiGt => ">"
+        }
+    }
+    pub fn lazy(&self) -> bool {
+        match *self {
+            BiAnd | BiOr => true,
+            _ => false
+        }
+    }
+
+    pub fn is_shift(&self) -> bool {
+        match *self {
+            BiShl | BiShr => true,
+            _ => false
+        }
+    }
+    pub fn is_comparison(&self) -> bool {
+        match *self {
+            BiEq | BiLt | BiLe | BiNe | BiGt | BiGe =>
+            true,
+            BiAnd | BiOr | BiAdd | BiSub | BiMul | BiDiv | BiRem |
+            BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr =>
+            false,
+        }
+    }
+    /// Returns `true` if the binary operator takes its arguments by value
+    pub fn is_by_value(&self) -> bool {
+        !BinOp_::is_comparison(self)
+    }
+}
+
 pub type BinOp = Spanned<BinOp_>;
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
@@ -669,13 +730,31 @@ pub enum UnOp {
     UnNeg
 }
 
+impl UnOp {
+    /// Returns `true` if the unary operator takes its argument by value
+    pub fn is_by_value(u: UnOp) -> bool {
+        match u {
+            UnNeg | UnNot => true,
+            _ => false,
+        }
+    }
+
+    pub fn to_string(op: UnOp) -> &'static str {
+        match op {
+            UnDeref => "*",
+            UnNot => "!",
+            UnNeg => "-",
+        }
+    }
+}
+
 /// A statement
 pub type Stmt = Spanned<Stmt_>;
 
 impl fmt::Debug for Stmt {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "stmt({}: {})",
-               ast_util::stmt_id(self)
+               self.node.id()
                    .map_or(Cow::Borrowed("<macro>"),|id|Cow::Owned(id.to_string())),
                pprust::stmt_to_string(self))
     }
@@ -697,6 +776,15 @@ pub enum Stmt_ {
 }
 
 impl Stmt_ {
+    pub fn id(&self) -> Option<NodeId> {
+        match *self {
+            StmtDecl(_, id) => Some(id),
+            StmtExpr(_, id) => Some(id),
+            StmtSemi(_, id) => Some(id),
+            StmtMac(..) => None,
+        }
+    }
+
     pub fn attrs(&self) -> &[Attribute] {
         match *self {
             StmtDecl(ref d, _) => d.attrs(),
@@ -853,6 +941,7 @@ pub enum Expr_ {
     ExprLit(P<Lit>),
     /// A cast (`foo as f64`)
     ExprCast(P<Expr>, P<Ty>),
+    ExprType(P<Expr>, P<Ty>),
     /// An `if` block, with an optional else block
     ///
     /// `if expr { block } else { expr }`
@@ -1006,7 +1095,7 @@ impl Delimited {
     }
 }
 
-/// A sequence of token treesee
+/// A sequence of token trees
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct SequenceRepetition {
     /// The sequence of token trees
@@ -1226,6 +1315,16 @@ pub enum Lit_ {
     LitBool(bool),
 }
 
+impl Lit_ {
+    /// Returns true if this literal is a string and false otherwise.
+    pub fn is_str(&self) -> bool {
+        match *self {
+            LitStr(..) => true,
+            _ => false,
+        }
+    }
+}
+
 // NB: If you change this, you'll probably want to change the corresponding
 // type structure in middle/ty.rs as well.
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -1247,7 +1346,7 @@ pub struct MethodSig {
 }
 
 /// Represents a method declaration in a trait declaration, possibly including
-/// a default implementation A trait method is either required (meaning it
+/// a default implementation. A trait method is either required (meaning it
 /// doesn't have an implementation, just a signature) or provided (meaning it
 /// has a default implementation).
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -1301,11 +1400,37 @@ impl fmt::Debug for IntTy {
 
 impl fmt::Display for IntTy {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", ast_util::int_ty_to_string(*self))
+        write!(f, "{}", self.ty_to_string())
     }
 }
 
 impl IntTy {
+    pub fn ty_to_string(&self) -> &'static str {
+        match *self {
+            TyIs => "isize",
+            TyI8 => "i8",
+            TyI16 => "i16",
+            TyI32 => "i32",
+            TyI64 => "i64"
+        }
+    }
+
+    pub fn val_to_string(&self, val: i64) -> String {
+        // cast to a u64 so we can correctly print INT64_MIN. All integral types
+        // are parsed as u64, so we wouldn't want to print an extra negative
+        // sign.
+        format!("{}{}", val as u64, self.ty_to_string())
+    }
+
+    pub fn ty_max(&self) -> u64 {
+        match *self {
+            TyI8 => 0x80,
+            TyI16 => 0x8000,
+            TyIs | TyI32 => 0x80000000, // actually ni about TyIs
+            TyI64 => 0x8000000000000000
+        }
+    }
+
     pub fn bit_width(&self) -> Option<usize> {
         Some(match *self {
             TyIs => return None,
@@ -1327,6 +1452,29 @@ pub enum UintTy {
 }
 
 impl UintTy {
+    pub fn ty_to_string(&self) -> &'static str {
+        match *self {
+            TyUs => "usize",
+            TyU8 => "u8",
+            TyU16 => "u16",
+            TyU32 => "u32",
+            TyU64 => "u64"
+        }
+    }
+
+    pub fn val_to_string(&self, val: u64) -> String {
+        format!("{}{}", val, self.ty_to_string())
+    }
+
+    pub fn ty_max(&self) -> u64 {
+        match *self {
+            TyU8 => 0xff,
+            TyU16 => 0xffff,
+            TyUs | TyU32 => 0xffffffff, // actually ni about TyUs
+            TyU64 => 0xffffffffffffffff
+        }
+    }
+
     pub fn bit_width(&self) -> Option<usize> {
         Some(match *self {
             TyUs => return None,
@@ -1346,7 +1494,7 @@ impl fmt::Debug for UintTy {
 
 impl fmt::Display for UintTy {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", ast_util::uint_ty_to_string(*self))
+        write!(f, "{}", self.ty_to_string())
     }
 }
 
@@ -1364,11 +1512,18 @@ impl fmt::Debug for FloatTy {
 
 impl fmt::Display for FloatTy {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", ast_util::float_ty_to_string(*self))
+        write!(f, "{}", self.ty_to_string())
     }
 }
 
 impl FloatTy {
+    pub fn ty_to_string(&self) -> &'static str {
+        match *self {
+            TyF32 => "f32",
+            TyF64 => "f64",
+        }
+    }
+
     pub fn bit_width(&self) -> usize {
         match *self {
             TyF32 => 32,
@@ -1458,11 +1613,19 @@ pub enum AsmDialect {
     Intel,
 }
 
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+pub struct InlineAsmOutput {
+    pub constraint: InternedString,
+    pub expr: P<Expr>,
+    pub is_rw: bool,
+    pub is_indirect: bool,
+}
+
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct InlineAsm {
     pub asm: InternedString,
     pub asm_str_style: StrStyle,
-    pub outputs: Vec<(InternedString, P<Expr>, bool)>,
+    pub outputs: Vec<InlineAsmOutput>,
     pub inputs: Vec<(InternedString, P<Expr>)>,
     pub clobbers: Vec<InternedString>,
     pub volatile: bool,
@@ -1491,7 +1654,7 @@ impl Arg {
             }),
             pat: P(Pat {
                 id: DUMMY_NODE_ID,
-                node: PatIdent(BindByValue(mutability), path, None),
+                node: PatIdent(BindingMode::ByValue(mutability), path, None),
                 span: span
             }),
             id: DUMMY_NODE_ID
index 489c61b83da75b3108a7783ecd9fee89e9912e8b..ba4d1e2193e5f0f49be54071dd73dca53783f282 100644 (file)
 
 use ast::*;
 use ast;
-use ast_util;
 use codemap;
 use codemap::Span;
-use owned_slice::OwnedSlice;
 use parse::token;
 use print::pprust;
 use ptr::P;
@@ -28,144 +26,10 @@ pub fn path_name_i(idents: &[Ident]) -> String {
     idents.iter().map(|i| i.to_string()).collect::<Vec<String>>().join("::")
 }
 
-pub fn stmt_id(s: &Stmt) -> Option<NodeId> {
-    match s.node {
-      StmtDecl(_, id) => Some(id),
-      StmtExpr(_, id) => Some(id),
-      StmtSemi(_, id) => Some(id),
-      StmtMac(..) => None,
-    }
-}
-
-pub fn binop_to_string(op: BinOp_) -> &'static str {
-    match op {
-        BiAdd => "+",
-        BiSub => "-",
-        BiMul => "*",
-        BiDiv => "/",
-        BiRem => "%",
-        BiAnd => "&&",
-        BiOr => "||",
-        BiBitXor => "^",
-        BiBitAnd => "&",
-        BiBitOr => "|",
-        BiShl => "<<",
-        BiShr => ">>",
-        BiEq => "==",
-        BiLt => "<",
-        BiLe => "<=",
-        BiNe => "!=",
-        BiGe => ">=",
-        BiGt => ">"
-    }
-}
-
-pub fn lazy_binop(b: BinOp_) -> bool {
-    match b {
-      BiAnd => true,
-      BiOr => true,
-      _ => false
-    }
-}
-
-pub fn is_shift_binop(b: BinOp_) -> bool {
-    match b {
-      BiShl => true,
-      BiShr => true,
-      _ => false
-    }
-}
-
-pub fn is_comparison_binop(b: BinOp_) -> bool {
-    match b {
-        BiEq | BiLt | BiLe | BiNe | BiGt | BiGe =>
-            true,
-        BiAnd | BiOr | BiAdd | BiSub | BiMul | BiDiv | BiRem |
-        BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr =>
-            false,
-    }
-}
-
-/// Returns `true` if the binary operator takes its arguments by value
-pub fn is_by_value_binop(b: BinOp_) -> bool {
-    !is_comparison_binop(b)
-}
-
-/// Returns `true` if the unary operator takes its argument by value
-pub fn is_by_value_unop(u: UnOp) -> bool {
-    match u {
-        UnNeg | UnNot => true,
-        _ => false,
-    }
-}
-
-pub fn unop_to_string(op: UnOp) -> &'static str {
-    match op {
-        UnDeref => "*",
-        UnNot => "!",
-        UnNeg => "-",
-    }
-}
-
 pub fn is_path(e: P<Expr>) -> bool {
     match e.node { ExprPath(..) => true, _ => false }
 }
 
-pub fn int_ty_to_string(t: IntTy) -> &'static str {
-    match t {
-        TyIs => "isize",
-        TyI8 => "i8",
-        TyI16 => "i16",
-        TyI32 => "i32",
-        TyI64 => "i64"
-    }
-}
-
-pub fn int_val_to_string(t: IntTy, val: i64) -> String {
-    // cast to a u64 so we can correctly print INT64_MIN. All integral types
-    // are parsed as u64, so we wouldn't want to print an extra negative
-    // sign.
-    format!("{}{}", val as u64, int_ty_to_string(t))
-}
-
-pub fn int_ty_max(t: IntTy) -> u64 {
-    match t {
-        TyI8 => 0x80,
-        TyI16 => 0x8000,
-        TyIs | TyI32 => 0x80000000, // actually ni about TyIs
-        TyI64 => 0x8000000000000000
-    }
-}
-
-pub fn uint_ty_to_string(t: UintTy) -> &'static str {
-    match t {
-        TyUs => "usize",
-        TyU8 => "u8",
-        TyU16 => "u16",
-        TyU32 => "u32",
-        TyU64 => "u64"
-    }
-}
-
-pub fn uint_val_to_string(t: UintTy, val: u64) -> String {
-    format!("{}{}", val, uint_ty_to_string(t))
-}
-
-pub fn uint_ty_max(t: UintTy) -> u64 {
-    match t {
-        TyU8 => 0xff,
-        TyU16 => 0xffff,
-        TyUs | TyU32 => 0xffffffff, // actually ni about TyUs
-        TyU64 => 0xffffffffffffffff
-    }
-}
-
-pub fn float_ty_to_string(t: FloatTy) -> &'static str {
-    match t {
-        TyF32 => "f32",
-        TyF64 => "f64",
-    }
-}
 
 // convert a span and an identifier to the corresponding
 // 1-segment path
@@ -176,10 +40,10 @@ pub fn ident_to_path(s: Span, identifier: Ident) -> Path {
         segments: vec!(
             ast::PathSegment {
                 identifier: identifier,
-                parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
+                parameters: ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData {
                     lifetimes: Vec::new(),
-                    types: OwnedSlice::empty(),
-                    bindings: OwnedSlice::empty(),
+                    types: P::empty(),
+                    bindings: P::empty(),
                 })
             }
         ),
@@ -204,7 +68,7 @@ pub fn path_to_ident(path: &Path) -> Option<Ident> {
 pub fn ident_to_pat(id: NodeId, s: Span, i: Ident) -> P<Pat> {
     P(Pat {
         id: id,
-        node: PatIdent(BindByValue(MutImmutable), codemap::Spanned{span:s, node:i}, None),
+        node: PatIdent(BindingMode::ByValue(MutImmutable), codemap::Spanned{span:s, node:i}, None),
         span: s
     })
 }
@@ -236,17 +100,6 @@ pub fn struct_field_visibility(field: ast::StructField) -> Visibility {
     }
 }
 
-pub fn empty_generics() -> Generics {
-    Generics {
-        lifetimes: Vec::new(),
-        ty_params: OwnedSlice::empty(),
-        where_clause: WhereClause {
-            id: DUMMY_NODE_ID,
-            predicates: Vec::new(),
-        }
-    }
-}
-
 // ______________________________________________________________________
 // Enumerating the IDs which appear in an AST
 
@@ -351,7 +204,7 @@ impl<'a, 'v, O: IdVisitingOperation> Visitor<'v> for IdVisitor<'a, O> {
 
     fn visit_stmt(&mut self, statement: &Stmt) {
         self.operation
-            .visit_id(ast_util::stmt_id(statement).expect("attempted to visit unexpanded stmt"));
+            .visit_id(statement.node.id().expect("attempted to visit unexpanded stmt"));
         visit::walk_stmt(self, statement)
     }
 
@@ -519,14 +372,6 @@ pub fn segments_name_eq(a : &[ast::PathSegment], b : &[ast::PathSegment]) -> boo
     })
 }
 
-/// Returns true if this literal is a string and false otherwise.
-pub fn lit_is_str(lit: &Lit) -> bool {
-    match lit.node {
-        LitStr(..) => true,
-        _ => false,
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use ast::*;
index e828d8ae24874fd4773e2fdbf054dc1c3dd445b7..96d0052cf180125b659fd5ce4157960ea0b4f616 100644 (file)
@@ -21,10 +21,10 @@ use ast::{Expr, Item, Local, Decl};
 use codemap::{Span, Spanned, spanned, dummy_spanned};
 use codemap::BytePos;
 use config::CfgDiag;
-use diagnostic::SpanHandler;
+use errors::Handler;
 use feature_gate::{GatedCfg, GatedCfgAttr};
 use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
-use parse::token::{InternedString, intern_and_get_ident};
+use parse::token::InternedString;
 use parse::token;
 use ptr::P;
 
@@ -299,14 +299,16 @@ pub fn find_crate_name(attrs: &[Attribute]) -> Option<InternedString> {
 }
 
 /// Find the value of #[export_name=*] attribute and check its validity.
-pub fn find_export_name_attr(diag: &SpanHandler, attrs: &[Attribute]) -> Option<InternedString> {
+pub fn find_export_name_attr(diag: &Handler, attrs: &[Attribute]) -> Option<InternedString> {
     attrs.iter().fold(None, |ia,attr| {
         if attr.check_name("export_name") {
             if let s@Some(_) = attr.value_str() {
                 s
             } else {
-                diag.span_err(attr.span, "export_name attribute has invalid format");
-                diag.handler.help("use #[export_name=\"*\"]");
+                diag.struct_span_err(attr.span,
+                                     "export_name attribute has invalid format")
+                    .help("use #[export_name=\"*\"]")
+                    .emit();
                 None
             }
         } else {
@@ -324,7 +326,7 @@ pub enum InlineAttr {
 }
 
 /// Determine what `#[inline]` attribute is present in `attrs`, if any.
-pub fn find_inline_attr(diagnostic: Option<&SpanHandler>, attrs: &[Attribute]) -> InlineAttr {
+pub fn find_inline_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> InlineAttr {
     attrs.iter().fold(InlineAttr::None, |ia,attr| {
         match attr.node.value.node {
             MetaWord(ref n) if *n == "inline" => {
@@ -398,7 +400,7 @@ pub fn cfg_matches<T: CfgDiag>(cfgs: &[P<MetaItem>],
 pub struct Stability {
     pub level: StabilityLevel,
     pub feature: InternedString,
-    pub depr: Option<Deprecation>,
+    pub rustc_depr: Option<RustcDeprecation>,
 }
 
 /// The available stability levels.
@@ -410,24 +412,30 @@ pub enum StabilityLevel {
 }
 
 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
-pub struct Deprecation {
+pub struct RustcDeprecation {
     pub since: InternedString,
     pub reason: InternedString,
 }
 
+#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
+pub struct Deprecation {
+    pub since: Option<InternedString>,
+    pub note: Option<InternedString>,
+}
+
 impl StabilityLevel {
     pub fn is_unstable(&self) -> bool { if let Unstable {..} = *self { true } else { false }}
     pub fn is_stable(&self) -> bool { if let Stable {..} = *self { true } else { false }}
 }
 
-fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
+fn find_stability_generic<'a, I>(diagnostic: &Handler,
                                  attrs_iter: I,
                                  item_sp: Span)
                                  -> Option<Stability>
     where I: Iterator<Item = &'a Attribute>
 {
     let mut stab: Option<Stability> = None;
-    let mut depr: Option<Deprecation> = None;
+    let mut rustc_depr: Option<RustcDeprecation> = None;
 
     'outer: for attr in attrs_iter {
         let tag = attr.name();
@@ -456,7 +464,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
 
             match tag {
                 "rustc_deprecated" => {
-                    if depr.is_some() {
+                    if rustc_depr.is_some() {
                         diagnostic.span_err(item_sp, "multiple rustc_deprecated attributes");
                         break
                     }
@@ -477,7 +485,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
 
                     match (since, reason) {
                         (Some(since), Some(reason)) => {
-                            depr = Some(Deprecation {
+                            rustc_depr = Some(RustcDeprecation {
                                 since: since,
                                 reason: reason,
                             })
@@ -529,7 +537,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
                                     }
                                 },
                                 feature: feature,
-                                depr: None,
+                                rustc_depr: None,
                             })
                         }
                         (None, _, _) => {
@@ -569,7 +577,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
                                     since: since,
                                 },
                                 feature: feature,
-                                depr: None,
+                                rustc_depr: None,
                             })
                         }
                         (None, _) => {
@@ -591,12 +599,12 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
     }
 
     // Merge the deprecation info into the stability info
-    if let Some(depr) = depr {
+    if let Some(rustc_depr) = rustc_depr {
         if let Some(ref mut stab) = stab {
             if let Unstable {reason: ref mut reason @ None, ..} = stab.level {
-                *reason = Some(depr.reason.clone())
+                *reason = Some(rustc_depr.reason.clone())
             }
-            stab.depr = Some(depr);
+            stab.rustc_depr = Some(rustc_depr);
         } else {
             diagnostic.span_err(item_sp, "rustc_deprecated attribute must be paired with \
                                           either stable or unstable attribute");
@@ -606,13 +614,78 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
     stab
 }
 
+fn find_deprecation_generic<'a, I>(diagnostic: &Handler,
+                                   attrs_iter: I,
+                                   item_sp: Span)
+                                   -> Option<Deprecation>
+    where I: Iterator<Item = &'a Attribute>
+{
+    let mut depr: Option<Deprecation> = None;
+
+    'outer: for attr in attrs_iter {
+        if attr.name() != "deprecated" {
+            continue
+        }
+
+        mark_used(attr);
+
+        if depr.is_some() {
+            diagnostic.span_err(item_sp, "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()));
+                    return false
+                }
+                if let Some(v) = meta.value_str() {
+                    *item = Some(v);
+                    true
+                } else {
+                    diagnostic.span_err(meta.span, "incorrect meta item");
+                    false
+                }
+            };
+
+            let mut since = None;
+            let mut note = None;
+            for meta in metas {
+                match &*meta.name() {
+                    "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()));
+                        continue 'outer
+                    }
+                }
+            }
+
+            Some(Deprecation {since: since, note: note})
+        } else {
+            Some(Deprecation{since: None, note: None})
+        }
+    }
+
+    depr
+}
+
 /// Find the first stability attribute. `None` if none exists.
-pub fn find_stability(diagnostic: &SpanHandler, attrs: &[Attribute],
+pub fn find_stability(diagnostic: &Handler, attrs: &[Attribute],
                       item_sp: Span) -> Option<Stability> {
     find_stability_generic(diagnostic, attrs.iter(), item_sp)
 }
 
-pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) {
+/// Find the deprecation attribute. `None` if none exists.
+pub fn find_deprecation(diagnostic: &Handler, attrs: &[Attribute],
+                        item_sp: Span) -> Option<Deprecation> {
+    find_deprecation_generic(diagnostic, attrs.iter(), item_sp)
+}
+
+pub fn require_unique_names(diagnostic: &Handler, metas: &[P<MetaItem>]) {
     let mut set = HashSet::new();
     for meta in metas {
         let name = meta.name();
@@ -631,7 +704,7 @@ pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) {
 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
 /// the same discriminant size that the corresponding C enum would or C
 /// structure layout, and `packed` to remove padding.
-pub fn find_repr_attrs(diagnostic: &SpanHandler, attr: &Attribute) -> Vec<ReprAttr> {
+pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr> {
     let mut acc = Vec::new();
     match attr.node.value.node {
         ast::MetaList(ref s, ref items) if *s == "repr" => {
index 18659cb2e78f18359f3a387c78130d0470060e46..19236f2cd98a80302e463ffd5d9112ac111f42c4 100644 (file)
@@ -164,18 +164,15 @@ impl Eq for Span {}
 
 impl Encodable for Span {
     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-        // Encode spans as a single u64 in order to cut down on tagging overhead
-        // added by the RBML metadata encoding. The should be solved differently
-        // altogether some time (FIXME #21482)
-        s.emit_u64( (self.lo.0 as u64) | ((self.hi.0 as u64) << 32) )
+        try!(s.emit_u32(self.lo.0));
+        s.emit_u32(self.hi.0)
     }
 }
 
 impl Decodable for Span {
     fn decode<D: Decoder>(d: &mut D) -> Result<Span, D::Error> {
-        let lo_hi: u64 = try! { d.read_u64() };
-        let lo = BytePos(lo_hi as u32);
-        let hi = BytePos((lo_hi >> 32) as u32);
+        let lo = BytePos(try! { d.read_u32() });
+        let hi = BytePos(try! { d.read_u32() });
         Ok(mk_sp(lo, hi))
     }
 }
index 1209c58fd5ed1abf01a440b7e2fa4d56e239f829..64b16538f05a14e0877ef28899475964ceba9b80 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use attr::AttrMetaMethods;
-use diagnostic::SpanHandler;
+use errors::Handler;
 use feature_gate::GatedCfgAttr;
 use fold::Folder;
 use {ast, fold, attr};
@@ -23,12 +23,12 @@ use util::small_vector::SmallVector;
 /// configuration.
 struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool {
     in_cfg: F,
-    diagnostic: &'a SpanHandler,
+    diagnostic: &'a Handler,
 }
 
 // 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: &SpanHandler, krate: ast::Crate,
+pub fn strip_unconfigured_items(diagnostic: &Handler, krate: ast::Crate,
                                 feature_gated_cfgs: &mut Vec<GatedCfgAttr>)
                                 -> ast::Crate
 {
@@ -83,7 +83,7 @@ impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) ->
     }
 }
 
-pub fn strip_items<'a, F>(diagnostic: &'a SpanHandler,
+pub fn strip_items<'a, F>(diagnostic: &'a Handler,
                           krate: ast::Crate, in_cfg: F) -> ast::Crate where
     F: FnMut(&[ast::Attribute]) -> bool,
 {
@@ -291,7 +291,7 @@ struct CfgAttrFolder<'a, T> {
 }
 
 // Process `#[cfg_attr]`.
-fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate,
+fn process_cfg_attr(diagnostic: &Handler, krate: ast::Crate,
                     feature_gated_cfgs: &mut Vec<GatedCfgAttr>) -> ast::Crate {
     let mut fld = CfgAttrFolder {
         diag: CfgDiagReal {
@@ -463,17 +463,17 @@ impl<'v, 'a, 'b> visit::Visitor<'v> for StmtExprAttrFeatureVisitor<'a, 'b> {
 }
 
 pub trait CfgDiag {
-    fn emit_error<F>(&mut self, f: F) where F: FnMut(&SpanHandler);
+    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>);
 }
 
 pub struct CfgDiagReal<'a, 'b> {
-    pub diag: &'a SpanHandler,
+    pub diag: &'a Handler,
     pub feature_gated_cfgs: &'b mut Vec<GatedCfgAttr>,
 }
 
 impl<'a, 'b> CfgDiag for CfgDiagReal<'a, 'b> {
-    fn emit_error<F>(&mut self, mut f: F) where F: FnMut(&SpanHandler) {
+    fn emit_error<F>(&mut self, mut f: F) where F: FnMut(&Handler) {
         f(self.diag)
     }
     fn flag_gated<F>(&mut self, mut f: F) where F: FnMut(&mut Vec<GatedCfgAttr>) {
@@ -486,7 +486,7 @@ struct CfgDiagSilent {
 }
 
 impl CfgDiag for CfgDiagSilent {
-    fn emit_error<F>(&mut self, _: F) where F: FnMut(&SpanHandler) {
+    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>) {}
diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs
deleted file mode 100644 (file)
index b854a2f..0000000
+++ /dev/null
@@ -1,900 +0,0 @@
-// 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.
-
-pub use self::Level::*;
-pub use self::RenderSpan::*;
-pub use self::ColorConfig::*;
-use self::Destination::*;
-
-use codemap::{self, COMMAND_LINE_SP, COMMAND_LINE_EXPN, Pos, Span};
-use diagnostics;
-
-use std::cell::{RefCell, Cell};
-use std::{cmp, error, fmt};
-use std::io::prelude::*;
-use std::io;
-use term;
-
-/// maximum number of lines we will print for each error; arbitrary.
-const MAX_LINES: usize = 6;
-
-#[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(Span),
-
-    /// Similar to a FullSpan, but the cited position is the end of
-    /// the span, instead of the start. Used, at least, for telling
-    /// compiletest/runtest to look at the last line of the span
-    /// (since `end_highlight_lines` displays an arrow to the end
-    /// of the span).
-    EndSpan(Span),
-
-    /// 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 the `String` is spliced
-    /// into the lines in place of the code covered by the span.
-    Suggestion(Span, String),
-
-    /// A FileLine renders with just a line for the message prefixed
-    /// by file:linenum.
-    FileLine(Span),
-}
-
-impl RenderSpan {
-    fn span(&self) -> Span {
-        match *self {
-            FullSpan(s) |
-            Suggestion(s, _) |
-            EndSpan(s) |
-            FileLine(s) =>
-                s
-        }
-    }
-}
-
-#[derive(Clone, Copy)]
-pub enum ColorConfig {
-    Auto,
-    Always,
-    Never
-}
-
-pub trait Emitter {
-    fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, Span)>,
-            msg: &str, code: Option<&str>, lvl: Level);
-    fn custom_emit(&mut self, cm: &codemap::CodeMap,
-                   sp: RenderSpan, msg: &str, lvl: Level);
-}
-
-/// 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"
-    }
-}
-
-/// A span-handler is like a handler but also
-/// accepts span information for source-location
-/// reporting.
-pub struct SpanHandler {
-    pub handler: Handler,
-    pub cm: codemap::CodeMap,
-}
-
-impl SpanHandler {
-    pub fn new(handler: Handler, cm: codemap::CodeMap) -> SpanHandler {
-        SpanHandler {
-            handler: handler,
-            cm: cm,
-        }
-    }
-    pub fn span_fatal(&self, sp: Span, msg: &str) -> FatalError {
-        self.handler.emit(Some((&self.cm, sp)), msg, Fatal);
-        return FatalError;
-    }
-    pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> FatalError {
-        self.handler.emit_with_code(Some((&self.cm, sp)), msg, code, Fatal);
-        return FatalError;
-    }
-    pub fn span_err(&self, sp: Span, msg: &str) {
-        self.handler.emit(Some((&self.cm, sp)), msg, Error);
-        self.handler.bump_err_count();
-    }
-    pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
-        self.handler.emit_with_code(Some((&self.cm, sp)), msg, code, Error);
-        self.handler.bump_err_count();
-    }
-    pub fn span_warn(&self, sp: Span, msg: &str) {
-        self.handler.emit(Some((&self.cm, sp)), msg, Warning);
-    }
-    pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) {
-        self.handler.emit_with_code(Some((&self.cm, sp)), msg, code, Warning);
-    }
-    pub fn span_note(&self, sp: Span, msg: &str) {
-        self.handler.emit(Some((&self.cm, sp)), msg, Note);
-    }
-    pub fn span_end_note(&self, sp: Span, msg: &str) {
-        self.handler.custom_emit(&self.cm, EndSpan(sp), msg, Note);
-    }
-    pub fn span_help(&self, sp: Span, msg: &str) {
-        self.handler.emit(Some((&self.cm, sp)), msg, Help);
-    }
-    /// Prints out a message with a suggested edit of the code.
-    ///
-    /// See `diagnostic::RenderSpan::Suggestion` for more information.
-    pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) {
-        self.handler.custom_emit(&self.cm, Suggestion(sp, suggestion), msg, Help);
-    }
-    pub fn fileline_note(&self, sp: Span, msg: &str) {
-        self.handler.custom_emit(&self.cm, FileLine(sp), msg, Note);
-    }
-    pub fn fileline_help(&self, sp: Span, msg: &str) {
-        self.handler.custom_emit(&self.cm, FileLine(sp), msg, Help);
-    }
-    pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
-        self.handler.emit(Some((&self.cm, sp)), msg, Bug);
-        panic!(ExplicitBug);
-    }
-    pub fn span_bug_no_panic(&self, sp: Span, msg: &str) {
-        self.handler.emit(Some((&self.cm, sp)), msg, Bug);
-        self.handler.bump_err_count();
-    }
-    pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
-        self.span_bug(sp, &format!("unimplemented {}", msg));
-    }
-    pub fn handler<'a>(&'a self) -> &'a Handler {
-        &self.handler
-    }
-}
-
-/// 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 + Send>>,
-    pub can_emit_warnings: bool
-}
-
-impl Handler {
-    pub fn new(color_config: ColorConfig,
-               registry: Option<diagnostics::registry::Registry>,
-               can_emit_warnings: bool) -> Handler {
-        let emitter = Box::new(EmitterWriter::stderr(color_config, registry));
-        Handler::with_emitter(can_emit_warnings, emitter)
-    }
-    pub fn with_emitter(can_emit_warnings: bool, e: Box<Emitter + Send>) -> Handler {
-        Handler {
-            err_count: Cell::new(0),
-            emit: RefCell::new(e),
-            can_emit_warnings: can_emit_warnings
-        }
-    }
-    pub fn fatal(&self, msg: &str) -> FatalError {
-        self.emit.borrow_mut().emit(None, msg, None, Fatal);
-        FatalError
-    }
-    pub fn err(&self, msg: &str) {
-        self.emit.borrow_mut().emit(None, msg, None, Error);
-        self.bump_err_count();
-    }
-    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 => 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 warn(&self, msg: &str) {
-        self.emit.borrow_mut().emit(None, msg, None, Warning);
-    }
-    pub fn note(&self, msg: &str) {
-        self.emit.borrow_mut().emit(None, msg, None, Note);
-    }
-    pub fn help(&self, msg: &str) {
-        self.emit.borrow_mut().emit(None, msg, None, Help);
-    }
-    pub fn bug(&self, msg: &str) -> ! {
-        self.emit.borrow_mut().emit(None, msg, None, Bug);
-        panic!(ExplicitBug);
-    }
-    pub fn unimpl(&self, msg: &str) -> ! {
-        self.bug(&format!("unimplemented {}", msg));
-    }
-    pub fn emit(&self,
-                cmsp: Option<(&codemap::CodeMap, Span)>,
-                msg: &str,
-                lvl: Level) {
-        if lvl == Warning && !self.can_emit_warnings { return }
-        self.emit.borrow_mut().emit(cmsp, msg, None, lvl);
-    }
-    pub fn emit_with_code(&self,
-                          cmsp: Option<(&codemap::CodeMap, Span)>,
-                          msg: &str,
-                          code: &str,
-                          lvl: Level) {
-        if lvl == Warning && !self.can_emit_warnings { return }
-        self.emit.borrow_mut().emit(cmsp, msg, Some(code), lvl);
-    }
-    pub fn custom_emit(&self, cm: &codemap::CodeMap,
-                       sp: RenderSpan, msg: &str, lvl: Level) {
-        if lvl == Warning && !self.can_emit_warnings { return }
-        self.emit.borrow_mut().custom_emit(cm, sp, msg, lvl);
-    }
-}
-
-#[derive(Copy, PartialEq, Clone, Debug)]
-pub enum Level {
-    Bug,
-    Fatal,
-    Error,
-    Warning,
-    Note,
-    Help,
-}
-
-impl fmt::Display for Level {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use std::fmt::Display;
-
-        match *self {
-            Bug => "error: internal compiler error".fmt(f),
-            Fatal | Error => "error".fmt(f),
-            Warning => "warning".fmt(f),
-            Note => "note".fmt(f),
-            Help => "help".fmt(f),
-        }
-    }
-}
-
-impl Level {
-    fn color(self) -> term::color::Color {
-        match self {
-            Bug | Fatal | Error => term::color::BRIGHT_RED,
-            Warning => term::color::BRIGHT_YELLOW,
-            Note => term::color::BRIGHT_GREEN,
-            Help => term::color::BRIGHT_CYAN,
-        }
-    }
-}
-
-pub struct EmitterWriter {
-    dst: Destination,
-    registry: Option<diagnostics::registry::Registry>
-}
-
-enum Destination {
-    Terminal(Box<term::StderrTerminal>),
-    Raw(Box<Write + Send>),
-}
-
-/// 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 {
-    ($writer: expr, $style: expr, $($arg: tt)*) => {
-        $writer.print_maybe_styled(format_args!($($arg)*), $style, false)
-    }
-}
-
-macro_rules! println_maybe_styled {
-    ($writer: expr, $style: expr, $($arg: tt)*) => {
-        $writer.print_maybe_styled(format_args!($($arg)*), $style, true)
-    }
-}
-
-impl EmitterWriter {
-    pub fn stderr(color_config: ColorConfig,
-                  registry: Option<diagnostics::registry::Registry>) -> EmitterWriter {
-        let stderr = io::stderr();
-
-        let use_color = match color_config {
-            Always => true,
-            Never  => false,
-            Auto   => stderr_isatty(),
-        };
-
-        if use_color {
-            let dst = match term::stderr() {
-                Some(t) => Terminal(t),
-                None    => Raw(Box::new(stderr)),
-            };
-            EmitterWriter { dst: dst, registry: registry }
-        } else {
-            EmitterWriter { dst: Raw(Box::new(stderr)), registry: registry }
-        }
-    }
-
-    pub fn new(dst: Box<Write + Send>,
-               registry: Option<diagnostics::registry::Registry>) -> EmitterWriter {
-        EmitterWriter { dst: Raw(dst), registry: registry }
-    }
-
-    fn print_maybe_styled(&mut self,
-                          args: fmt::Arguments,
-                          color: term::Attr,
-                          print_newline_at_end: bool) -> io::Result<()> {
-        match self.dst {
-            Terminal(ref mut t) => {
-                try!(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.
-                try!(t.write_fmt(args));
-                try!(t.reset());
-                if print_newline_at_end {
-                    t.write_all(b"\n")
-                } else {
-                    Ok(())
-                }
-            }
-            Raw(ref mut w) => {
-                try!(w.write_fmt(args));
-                if print_newline_at_end {
-                    w.write_all(b"\n")
-                } else {
-                    Ok(())
-                }
-            }
-        }
-    }
-
-    fn print_diagnostic(&mut self, topic: &str, lvl: Level,
-                        msg: &str, code: Option<&str>) -> io::Result<()> {
-        if !topic.is_empty() {
-            try!(write!(&mut self.dst, "{} ", topic));
-        }
-
-        try!(print_maybe_styled!(self, term::Attr::ForegroundColor(lvl.color()),
-                                 "{}: ", lvl.to_string()));
-        try!(print_maybe_styled!(self, term::Attr::Bold, "{}", msg));
-
-        match code {
-            Some(code) => {
-                let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
-                try!(print_maybe_styled!(self, style, " [{}]", code.clone()));
-            }
-            None => ()
-        }
-        try!(write!(&mut self.dst, "\n"));
-        Ok(())
-    }
-
-    fn emit_(&mut self, cm: &codemap::CodeMap, rsp: RenderSpan,
-             msg: &str, code: Option<&str>, lvl: Level) -> io::Result<()> {
-        let sp = rsp.span();
-
-        // We cannot check equality directly with COMMAND_LINE_SP
-        // since PartialEq is manually implemented to ignore the ExpnId
-        let ss = if sp.expn_id == COMMAND_LINE_EXPN {
-            "<command line option>".to_string()
-        } else if let EndSpan(_) = rsp {
-            let span_end = Span { lo: sp.hi, hi: sp.hi, expn_id: sp.expn_id};
-            cm.span_to_string(span_end)
-        } else {
-            cm.span_to_string(sp)
-        };
-
-        try!(self.print_diagnostic(&ss[..], lvl, msg, code));
-
-        match rsp {
-            FullSpan(_) => {
-                try!(self.highlight_lines(cm, sp, lvl, cm.span_to_lines(sp)));
-                try!(self.print_macro_backtrace(cm, sp));
-            }
-            EndSpan(_) => {
-                try!(self.end_highlight_lines(cm, sp, lvl, cm.span_to_lines(sp)));
-                try!(self.print_macro_backtrace(cm, sp));
-            }
-            Suggestion(_, ref suggestion) => {
-                try!(self.highlight_suggestion(cm, sp, suggestion));
-                try!(self.print_macro_backtrace(cm, sp));
-            }
-            FileLine(..) => {
-                // no source text in this case!
-            }
-        }
-
-        match code {
-            Some(code) =>
-                match self.registry.as_ref().and_then(|registry| registry.find_description(code)) {
-                    Some(_) => {
-                        try!(self.print_diagnostic(&ss[..], Help,
-                                                   &format!("run `rustc --explain {}` to see a \
-                                                             detailed explanation", code), None));
-                    }
-                    None => ()
-                },
-            None => (),
-        }
-        Ok(())
-    }
-
-    fn highlight_suggestion(&mut self,
-                            cm: &codemap::CodeMap,
-                            sp: Span,
-                            suggestion: &str)
-                            -> io::Result<()>
-    {
-        let lines = cm.span_to_lines(sp).unwrap();
-        assert!(!lines.lines.is_empty());
-
-        // To build up the result, we want to take the snippet from the first
-        // line that precedes the span, prepend that with the suggestion, and
-        // then append the snippet from the last line that trails the span.
-        let fm = &lines.file;
-
-        let first_line = &lines.lines[0];
-        let prefix = fm.get_line(first_line.line_index)
-                       .map(|l| &l[..first_line.start_col.0])
-                       .unwrap_or("");
-
-        let last_line = lines.lines.last().unwrap();
-        let suffix = fm.get_line(last_line.line_index)
-                       .map(|l| &l[last_line.end_col.0..])
-                       .unwrap_or("");
-
-        let complete = format!("{}{}{}", prefix, suggestion, suffix);
-
-        // 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 fm = &*lines.file;
-        let mut lines = complete.lines();
-        for (line, line_index) in lines.by_ref().take(MAX_LINES).zip(first_line.line_index..) {
-            let elided_line_num = format!("{}", line_index+1);
-            try!(write!(&mut self.dst, "{0}:{1:2$} {3}\n",
-                        fm.name, "", elided_line_num.len(), line));
-        }
-
-        // if we elided some lines, add an ellipsis
-        if lines.next().is_some() {
-            let elided_line_num = format!("{}", first_line.line_index + MAX_LINES + 1);
-            try!(write!(&mut self.dst, "{0:1$} {0:2$} ...\n",
-                        "", fm.name.len(), elided_line_num.len()));
-        }
-
-        Ok(())
-    }
-
-    fn highlight_lines(&mut self,
-                       cm: &codemap::CodeMap,
-                       sp: Span,
-                       lvl: Level,
-                       lines: codemap::FileLinesResult)
-                       -> io::Result<()>
-    {
-        let lines = match lines {
-            Ok(lines) => lines,
-            Err(_) => {
-                try!(write!(&mut self.dst, "(internal compiler error: unprintable span)\n"));
-                return Ok(());
-            }
-        };
-
-        let fm = &*lines.file;
-
-        let line_strings: Option<Vec<&str>> =
-            lines.lines.iter()
-                       .map(|info| fm.get_line(info.line_index))
-                       .collect();
-
-        let line_strings = match line_strings {
-            None => { return Ok(()); }
-            Some(line_strings) => line_strings
-        };
-
-        // Display only the first MAX_LINES lines.
-        let all_lines = lines.lines.len();
-        let display_lines = cmp::min(all_lines, MAX_LINES);
-        let display_line_infos = &lines.lines[..display_lines];
-        let display_line_strings = &line_strings[..display_lines];
-
-        // Calculate the widest number to format evenly and fix #11715
-        assert!(display_line_infos.len() > 0);
-        let mut max_line_num = display_line_infos[display_line_infos.len() - 1].line_index + 1;
-        let mut digits = 0;
-        while max_line_num > 0 {
-            max_line_num /= 10;
-            digits += 1;
-        }
-
-        // Print the offending lines
-        for (line_info, line) in display_line_infos.iter().zip(display_line_strings) {
-            try!(write!(&mut self.dst, "{}:{:>width$} {}\n",
-                        fm.name,
-                        line_info.line_index + 1,
-                        line,
-                        width=digits));
-        }
-
-        // If we elided something, put an ellipsis.
-        if display_lines < all_lines {
-            let last_line_index = display_line_infos.last().unwrap().line_index;
-            let s = format!("{}:{} ", fm.name, last_line_index + 1);
-            try!(write!(&mut self.dst, "{0:1$}...\n", "", s.len()));
-        }
-
-        // FIXME (#3260)
-        // If there's one line at fault we can easily point to the problem
-        if lines.lines.len() == 1 {
-            let lo = cm.lookup_char_pos(sp.lo);
-            let mut digits = 0;
-            let mut num = (lines.lines[0].line_index + 1) / 10;
-
-            // how many digits must be indent past?
-            while num > 0 { num /= 10; digits += 1; }
-
-            let mut s = String::new();
-            // Skip is the number of characters we need to skip because they are
-            // part of the 'filename:line ' part of the previous line.
-            let skip = fm.name.chars().count() + digits + 3;
-            for _ in 0..skip {
-                s.push(' ');
-            }
-            if let Some(orig) = fm.get_line(lines.lines[0].line_index) {
-                let mut col = skip;
-                let mut lastc = ' ';
-                let mut iter = orig.chars().enumerate();
-                for (pos, ch) in iter.by_ref() {
-                    lastc = ch;
-                    if pos >= lo.col.to_usize() { break; }
-                    // Whenever a tab occurs on the previous line, we insert one on
-                    // the error-point-squiggly-line as well (instead of a space).
-                    // That way the squiggly line will usually appear in the correct
-                    // position.
-                    match ch {
-                        '\t' => {
-                            col += 8 - col%8;
-                            s.push('\t');
-                        },
-                        _ => {
-                            col += 1;
-                            s.push(' ');
-                        },
-                    }
-                }
-
-                try!(write!(&mut self.dst, "{}", s));
-                let mut s = String::from("^");
-                let count = match lastc {
-                    // Most terminals have a tab stop every eight columns by default
-                    '\t' => 8 - col%8,
-                    _ => 1,
-                };
-                col += count;
-                s.extend(::std::iter::repeat('~').take(count));
-
-                let hi = cm.lookup_char_pos(sp.hi);
-                if hi.col != lo.col {
-                    for (pos, ch) in iter {
-                        if pos >= hi.col.to_usize() { break; }
-                        let count = match ch {
-                            '\t' => 8 - col%8,
-                            _ => 1,
-                        };
-                        col += count;
-                        s.extend(::std::iter::repeat('~').take(count));
-                    }
-                }
-
-                if s.len() > 1 {
-                    // One extra squiggly is replaced by a "^"
-                    s.pop();
-                }
-
-                try!(println_maybe_styled!(self, term::Attr::ForegroundColor(lvl.color()),
-                                           "{}", s));
-            }
-        }
-        Ok(())
-    }
-
-    /// Here are the differences between this and the normal `highlight_lines`:
-    /// `end_highlight_lines` will always put arrow on the last byte of the
-    /// span (instead of the first byte). Also, when the span is too long (more
-    /// than 6 lines), `end_highlight_lines` will print the first line, then
-    /// dot dot dot, then last line, whereas `highlight_lines` prints the first
-    /// six lines.
-    #[allow(deprecated)]
-    fn end_highlight_lines(&mut self,
-                           cm: &codemap::CodeMap,
-                           sp: Span,
-                           lvl: Level,
-                           lines: codemap::FileLinesResult)
-                          -> io::Result<()> {
-        let lines = match lines {
-            Ok(lines) => lines,
-            Err(_) => {
-                try!(write!(&mut self.dst, "(internal compiler error: unprintable span)\n"));
-                return Ok(());
-            }
-        };
-
-        let fm = &*lines.file;
-
-        let lines = &lines.lines[..];
-        if lines.len() > MAX_LINES {
-            if let Some(line) = fm.get_line(lines[0].line_index) {
-                try!(write!(&mut self.dst, "{}:{} {}\n", fm.name,
-                            lines[0].line_index + 1, line));
-            }
-            try!(write!(&mut self.dst, "...\n"));
-            let last_line_index = lines[lines.len() - 1].line_index;
-            if let Some(last_line) = fm.get_line(last_line_index) {
-                try!(write!(&mut self.dst, "{}:{} {}\n", fm.name,
-                            last_line_index + 1, last_line));
-            }
-        } else {
-            for line_info in lines {
-                if let Some(line) = fm.get_line(line_info.line_index) {
-                    try!(write!(&mut self.dst, "{}:{} {}\n", fm.name,
-                                line_info.line_index + 1, line));
-                }
-            }
-        }
-        let last_line_start = format!("{}:{} ", fm.name, lines[lines.len()-1].line_index + 1);
-        let hi = cm.lookup_char_pos(sp.hi);
-        let skip = last_line_start.chars().count();
-        let mut s = String::new();
-        for _ in 0..skip {
-            s.push(' ');
-        }
-        if let Some(orig) = fm.get_line(lines[0].line_index) {
-            let iter = orig.chars().enumerate();
-            for (pos, ch) in iter {
-                // Span seems to use half-opened interval, so subtract 1
-                if pos >= hi.col.to_usize() - 1 { break; }
-                // Whenever a tab occurs on the previous line, we insert one on
-                // the error-point-squiggly-line as well (instead of a space).
-                // That way the squiggly line will usually appear in the correct
-                // position.
-                match ch {
-                    '\t' => s.push('\t'),
-                    _ => s.push(' '),
-                }
-            }
-        }
-        s.push('^');
-        println_maybe_styled!(self, term::Attr::ForegroundColor(lvl.color()),
-                              "{}", s)
-    }
-
-    fn print_macro_backtrace(&mut self,
-                             cm: &codemap::CodeMap,
-                             sp: Span)
-                             -> io::Result<()> {
-        let mut last_span = codemap::DUMMY_SP;
-        let mut sp_opt = Some(sp);
-
-        while let Some(sp) = sp_opt {
-            sp_opt = try!(cm.with_expn_info(sp.expn_id, |expn_info| -> io::Result<_> {
-                match expn_info {
-                    Some(ei) => {
-                        let (pre, post) = match ei.callee.format {
-                            codemap::MacroAttribute(..) => ("#[", "]"),
-                            codemap::MacroBang(..) => ("", "!"),
-                        };
-                        // Don't print recursive invocations
-                        if ei.call_site != last_span {
-                            last_span = ei.call_site;
-
-                            let mut diag_string = format!("in this expansion of {}{}{}",
-                                                          pre,
-                                                          ei.callee.name(),
-                                                          post);
-
-                            if let Some(def_site_span) = ei.callee.span {
-                                diag_string.push_str(&format!(" (defined in {})",
-                                                              cm.span_to_filename(def_site_span)));
-                            }
-
-                            try!(self.print_diagnostic(&cm.span_to_string(ei.call_site),
-                                                       Note,
-                                                       &diag_string,
-                                                       None));
-                        }
-                        Ok(Some(ei.call_site))
-                    }
-                    None => Ok(None)
-                }
-            }));
-        }
-
-        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
-    }
-}
-
-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(),
-        }
-    }
-}
-
-impl Emitter for EmitterWriter {
-    fn emit(&mut self,
-            cmsp: Option<(&codemap::CodeMap, Span)>,
-            msg: &str, code: Option<&str>, lvl: Level) {
-        let error = match cmsp {
-            Some((cm, COMMAND_LINE_SP)) => self.emit_(cm,
-                                                FileLine(COMMAND_LINE_SP),
-                                                msg, code, lvl),
-            Some((cm, sp)) => self.emit_(cm, FullSpan(sp), msg, code, lvl),
-            None => self.print_diagnostic("", lvl, msg, code),
-        };
-
-        match error {
-            Ok(()) => {}
-            Err(e) => panic!("failed to print diagnostics: {:?}", e),
-        }
-    }
-
-    fn custom_emit(&mut self, cm: &codemap::CodeMap,
-                   sp: RenderSpan, msg: &str, lvl: Level) {
-        match self.emit_(cm, sp, msg, None, lvl) {
-            Ok(()) => {}
-            Err(e) => panic!("failed to print diagnostics: {:?}", e),
-        }
-    }
-}
-
-pub fn expect<T, M>(diag: &SpanHandler, opt: Option<T>, msg: M) -> T where
-    M: FnOnce() -> String,
-{
-    match opt {
-        Some(t) => t,
-        None => diag.handler().bug(&msg()),
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::{EmitterWriter, Level};
-    use codemap::{mk_sp, CodeMap};
-    use std::sync::{Arc, Mutex};
-    use std::io::{self, Write};
-    use std::str::from_utf8;
-
-    // Diagnostic doesn't align properly in span where line number increases by one digit
-    #[test]
-    fn test_hilight_suggestion_issue_11715() {
-        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(()) }
-        }
-        let data = Arc::new(Mutex::new(Vec::new()));
-        let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), None);
-        let cm = CodeMap::new();
-        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()[7];
-        let end = file.lines.borrow()[11];
-        let sp = mk_sp(start, end);
-        let lvl = Level::Error;
-        println!("span_to_lines");
-        let lines = cm.span_to_lines(sp);
-        println!("highlight_lines");
-        ew.highlight_lines(&cm, sp, lvl, lines).unwrap();
-        println!("done");
-        let vec = data.lock().unwrap().clone();
-        let vec: &[u8] = &vec;
-        let str = from_utf8(vec).unwrap();
-        println!("{}", str);
-        assert_eq!(str, "dummy.txt: 8         line8\n\
-                         dummy.txt: 9         line9\n\
-                         dummy.txt:10         line10\n\
-                         dummy.txt:11         e-lä-vän\n\
-                         dummy.txt:12         tolv\n");
-    }
-}
index 3c8347f8a8e0e165b3852e1c52cf95df903188e5..95a74d875545fcfeac1fd6ef15e10dbe06ab238a 100644 (file)
@@ -30,6 +30,14 @@ macro_rules! span_err {
     })
 }
 
+#[macro_export]
+macro_rules! span_warn {
+    ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
+        __diagnostic_used!($code);
+        $session.span_warn_with_code($span, &format!($($message)*), stringify!($code))
+    })
+}
+
 #[macro_export]
 macro_rules! span_err_or_warn {
     ($is_warning:expr, $session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
@@ -43,31 +51,59 @@ macro_rules! span_err_or_warn {
 }
 
 #[macro_export]
-macro_rules! span_warn {
+macro_rules! struct_span_fatal {
     ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
         __diagnostic_used!($code);
-        $session.span_warn_with_code($span, &format!($($message)*), stringify!($code))
+        $session.struct_span_fatal_with_code($span, &format!($($message)*), stringify!($code))
+    })
+}
+
+#[macro_export]
+macro_rules! struct_span_err {
+    ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
+        __diagnostic_used!($code);
+        $session.struct_span_err_with_code($span, &format!($($message)*), stringify!($code))
+    })
+}
+
+#[macro_export]
+macro_rules! struct_span_warn {
+    ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
+        __diagnostic_used!($code);
+        $session.struct_span_warn_with_code($span, &format!($($message)*), stringify!($code))
+    })
+}
+
+#[macro_export]
+macro_rules! struct_span_err_or_warn {
+    ($is_warning:expr, $session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
+        __diagnostic_used!($code);
+        if $is_warning {
+            $session.struct_span_warn_with_code($span, &format!($($message)*), stringify!($code))
+        } else {
+            $session.struct_span_err_with_code($span, &format!($($message)*), stringify!($code))
+        }
     })
 }
 
 #[macro_export]
 macro_rules! span_note {
-    ($session:expr, $span:expr, $($message:tt)*) => ({
-        ($session).span_note($span, &format!($($message)*))
+    ($err:expr, $span:expr, $($message:tt)*) => ({
+        ($err).span_note($span, &format!($($message)*));
     })
 }
 
 #[macro_export]
 macro_rules! span_help {
-    ($session:expr, $span:expr, $($message:tt)*) => ({
-        ($session).span_help($span, &format!($($message)*))
+    ($err:expr, $span:expr, $($message:tt)*) => ({
+        ($err).span_help($span, &format!($($message)*));
     })
 }
 
 #[macro_export]
 macro_rules! fileline_help {
-    ($session:expr, $span:expr, $($message:tt)*) => ({
-        ($session).fileline_help($span, &format!($($message)*))
+    ($err:expr, $span:expr, $($message:tt)*) => ({
+        ($err).fileline_help($span, &format!($($message)*));
     })
 }
 
index be0d5729c7009fe738158e39765379f73e6c46f1..d17ca3892dc684746c2e3f379b28447834940ff8 100644 (file)
@@ -62,10 +62,10 @@ pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt,
         match diagnostics.get_mut(&code.name) {
             // Previously used errors.
             Some(&mut ErrorInfo { description: _, use_site: Some(previous_span) }) => {
-                ecx.span_warn(span, &format!(
+                ecx.struct_span_warn(span, &format!(
                     "diagnostic code {} already used", code
-                ));
-                ecx.span_note(previous_span, "previous invocation");
+                )).span_note(previous_span, "previous invocation")
+                  .emit();
             }
             // Newly used errors.
             Some(ref mut info) => {
diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs
new file mode 100644 (file)
index 0000000..c21bf1e
--- /dev/null
@@ -0,0 +1,690 @@
+// 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, COMMAND_LINE_EXPN, Pos, Span};
+use diagnostics;
+
+use errors::{Level, RenderSpan, DiagnosticBuilder};
+use errors::RenderSpan::*;
+use errors::Level::*;
+
+use std::{cmp, fmt};
+use std::io::prelude::*;
+use std::io;
+use std::rc::Rc;
+use term;
+
+
+pub trait Emitter {
+    fn emit(&mut self, span: Option<Span>, msg: &str, code: Option<&str>, lvl: Level);
+    fn custom_emit(&mut self, sp: RenderSpan, msg: &str, lvl: Level);
+
+    /// Emit a structured diagnostic.
+    fn emit_struct(&mut self, db: &DiagnosticBuilder) {
+        self.emit(db.span, &db.message, db.code.as_ref().map(|s| &**s), db.level);
+        for child in &db.children {
+            match child.render_span {
+                Some(ref sp) => self.custom_emit(sp.clone(), &child.message, child.level),
+                None => self.emit(child.span, &child.message, None, child.level),
+            }
+        }
+    }
+}
+
+/// maximum number of lines we will print for each error; arbitrary.
+const MAX_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 Emitter for BasicEmitter {
+    fn emit(&mut self,
+            sp: Option<Span>,
+            msg: &str,
+            code: Option<&str>,
+            lvl: Level) {
+        assert!(sp.is_none(), "BasicEmitter can't handle spans");
+        if let Err(e) = print_diagnostic(&mut self.dst, "", lvl, msg, code) {
+            panic!("failed to print diagnostics: {:?}", e);
+        }
+
+    }
+
+    fn custom_emit(&mut self, _: RenderSpan, _: &str, _: Level) {
+        panic!("BasicEmitter can't handle custom_emit");
+    }
+}
+
+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>,
+}
+
+impl Emitter for EmitterWriter {
+    fn emit(&mut self,
+            sp: Option<Span>,
+            msg: &str,
+            code: Option<&str>,
+            lvl: Level) {
+        let error = match sp {
+            Some(COMMAND_LINE_SP) => self.emit_(FileLine(COMMAND_LINE_SP), msg, code, lvl),
+            Some(sp) => self.emit_(FullSpan(sp), msg, code, lvl),
+            None => print_diagnostic(&mut self.dst, "", lvl, msg, code),
+        };
+
+        if let Err(e) = error {
+            panic!("failed to print diagnostics: {:?}", e);
+        }
+    }
+
+    fn custom_emit(&mut self,
+                   sp: RenderSpan,
+                   msg: &str,
+                   lvl: Level) {
+        if let Err(e) = self.emit_(sp, msg, None, lvl) {
+            panic!("failed to print diagnostics: {:?}", 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 {
+        if color_config.use_color() {
+            let dst = Destination::from_stderr();
+            EmitterWriter { dst: dst, registry: registry, cm: code_map }
+        } else {
+            EmitterWriter { dst: Raw(Box::new(io::stderr())), registry: registry, cm: code_map }
+        }
+    }
+
+    pub fn new(dst: Box<Write + Send>,
+               registry: Option<diagnostics::registry::Registry>,
+               code_map: Rc<codemap::CodeMap>)
+               -> EmitterWriter {
+        EmitterWriter { dst: Raw(dst), registry: registry, cm: code_map }
+    }
+
+    fn emit_(&mut self,
+             rsp: RenderSpan,
+             msg: &str,
+             code: Option<&str>,
+             lvl: Level)
+             -> io::Result<()> {
+        let sp = rsp.span();
+
+        // We cannot check equality directly with COMMAND_LINE_SP
+        // since PartialEq is manually implemented to ignore the ExpnId
+        let ss = if sp.expn_id == COMMAND_LINE_EXPN {
+            "<command line option>".to_string()
+        } else if let EndSpan(_) = rsp {
+            let span_end = Span { lo: sp.hi, hi: sp.hi, expn_id: sp.expn_id};
+            self.cm.span_to_string(span_end)
+        } else {
+            self.cm.span_to_string(sp)
+        };
+
+        try!(print_diagnostic(&mut self.dst, &ss[..], lvl, msg, code));
+
+        match rsp {
+            FullSpan(_) => {
+                let lines = self.cm.span_to_lines(sp);
+                try!(self.highlight_lines(sp, lvl, lines));
+                try!(self.print_macro_backtrace(sp));
+            }
+            EndSpan(_) => {
+                let lines = self.cm.span_to_lines(sp);
+                try!(self.end_highlight_lines(sp, lvl, lines));
+                try!(self.print_macro_backtrace(sp));
+            }
+            Suggestion(_, ref suggestion) => {
+                try!(self.highlight_suggestion(sp, suggestion));
+                try!(self.print_macro_backtrace(sp));
+            }
+            FileLine(..) => {
+                // no source text in this case!
+            }
+        }
+
+        match code {
+            Some(code) =>
+                match self.registry.as_ref().and_then(|registry| registry.find_description(code)) {
+                    Some(_) => {
+                        try!(print_diagnostic(&mut self.dst, &ss[..], Help,
+                                              &format!("run `rustc --explain {}` to see a \
+                                                       detailed explanation", code), None));
+                    }
+                    None => ()
+                },
+            None => (),
+        }
+        Ok(())
+    }
+
+    fn highlight_suggestion(&mut self,
+                            sp: Span,
+                            suggestion: &str)
+                            -> io::Result<()>
+    {
+        let lines = self.cm.span_to_lines(sp).unwrap();
+        assert!(!lines.lines.is_empty());
+
+        // To build up the result, we want to take the snippet from the first
+        // line that precedes the span, prepend that with the suggestion, and
+        // then append the snippet from the last line that trails the span.
+        let fm = &lines.file;
+
+        let first_line = &lines.lines[0];
+        let prefix = fm.get_line(first_line.line_index)
+                       .map(|l| &l[..first_line.start_col.0])
+                       .unwrap_or("");
+
+        let last_line = lines.lines.last().unwrap();
+        let suffix = fm.get_line(last_line.line_index)
+                       .map(|l| &l[last_line.end_col.0..])
+                       .unwrap_or("");
+
+        let complete = format!("{}{}{}", prefix, suggestion, suffix);
+
+        // 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 fm = &*lines.file;
+        let mut lines = complete.lines();
+        for (line, line_index) in lines.by_ref().take(MAX_LINES).zip(first_line.line_index..) {
+            let elided_line_num = format!("{}", line_index+1);
+            try!(write!(&mut self.dst, "{0}:{1:2$} {3}\n",
+                        fm.name, "", elided_line_num.len(), line));
+        }
+
+        // if we elided some lines, add an ellipsis
+        if lines.next().is_some() {
+            let elided_line_num = format!("{}", first_line.line_index + MAX_LINES + 1);
+            try!(write!(&mut self.dst, "{0:1$} {0:2$} ...\n",
+                        "", fm.name.len(), elided_line_num.len()));
+        }
+
+        Ok(())
+    }
+
+    fn highlight_lines(&mut self,
+                       sp: Span,
+                       lvl: Level,
+                       lines: codemap::FileLinesResult)
+                       -> io::Result<()>
+    {
+        let lines = match lines {
+            Ok(lines) => lines,
+            Err(_) => {
+                try!(write!(&mut self.dst, "(internal compiler error: unprintable span)\n"));
+                return Ok(());
+            }
+        };
+
+        let fm = &*lines.file;
+
+        let line_strings: Option<Vec<&str>> =
+            lines.lines.iter()
+                       .map(|info| fm.get_line(info.line_index))
+                       .collect();
+
+        let line_strings = match line_strings {
+            None => { return Ok(()); }
+            Some(line_strings) => line_strings
+        };
+
+        // Display only the first MAX_LINES lines.
+        let all_lines = lines.lines.len();
+        let display_lines = cmp::min(all_lines, MAX_LINES);
+        let display_line_infos = &lines.lines[..display_lines];
+        let display_line_strings = &line_strings[..display_lines];
+
+        // Calculate the widest number to format evenly and fix #11715
+        assert!(display_line_infos.len() > 0);
+        let mut max_line_num = display_line_infos[display_line_infos.len() - 1].line_index + 1;
+        let mut digits = 0;
+        while max_line_num > 0 {
+            max_line_num /= 10;
+            digits += 1;
+        }
+
+        // Print the offending lines
+        for (line_info, line) in display_line_infos.iter().zip(display_line_strings) {
+            try!(write!(&mut self.dst, "{}:{:>width$} {}\n",
+                        fm.name,
+                        line_info.line_index + 1,
+                        line,
+                        width=digits));
+        }
+
+        // If we elided something, put an ellipsis.
+        if display_lines < all_lines {
+            let last_line_index = display_line_infos.last().unwrap().line_index;
+            let s = format!("{}:{} ", fm.name, last_line_index + 1);
+            try!(write!(&mut self.dst, "{0:1$}...\n", "", s.len()));
+        }
+
+        // FIXME (#3260)
+        // If there's one line at fault we can easily point to the problem
+        if lines.lines.len() == 1 {
+            let lo = self.cm.lookup_char_pos(sp.lo);
+            let mut digits = 0;
+            let mut num = (lines.lines[0].line_index + 1) / 10;
+
+            // how many digits must be indent past?
+            while num > 0 { num /= 10; digits += 1; }
+
+            let mut s = String::new();
+            // Skip is the number of characters we need to skip because they are
+            // part of the 'filename:line ' part of the previous line.
+            let skip = fm.name.chars().count() + digits + 3;
+            for _ in 0..skip {
+                s.push(' ');
+            }
+            if let Some(orig) = fm.get_line(lines.lines[0].line_index) {
+                let mut col = skip;
+                let mut lastc = ' ';
+                let mut iter = orig.chars().enumerate();
+                for (pos, ch) in iter.by_ref() {
+                    lastc = ch;
+                    if pos >= lo.col.to_usize() { break; }
+                    // Whenever a tab occurs on the previous line, we insert one on
+                    // the error-point-squiggly-line as well (instead of a space).
+                    // That way the squiggly line will usually appear in the correct
+                    // position.
+                    match ch {
+                        '\t' => {
+                            col += 8 - col%8;
+                            s.push('\t');
+                        },
+                        _ => {
+                            col += 1;
+                            s.push(' ');
+                        },
+                    }
+                }
+
+                try!(write!(&mut self.dst, "{}", s));
+                let mut s = String::from("^");
+                let count = match lastc {
+                    // Most terminals have a tab stop every eight columns by default
+                    '\t' => 8 - col%8,
+                    _ => 1,
+                };
+                col += count;
+                s.extend(::std::iter::repeat('~').take(count));
+
+                let hi = self.cm.lookup_char_pos(sp.hi);
+                if hi.col != lo.col {
+                    for (pos, ch) in iter {
+                        if pos >= hi.col.to_usize() { break; }
+                        let count = match ch {
+                            '\t' => 8 - col%8,
+                            _ => 1,
+                        };
+                        col += count;
+                        s.extend(::std::iter::repeat('~').take(count));
+                    }
+                }
+
+                if s.len() > 1 {
+                    // One extra squiggly is replaced by a "^"
+                    s.pop();
+                }
+
+                try!(println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()),
+                                           "{}", s));
+            }
+        }
+        Ok(())
+    }
+
+    /// Here are the differences between this and the normal `highlight_lines`:
+    /// `end_highlight_lines` will always put arrow on the last byte of the
+    /// span (instead of the first byte). Also, when the span is too long (more
+    /// than 6 lines), `end_highlight_lines` will print the first line, then
+    /// dot dot dot, then last line, whereas `highlight_lines` prints the first
+    /// six lines.
+    #[allow(deprecated)]
+    fn end_highlight_lines(&mut self,
+                           sp: Span,
+                           lvl: Level,
+                           lines: codemap::FileLinesResult)
+                          -> io::Result<()> {
+        let lines = match lines {
+            Ok(lines) => lines,
+            Err(_) => {
+                try!(write!(&mut self.dst, "(internal compiler error: unprintable span)\n"));
+                return Ok(());
+            }
+        };
+
+        let fm = &*lines.file;
+
+        let lines = &lines.lines[..];
+        if lines.len() > MAX_LINES {
+            if let Some(line) = fm.get_line(lines[0].line_index) {
+                try!(write!(&mut self.dst, "{}:{} {}\n", fm.name,
+                            lines[0].line_index + 1, line));
+            }
+            try!(write!(&mut self.dst, "...\n"));
+            let last_line_index = lines[lines.len() - 1].line_index;
+            if let Some(last_line) = fm.get_line(last_line_index) {
+                try!(write!(&mut self.dst, "{}:{} {}\n", fm.name,
+                            last_line_index + 1, last_line));
+            }
+        } else {
+            for line_info in lines {
+                if let Some(line) = fm.get_line(line_info.line_index) {
+                    try!(write!(&mut self.dst, "{}:{} {}\n", fm.name,
+                                line_info.line_index + 1, line));
+                }
+            }
+        }
+        let last_line_start = format!("{}:{} ", fm.name, lines[lines.len()-1].line_index + 1);
+        let hi = self.cm.lookup_char_pos(sp.hi);
+        let skip = last_line_start.chars().count();
+        let mut s = String::new();
+        for _ in 0..skip {
+            s.push(' ');
+        }
+        if let Some(orig) = fm.get_line(lines[0].line_index) {
+            let iter = orig.chars().enumerate();
+            for (pos, ch) in iter {
+                // Span seems to use half-opened interval, so subtract 1
+                if pos >= hi.col.to_usize() - 1 { break; }
+                // Whenever a tab occurs on the previous line, we insert one on
+                // the error-point-squiggly-line as well (instead of a space).
+                // That way the squiggly line will usually appear in the correct
+                // position.
+                match ch {
+                    '\t' => s.push('\t'),
+                    _ => s.push(' '),
+                }
+            }
+        }
+        s.push('^');
+        println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()),
+                              "{}", s)
+    }
+
+    fn print_macro_backtrace(&mut self,
+                             sp: Span)
+                             -> io::Result<()> {
+        let mut last_span = codemap::DUMMY_SP;
+        let mut span = sp;
+
+        loop {
+            let span_name_span = self.cm.with_expn_info(span.expn_id, |expn_info| {
+                expn_info.map(|ei| {
+                    let (pre, post) = match ei.callee.format {
+                        codemap::MacroAttribute(..) => ("#[", "]"),
+                        codemap::MacroBang(..) => ("", "!"),
+                    };
+                    let macro_decl_name = format!("in this expansion of {}{}{}",
+                                                  pre,
+                                                  ei.callee.name(),
+                                                  post);
+                    let def_site_span = ei.callee.span;
+                    (ei.call_site, macro_decl_name, def_site_span)
+                })
+            });
+            let (macro_decl_name, def_site_span) = match span_name_span {
+                None => break,
+                Some((sp, macro_decl_name, def_site_span)) => {
+                    span = sp;
+                    (macro_decl_name, def_site_span)
+                }
+            };
+
+            // Don't print recursive invocations
+            if span != last_span {
+                let mut diag_string = macro_decl_name;
+                if let Some(def_site_span) = 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(span);
+                try!(print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None));
+            }
+            last_span = span;
+        }
+
+        Ok(())
+    }
+}
+
+fn print_diagnostic(dst: &mut Destination,
+                    topic: &str,
+                    lvl: Level,
+                    msg: &str,
+                    code: Option<&str>)
+                    -> io::Result<()> {
+    if !topic.is_empty() {
+        try!(write!(dst, "{} ", topic));
+    }
+
+    try!(print_maybe_styled!(dst, term::Attr::ForegroundColor(lvl.color()),
+                             "{}: ", lvl.to_string()));
+    try!(print_maybe_styled!(dst, term::Attr::Bold, "{}", msg));
+
+    match code {
+        Some(code) => {
+            let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
+            try!(print_maybe_styled!(dst, style, " [{}]", code.clone()));
+        }
+        None => ()
+    }
+    try!(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 print_maybe_styled(&mut self,
+                          args: fmt::Arguments,
+                          color: term::Attr,
+                          print_newline_at_end: bool)
+                          -> io::Result<()> {
+        match *self {
+            Terminal(ref mut t) => {
+                try!(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.
+                try!(t.write_fmt(args));
+                try!(t.reset());
+                if print_newline_at_end {
+                    t.write_all(b"\n")
+                } else {
+                    Ok(())
+                }
+            }
+            Raw(ref mut w) => {
+                try!(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;
+    use super::EmitterWriter;
+    use codemap::{mk_sp, CodeMap};
+    use std::sync::{Arc, Mutex};
+    use std::io::{self, Write};
+    use std::str::from_utf8;
+    use std::rc::Rc;
+
+    // Diagnostic doesn't align properly in span where line number increases by one digit
+    #[test]
+    fn test_hilight_suggestion_issue_11715() {
+        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(()) }
+        }
+        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()[7];
+        let end = file.lines.borrow()[11];
+        let sp = mk_sp(start, end);
+        let lvl = Level::Error;
+        println!("span_to_lines");
+        let lines = cm.span_to_lines(sp);
+        println!("highlight_lines");
+        ew.highlight_lines(sp, lvl, lines).unwrap();
+        println!("done");
+        let vec = data.lock().unwrap().clone();
+        let vec: &[u8] = &vec;
+        let str = from_utf8(vec).unwrap();
+        println!("{}", str);
+        assert_eq!(str, "dummy.txt: 8         line8\n\
+                         dummy.txt: 9         line9\n\
+                         dummy.txt:10         line10\n\
+                         dummy.txt:11         e-lä-vän\n\
+                         dummy.txt:12         tolv\n");
+    }
+}
diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/errors/json.rs
new file mode 100644 (file)
index 0000000..713190e
--- /dev/null
@@ -0,0 +1,233 @@
+// 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.
+
+//! 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::{Span, CodeMap};
+use diagnostics::registry::Registry;
+use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan};
+use errors::emitter::Emitter;
+
+use std::rc::Rc;
+use std::io::{self, Write};
+
+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: Option<Span>, 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 custom_emit(&mut self, sp: RenderSpan, msg: &str, level: Level) {
+        let data = Diagnostic::from_render_span(&sp, msg, 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,
+    span: Option<DiagnosticSpan>,
+    /// Assocaited diagnostic messages.
+    children: Vec<Diagnostic<'a>>,
+}
+
+#[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,
+}
+
+#[derive(RustcEncodable)]
+struct DiagnosticCode {
+    /// The code itself.
+    code: String,
+    /// An explanation for the code.
+    explanation: Option<&'static str>,
+}
+
+impl<'a> Diagnostic<'a> {
+    fn new(span: Option<Span>,
+           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(),
+            span: span.map(|sp| DiagnosticSpan::from_span(sp, je)),
+            children: vec![],
+        }
+    }
+
+    fn from_render_span(span: &RenderSpan,
+                        msg: &'a str,
+                        level: Level,
+                        je: &JsonEmitter)
+                        -> Diagnostic<'a> {
+        Diagnostic {
+            message: msg,
+            code: None,
+            level: level.to_str(),
+            span: Some(DiagnosticSpan::from_render_span(span, je)),
+            children: vec![],
+        }
+    }
+
+    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(),
+            span: db.span.map(|sp| DiagnosticSpan::from_span(sp, je)),
+            children: db.children.iter().map(|c| {
+                Diagnostic::from_sub_diagnostic(c, je)
+            }).collect(),
+        }
+    }
+
+    fn from_sub_diagnostic<'c>(db: &'c SubDiagnostic, je: &JsonEmitter) -> Diagnostic<'c> {
+        Diagnostic {
+            message: &db.message,
+            code: None,
+            level: db.level.to_str(),
+            span: db.render_span.as_ref()
+                    .map(|sp| DiagnosticSpan::from_render_span(sp, je))
+                    .or_else(|| db.span.map(|sp| DiagnosticSpan::from_span(sp, je))),
+            children: vec![],
+        }
+    }
+}
+
+impl DiagnosticSpan {
+    fn from_span(span: Span, je: &JsonEmitter) -> DiagnosticSpan {
+        let start = je.cm.lookup_char_pos(span.lo);
+        let end = je.cm.lookup_char_pos(span.hi);
+        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,
+        }
+    }
+
+    fn from_render_span(span: &RenderSpan, je: &JsonEmitter) -> DiagnosticSpan {
+        match *span {
+            // FIXME(#30701) handle Suggestion properly
+            RenderSpan::FullSpan(sp) | RenderSpan::Suggestion(sp, _) => {
+                DiagnosticSpan::from_span(sp, je)
+            }
+            RenderSpan::EndSpan(span) => {
+                let end = je.cm.lookup_char_pos(span.hi);
+                DiagnosticSpan {
+                    file_name: end.file.name.clone(),
+                    byte_start: span.lo.0,
+                    byte_end: span.hi.0,
+                    line_start: 0,
+                    line_end: end.line,
+                    column_start: 0,
+                    column_end: end.col.0 + 1,
+                }
+            }
+            RenderSpan::FileLine(span) => {
+                let start = je.cm.lookup_char_pos(span.lo);
+                let end = je.cm.lookup_char_pos(span.hi);
+                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: 0,
+                    column_end: 0,
+                }
+            }
+        }
+    }
+}
+
+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,
+            }
+        })
+    }
+}
diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs
new file mode 100644 (file)
index 0000000..6983c74
--- /dev/null
@@ -0,0 +1,603 @@
+// 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, Span};
+use diagnostics;
+use errors::emitter::{Emitter, EmitterWriter};
+
+use std::cell::{RefCell, Cell};
+use std::{error, fmt};
+use std::io::prelude::*;
+use std::rc::Rc;
+use term;
+
+pub mod emitter;
+pub mod json;
+
+#[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(Span),
+
+    /// Similar to a FullSpan, but the cited position is the end of
+    /// the span, instead of the start. Used, at least, for telling
+    /// compiletest/runtest to look at the last line of the span
+    /// (since `end_highlight_lines` displays an arrow to the end
+    /// of the span).
+    EndSpan(Span),
+
+    /// 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 the `String` is spliced
+    /// into the lines in place of the code covered by the span.
+    Suggestion(Span, String),
+
+    /// A FileLine renders with just a line for the message prefixed
+    /// by file:linenum.
+    FileLine(Span),
+}
+
+impl RenderSpan {
+    fn span(&self) -> Span {
+        match *self {
+            FullSpan(s) |
+            Suggestion(s, _) |
+            EndSpan(s) |
+            FileLine(s) =>
+                s
+        }
+    }
+}
+
+/// 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]
+pub struct DiagnosticBuilder<'a> {
+    emitter: &'a RefCell<Box<Emitter>>,
+    level: Level,
+    message: String,
+    code: Option<String>,
+    span: Option<Span>,
+    children: Vec<SubDiagnostic>,
+}
+
+/// For example a note attached to an error.
+struct SubDiagnostic {
+    level: Level,
+    message: String,
+    span: Option<Span>,
+    render_span: Option<RenderSpan>,
+}
+
+impl<'a> DiagnosticBuilder<'a> {
+    /// Emit the diagnostic.
+    pub fn emit(&mut self) {
+        if self.cancelled() {
+            return;
+        }
+
+        self.emitter.borrow_mut().emit_struct(&self);
+        self.cancel();
+
+        // 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
+    }
+
+    pub fn note(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Note, msg, None, None);
+        self
+    }
+    pub fn span_note(&mut self ,
+                     sp: Span,
+                     msg: &str)
+                     -> &mut DiagnosticBuilder<'a> {
+        self.sub(Level::Note, msg, Some(sp), None);
+        self
+    }
+    pub fn warn(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Warning, msg, None, None);
+        self
+    }
+    pub fn span_warn(&mut self,
+                     sp: Span,
+                     msg: &str)
+                     -> &mut DiagnosticBuilder<'a> {
+        self.sub(Level::Warning, msg, Some(sp), None);
+        self
+    }
+    pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Help, msg, None, None);
+        self
+    }
+    pub fn span_help(&mut self ,
+                     sp: Span,
+                     msg: &str)
+                     -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Help, msg, Some(sp), None);
+        self
+    }
+    /// Prints out a message with a suggested edit of the code.
+    ///
+    /// See `diagnostic::RenderSpan::Suggestion` for more information.
+    pub fn span_suggestion(&mut self ,
+                           sp: Span,
+                           msg: &str,
+                           suggestion: String)
+                           -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Help, msg, Some(sp), Some(Suggestion(sp, suggestion)));
+        self
+    }
+    pub fn span_end_note(&mut self ,
+                         sp: Span,
+                         msg: &str)
+                         -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Note, msg, Some(sp), Some(EndSpan(sp)));
+        self
+    }
+    pub fn fileline_warn(&mut self ,
+                         sp: Span,
+                         msg: &str)
+                         -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Warning, msg, Some(sp), Some(FileLine(sp)));
+        self
+    }
+    pub fn fileline_note(&mut self ,
+                         sp: Span,
+                         msg: &str)
+                         -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Note, msg, Some(sp), Some(FileLine(sp)));
+        self
+    }
+    pub fn fileline_help(&mut self ,
+                         sp: Span,
+                         msg: &str)
+                         -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Help, msg, Some(sp), Some(FileLine(sp)));
+        self
+    }
+
+    pub fn span(&mut self, sp: Span) -> &mut Self {
+        self.span = Some(sp);
+        self
+    }
+
+    pub fn code(&mut self, s: String) -> &mut Self {
+        self.code = Some(s);
+        self
+    }
+
+    /// Convenience function for internal use, clients should use one of the
+    /// struct_* methods on Handler.
+    fn new(emitter: &'a RefCell<Box<Emitter>>,
+           level: Level,
+           message: &str) -> DiagnosticBuilder<'a>  {
+        DiagnosticBuilder {
+            emitter: emitter,
+            level: level,
+            message: message.to_owned(),
+            code: None,
+            span: None,
+            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: Option<Span>,
+           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 !self.cancelled() {
+            self.emitter.borrow_mut().emit(None, "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,
+    delayed_span_bug: RefCell<Option<(codemap::Span, 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,
+            delayed_span_bug: RefCell::new(None),
+        }
+    }
+
+    pub fn struct_dummy<'a>(&'a self) -> DiagnosticBuilder<'a> {
+        DiagnosticBuilder::new(&self.emit, Level::Cancelled, "")
+    }
+
+    pub fn struct_span_warn<'a>(&'a self,
+                                sp: Span,
+                                msg: &str)
+                                -> DiagnosticBuilder<'a> {
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
+        result.span(sp);
+        if !self.can_emit_warnings {
+            result.cancel();
+        }
+        result
+    }
+    pub fn struct_span_warn_with_code<'a>(&'a self,
+                                          sp: Span,
+                                          msg: &str,
+                                          code: &str)
+                                          -> DiagnosticBuilder<'a> {
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
+        result.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.emit, Level::Warning, msg);
+        if !self.can_emit_warnings {
+            result.cancel();
+        }
+        result
+    }
+    pub fn struct_span_err<'a>(&'a self,
+                               sp: Span,
+                               msg: &str)
+                               -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
+        result.span(sp);
+        result
+    }
+    pub fn struct_span_err_with_code<'a>(&'a self,
+                                         sp: Span,
+                                         msg: &str,
+                                         code: &str)
+                                         -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
+        result.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.emit, Level::Error, msg)
+    }
+    pub fn struct_span_fatal<'a>(&'a self,
+                                 sp: Span,
+                                 msg: &str)
+                                 -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
+        result.span(sp);
+        result
+    }
+    pub fn struct_span_fatal_with_code<'a>(&'a self,
+                                           sp: Span,
+                                           msg: &str,
+                                           code: &str)
+                                           -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
+        result.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.emit, 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();
+    }
+
+    pub fn span_fatal(&self, sp: Span, msg: &str) -> FatalError {
+        if self.treat_err_as_bug {
+            self.span_bug(sp, msg);
+        }
+        self.emit(Some(sp), msg, Fatal);
+        self.bump_err_count();
+        return FatalError;
+    }
+    pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> FatalError {
+        if self.treat_err_as_bug {
+            self.span_bug(sp, msg);
+        }
+        self.emit_with_code(Some(sp), msg, code, Fatal);
+        self.bump_err_count();
+        return FatalError;
+    }
+    pub fn span_err(&self, sp: Span, msg: &str) {
+        if self.treat_err_as_bug {
+            self.span_bug(sp, msg);
+        }
+        self.emit(Some(sp), msg, Error);
+        self.bump_err_count();
+    }
+    pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
+        if self.treat_err_as_bug {
+            self.span_bug(sp, msg);
+        }
+        self.emit_with_code(Some(sp), msg, code, Error);
+        self.bump_err_count();
+    }
+    pub fn span_warn(&self, sp: Span, msg: &str) {
+        self.emit(Some(sp), msg, Warning);
+    }
+    pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) {
+        self.emit_with_code(Some(sp), msg, code, Warning);
+    }
+    pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
+        self.emit(Some(sp), msg, Bug);
+        panic!(ExplicitBug);
+    }
+    pub fn delay_span_bug(&self, sp: Span, msg: &str) {
+        let mut delayed = self.delayed_span_bug.borrow_mut();
+        *delayed = Some((sp, msg.to_string()));
+    }
+    pub fn span_bug_no_panic(&self, sp: Span, msg: &str) {
+        self.emit(Some(sp), msg, Bug);
+        self.bump_err_count();
+    }
+    pub fn span_note_without_error(&self, sp: Span, msg: &str) {
+        self.emit.borrow_mut().emit(Some(sp), msg, None, Note);
+    }
+    pub fn span_unimpl(&self, sp: Span, 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(None, 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(None, msg, None, Error);
+        self.bump_err_count();
+    }
+    pub fn warn(&self, msg: &str) {
+        self.emit.borrow_mut().emit(None, msg, None, Warning);
+    }
+    pub fn note_without_error(&self, msg: &str) {
+        self.emit.borrow_mut().emit(None, msg, None, Note);
+    }
+    pub fn bug(&self, msg: &str) -> ! {
+        self.emit.borrow_mut().emit(None, 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((span, ref errmsg)) => {
+                        self.span_bug(span, 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,
+                sp: Option<Span>,
+                msg: &str,
+                lvl: Level) {
+        if lvl == Warning && !self.can_emit_warnings { return }
+        self.emit.borrow_mut().emit(sp, msg, None, lvl);
+    }
+
+    pub fn emit_with_code(&self,
+                          sp: Option<Span>,
+                          msg: &str,
+                          code: &str,
+                          lvl: Level) {
+        if lvl == Warning && !self.can_emit_warnings { return }
+        self.emit.borrow_mut().emit(sp, msg, Some(code), lvl);
+    }
+
+    pub fn custom_emit(&self, sp: RenderSpan, msg: &str, lvl: Level) {
+        if lvl == Warning && !self.can_emit_warnings { return }
+        self.emit.borrow_mut().custom_emit(sp, msg, lvl);
+    }
+}
+
+
+#[derive(Copy, PartialEq, Clone, Debug)]
+pub enum Level {
+    Bug,
+    Fatal,
+    Error,
+    Warning,
+    Note,
+    Help,
+    Cancelled,
+}
+
+impl fmt::Display for Level {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use std::fmt::Display;
+
+        self.to_str().fmt(f)
+    }
+}
+
+impl Level {
+    fn color(self) -> term::color::Color {
+        match self {
+            Bug | Fatal | Error => term::color::BRIGHT_RED,
+            Warning => term::color::BRIGHT_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 | 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()),
+    }
+}
diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs
deleted file mode 100644 (file)
index d968858..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-// 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.
-
-/*
- * Inline assembly support.
- */
-use self::State::*;
-
-use ast;
-use codemap;
-use codemap::Span;
-use ext::base;
-use ext::base::*;
-use feature_gate;
-use parse::token::{intern, InternedString};
-use parse::token;
-use ptr::P;
-use syntax::ast::AsmDialect;
-
-enum State {
-    Asm,
-    Outputs,
-    Inputs,
-    Clobbers,
-    Options,
-    StateNone
-}
-
-impl State {
-    fn next(&self) -> State {
-        match *self {
-            Asm       => Outputs,
-            Outputs   => Inputs,
-            Inputs    => Clobbers,
-            Clobbers  => Options,
-            Options   => StateNone,
-            StateNone => StateNone
-        }
-    }
-}
-
-const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
-
-pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-                       -> Box<base::MacResult+'cx> {
-    if !cx.ecfg.enable_asm() {
-        feature_gate::emit_feature_err(
-            &cx.parse_sess.span_diagnostic, "asm", sp,
-            feature_gate::GateIssue::Language,
-            feature_gate::EXPLAIN_ASM);
-        return DummyResult::expr(sp);
-    }
-
-    let mut p = cx.new_parser_from_tts(tts);
-    let mut asm = InternedString::new("");
-    let mut asm_str_style = None;
-    let mut outputs = Vec::new();
-    let mut inputs = Vec::new();
-    let mut clobs = Vec::new();
-    let mut volatile = false;
-    let mut alignstack = false;
-    let mut dialect = AsmDialect::Att;
-
-    let mut state = Asm;
-
-    'statement: loop {
-        match state {
-            Asm => {
-                if asm_str_style.is_some() {
-                    // If we already have a string with instructions,
-                    // ending up in Asm state again is an error.
-                    cx.span_err(sp, "malformed inline assembly");
-                    return DummyResult::expr(sp);
-                }
-                let (s, style) = match expr_to_string(cx, panictry!(p.parse_expr()),
-                                                   "inline assembly must be a string literal") {
-                    Some((s, st)) => (s, st),
-                    // let compilation continue
-                    None => return DummyResult::expr(sp),
-                };
-                asm = s;
-                asm_str_style = Some(style);
-            }
-            Outputs => {
-                while p.token != token::Eof &&
-                      p.token != token::Colon &&
-                      p.token != token::ModSep {
-
-                    if !outputs.is_empty() {
-                        panictry!(p.eat(&token::Comma));
-                    }
-
-                    let (constraint, _str_style) = panictry!(p.parse_str());
-
-                    let span = p.last_span;
-
-                    panictry!(p.expect(&token::OpenDelim(token::Paren)));
-                    let out = panictry!(p.parse_expr());
-                    panictry!(p.expect(&token::CloseDelim(token::Paren)));
-
-                    // Expands a read+write operand into two operands.
-                    //
-                    // Use '+' modifier when you want the same expression
-                    // to be both an input and an output at the same time.
-                    // It's the opposite of '=&' which means that the memory
-                    // cannot be shared with any other operand (usually when
-                    // a register is clobbered early.)
-                    let output = match constraint.slice_shift_char() {
-                        Some(('=', _)) => None,
-                        Some(('+', operand)) => {
-                            Some(token::intern_and_get_ident(&format!(
-                                        "={}", operand)))
-                        }
-                        _ => {
-                            cx.span_err(span, "output operand constraint lacks '=' or '+'");
-                            None
-                        }
-                    };
-
-                    let is_rw = output.is_some();
-                    outputs.push((output.unwrap_or(constraint), out, is_rw));
-                }
-            }
-            Inputs => {
-                while p.token != token::Eof &&
-                      p.token != token::Colon &&
-                      p.token != token::ModSep {
-
-                    if !inputs.is_empty() {
-                        panictry!(p.eat(&token::Comma));
-                    }
-
-                    let (constraint, _str_style) = panictry!(p.parse_str());
-
-                    if constraint.starts_with("=") && !constraint.contains("*") {
-                        cx.span_err(p.last_span, "input operand constraint contains '='");
-                    } else if constraint.starts_with("+") && !constraint.contains("*") {
-                        cx.span_err(p.last_span, "input operand constraint contains '+'");
-                    }
-
-                    panictry!(p.expect(&token::OpenDelim(token::Paren)));
-                    let input = panictry!(p.parse_expr());
-                    panictry!(p.expect(&token::CloseDelim(token::Paren)));
-
-                    inputs.push((constraint, input));
-                }
-            }
-            Clobbers => {
-                while p.token != token::Eof &&
-                      p.token != token::Colon &&
-                      p.token != token::ModSep {
-
-                    if !clobs.is_empty() {
-                        panictry!(p.eat(&token::Comma));
-                    }
-
-                    let (s, _str_style) = panictry!(p.parse_str());
-
-                    if OPTIONS.iter().any(|&opt| s == opt) {
-                        cx.span_warn(p.last_span, "expected a clobber, found an option");
-                    }
-                    clobs.push(s);
-                }
-            }
-            Options => {
-                let (option, _str_style) = panictry!(p.parse_str());
-
-                if option == "volatile" {
-                    // Indicates that the inline assembly has side effects
-                    // and must not be optimized out along with its outputs.
-                    volatile = true;
-                } else if option == "alignstack" {
-                    alignstack = true;
-                } else if option == "intel" {
-                    dialect = AsmDialect::Intel;
-                } else {
-                    cx.span_warn(p.last_span, "unrecognized option");
-                }
-
-                if p.token == token::Comma {
-                    panictry!(p.eat(&token::Comma));
-                }
-            }
-            StateNone => ()
-        }
-
-        loop {
-            // MOD_SEP is a double colon '::' without space in between.
-            // When encountered, the state must be advanced twice.
-            match (&p.token, state.next(), state.next().next()) {
-                (&token::Colon, StateNone, _)   |
-                (&token::ModSep, _, StateNone) => {
-                    panictry!(p.bump());
-                    break 'statement;
-                }
-                (&token::Colon, st, _)   |
-                (&token::ModSep, _, st) => {
-                    panictry!(p.bump());
-                    state = st;
-                }
-                (&token::Eof, _, _) => break 'statement,
-                _ => break
-            }
-        }
-    }
-
-    let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
-        call_site: sp,
-        callee: codemap::NameAndSpan {
-            format: codemap::MacroBang(intern("asm")),
-            span: None,
-            allow_internal_unstable: false,
-        },
-    });
-
-    MacEager::expr(P(ast::Expr {
-        id: ast::DUMMY_NODE_ID,
-        node: ast::ExprInlineAsm(ast::InlineAsm {
-            asm: token::intern_and_get_ident(&asm),
-            asm_str_style: asm_str_style.unwrap(),
-            outputs: outputs,
-            inputs: inputs,
-            clobbers: clobs,
-            volatile: volatile,
-            alignstack: alignstack,
-            dialect: dialect,
-            expn_id: expn_id,
-        }),
-        span: sp,
-        attrs: None,
-    }))
-}
index 3b613922bc947f89a7081ad79513aefc5fca0850..107626c54cc9da82749d5d4b18c28478c14e511f 100644 (file)
@@ -14,6 +14,7 @@ use ast;
 use ast::Name;
 use codemap;
 use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION};
+use errors::DiagnosticBuilder;
 use ext;
 use ext::expand;
 use ext::tt::macro_rules;
@@ -24,7 +25,7 @@ use parse::token;
 use parse::token::{InternedString, intern, str_to_ident};
 use ptr::P;
 use util::small_vector::SmallVector;
-use util::lev_distance::{lev_distance, max_suggestion_distance};
+use util::lev_distance::find_best_match_for_name;
 use ext::mtwt;
 use fold::Folder;
 
@@ -464,26 +465,6 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
 
     let mut syntax_expanders = SyntaxEnv::new();
     syntax_expanders.insert(intern("macro_rules"), MacroRulesTT);
-    syntax_expanders.insert(intern("format_args"),
-                            // format_args uses `unstable` things internally.
-                            NormalTT(Box::new(ext::format::expand_format_args), None, true));
-    syntax_expanders.insert(intern("env"),
-                            builtin_normal_expander(
-                                    ext::env::expand_env));
-    syntax_expanders.insert(intern("option_env"),
-                            builtin_normal_expander(
-                                    ext::env::expand_option_env));
-    syntax_expanders.insert(intern("concat_idents"),
-                            builtin_normal_expander(
-                                    ext::concat_idents::expand_syntax_ext));
-    syntax_expanders.insert(intern("concat"),
-                            builtin_normal_expander(
-                                    ext::concat::expand_syntax_ext));
-    syntax_expanders.insert(intern("log_syntax"),
-                            builtin_normal_expander(
-                                    ext::log_syntax::expand_syntax_ext));
-
-    ext::deriving::register_all(&mut syntax_expanders);
 
     if ecfg.enable_quotes() {
         // Quasi-quoting expanders
@@ -552,15 +533,6 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
     syntax_expanders.insert(intern("module_path"),
                             builtin_normal_expander(
                                     ext::source_util::expand_mod));
-    syntax_expanders.insert(intern("asm"),
-                            builtin_normal_expander(
-                                    ext::asm::expand_asm));
-    syntax_expanders.insert(intern("cfg"),
-                            builtin_normal_expander(
-                                    ext::cfg::expand_cfg));
-    syntax_expanders.insert(intern("trace_macros"),
-                            builtin_normal_expander(
-                                    ext::trace_macros::expand_trace_macros));
     syntax_expanders
 }
 
@@ -601,13 +573,6 @@ impl<'a> ExtCtxt<'a> {
         }
     }
 
-    #[unstable(feature = "rustc_private", issue = "0")]
-    #[rustc_deprecated(since = "1.0.0",
-                 reason = "Replaced with `expander().fold_expr()`")]
-    pub fn expand_expr(&mut self, e: P<ast::Expr>) -> P<ast::Expr> {
-        self.expander().fold_expr(e)
-    }
-
     /// Returns a `Folder` for deeply expanding all macros in an AST node.
     pub fn expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> {
         expand::MacroExpander::new(self)
@@ -714,6 +679,25 @@ impl<'a> ExtCtxt<'a> {
         }
     }
 
+    pub fn struct_span_warn(&self,
+                            sp: Span,
+                            msg: &str)
+                            -> DiagnosticBuilder<'a> {
+        self.parse_sess.span_diagnostic.struct_span_warn(sp, msg)
+    }
+    pub fn struct_span_err(&self,
+                           sp: Span,
+                           msg: &str)
+                           -> DiagnosticBuilder<'a> {
+        self.parse_sess.span_diagnostic.struct_span_err(sp, msg)
+    }
+    pub fn struct_span_fatal(&self,
+                             sp: Span,
+                             msg: &str)
+                             -> DiagnosticBuilder<'a> {
+        self.parse_sess.span_diagnostic.struct_span_fatal(sp, msg)
+    }
+
     /// Emit `msg` attached to `sp`, and stop compilation immediately.
     ///
     /// `span_err` should be strongly preferred where-ever possible:
@@ -746,17 +730,8 @@ impl<'a> ExtCtxt<'a> {
     pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
         self.parse_sess.span_diagnostic.span_bug(sp, msg);
     }
-    pub fn span_note(&self, sp: Span, msg: &str) {
-        self.parse_sess.span_diagnostic.span_note(sp, msg);
-    }
-    pub fn span_help(&self, sp: Span, msg: &str) {
-        self.parse_sess.span_diagnostic.span_help(sp, msg);
-    }
-    pub fn fileline_help(&self, sp: Span, msg: &str) {
-        self.parse_sess.span_diagnostic.fileline_help(sp, msg);
-    }
     pub fn bug(&self, msg: &str) -> ! {
-        self.parse_sess.span_diagnostic.handler().bug(msg);
+        self.parse_sess.span_diagnostic.bug(msg);
     }
     pub fn trace_macros(&self) -> bool {
         self.ecfg.trace_mac
@@ -779,17 +754,13 @@ impl<'a> ExtCtxt<'a> {
         token::intern(st)
     }
 
-    pub fn suggest_macro_name(&mut self, name: &str, span: Span) {
-        let mut min: Option<(Name, usize)> = None;
-        let max_dist = max_suggestion_distance(name);
-        for macro_name in self.syntax_env.names.iter() {
-            let dist = lev_distance(name, &macro_name.as_str());
-            if dist <= max_dist && (min.is_none() || min.unwrap().1 > dist) {
-                min = Some((*macro_name, dist));
-            }
-        }
-        if let Some((suggestion, _)) = min {
-            self.fileline_help(span, &format!("did you mean `{}!`?", suggestion));
+    pub fn suggest_macro_name(&mut self,
+                              name: &str,
+                              span: Span,
+                              err: &mut DiagnosticBuilder<'a>) {
+        let names = &self.syntax_env.names;
+        if let Some(suggestion) = find_best_match_for_name(names.iter(), name, None) {
+            err.fileline_help(span, &format!("did you mean `{}!`?", suggestion));
         }
     }
 }
@@ -855,7 +826,7 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt,
     let mut es = Vec::new();
     while p.token != token::Eof {
         es.push(cx.expander().fold_expr(panictry!(p.parse_expr())));
-        if panictry!(p.eat(&token::Comma)){
+        if p.eat(&token::Comma) {
             continue;
         }
         if p.token != token::Eof {
index 806f5a7ee22ed36cec7529a9d89836dd503eb515..a74c2340cecb8621ae1d3ce2034bec3c43f75594 100644 (file)
 use abi;
 use ast::{Ident, Generics, Expr};
 use ast;
-use ast_util;
 use attr;
 use codemap::{Span, respan, Spanned, DUMMY_SP, Pos};
 use ext::base::ExtCtxt;
-use owned_slice::OwnedSlice;
 use parse::token::special_idents;
 use parse::token::InternedString;
 use parse::token;
@@ -57,7 +55,7 @@ pub trait AstBuilder {
 
     fn ty(&self, span: Span, ty: ast::Ty_) -> P<ast::Ty>;
     fn ty_path(&self, ast::Path) -> P<ast::Ty>;
-    fn ty_sum(&self, ast::Path, OwnedSlice<ast::TyParamBound>) -> P<ast::Ty>;
+    fn ty_sum(&self, ast::Path, ast::TyParamBounds) -> P<ast::Ty>;
     fn ty_ident(&self, span: Span, idents: ast::Ident) -> P<ast::Ty>;
 
     fn ty_rptr(&self, span: Span,
@@ -71,13 +69,13 @@ pub trait AstBuilder {
     fn ty_option(&self, ty: P<ast::Ty>) -> P<ast::Ty>;
     fn ty_infer(&self, sp: Span) -> P<ast::Ty>;
 
-    fn ty_vars(&self, ty_params: &OwnedSlice<ast::TyParam>) -> Vec<P<ast::Ty>> ;
-    fn ty_vars_global(&self, ty_params: &OwnedSlice<ast::TyParam>) -> Vec<P<ast::Ty>> ;
+    fn ty_vars(&self, ty_params: &P<[ast::TyParam]>) -> Vec<P<ast::Ty>> ;
+    fn ty_vars_global(&self, ty_params: &P<[ast::TyParam]>) -> Vec<P<ast::Ty>> ;
 
     fn typaram(&self,
                span: Span,
                id: ast::Ident,
-               bounds: OwnedSlice<ast::TyParamBound>,
+               bounds: ast::TyParamBounds,
                default: Option<P<ast::Ty>>) -> ast::TyParam;
 
     fn trait_ref(&self, path: ast::Path) -> ast::TraitRef;
@@ -330,10 +328,10 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         }).collect();
         segments.push(ast::PathSegment {
             identifier: last_identifier,
-            parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
+            parameters: ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData {
                 lifetimes: lifetimes,
-                types: OwnedSlice::from_vec(types),
-                bindings: OwnedSlice::from_vec(bindings),
+                types: P::from_vec(types),
+                bindings: P::from_vec(bindings),
             })
         });
         ast::Path {
@@ -368,10 +366,10 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         let mut path = trait_path;
         path.segments.push(ast::PathSegment {
             identifier: ident,
-            parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
+            parameters: ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData {
                 lifetimes: lifetimes,
-                types: OwnedSlice::from_vec(types),
-                bindings: OwnedSlice::from_vec(bindings),
+                types: P::from_vec(types),
+                bindings: P::from_vec(bindings),
             })
         });
 
@@ -400,7 +398,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         self.ty(path.span, ast::TyPath(None, path))
     }
 
-    fn ty_sum(&self, path: ast::Path, bounds: OwnedSlice<ast::TyParamBound>) -> P<ast::Ty> {
+    fn ty_sum(&self, path: ast::Path, bounds: ast::TyParamBounds) -> P<ast::Ty> {
         self.ty(path.span,
                 ast::TyObjectSum(self.ty_path(path),
                                  bounds))
@@ -449,7 +447,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     fn typaram(&self,
                span: Span,
                id: ast::Ident,
-               bounds: OwnedSlice<ast::TyParamBound>,
+               bounds: ast::TyParamBounds,
                default: Option<P<ast::Ty>>) -> ast::TyParam {
         ast::TyParam {
             ident: id,
@@ -463,11 +461,11 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     // these are strange, and probably shouldn't be used outside of
     // pipes. Specifically, the global version possible generates
     // incorrect code.
-    fn ty_vars(&self, ty_params: &OwnedSlice<ast::TyParam>) -> Vec<P<ast::Ty>> {
+    fn ty_vars(&self, ty_params: &P<[ast::TyParam]>) -> Vec<P<ast::Ty>> {
         ty_params.iter().map(|p| self.ty_ident(DUMMY_SP, p.ident)).collect()
     }
 
-    fn ty_vars_global(&self, ty_params: &OwnedSlice<ast::TyParam>) -> Vec<P<ast::Ty>> {
+    fn ty_vars_global(&self, ty_params: &P<[ast::TyParam]>) -> Vec<P<ast::Ty>> {
         ty_params
             .iter()
             .map(|p| self.ty_path(self.path_global(DUMMY_SP, vec!(p.ident))))
@@ -515,7 +513,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     fn stmt_let(&self, sp: Span, mutbl: bool, ident: ast::Ident,
                 ex: P<ast::Expr>) -> P<ast::Stmt> {
         let pat = if mutbl {
-            self.pat_ident_binding_mode(sp, ident, ast::BindByValue(ast::MutMutable))
+            self.pat_ident_binding_mode(sp, ident, ast::BindingMode::ByValue(ast::MutMutable))
         } else {
             self.pat_ident(sp, ident)
         };
@@ -539,7 +537,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
                       ex: P<ast::Expr>)
                       -> P<ast::Stmt> {
         let pat = if mutbl {
-            self.pat_ident_binding_mode(sp, ident, ast::BindByValue(ast::MutMutable))
+            self.pat_ident_binding_mode(sp, ident, ast::BindingMode::ByValue(ast::MutMutable))
         } else {
             self.pat_ident(sp, ident)
         };
@@ -810,7 +808,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         self.pat(span, ast::PatLit(expr))
     }
     fn pat_ident(&self, span: Span, ident: ast::Ident) -> P<ast::Pat> {
-        self.pat_ident_binding_mode(span, ident, ast::BindByValue(ast::MutImmutable))
+        self.pat_ident_binding_mode(span, ident, ast::BindingMode::ByValue(ast::MutImmutable))
     }
 
     fn pat_ident_binding_mode(&self,
@@ -991,7 +989,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             name,
             inputs,
             output,
-            ast_util::empty_generics(),
+            Generics::default(),
             body)
     }
 
@@ -1029,7 +1027,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     fn item_enum(&self, span: Span, name: Ident,
                  enum_definition: ast::EnumDef) -> P<ast::Item> {
         self.item_enum_poly(span, name, enum_definition,
-                            ast_util::empty_generics())
+                            Generics::default())
     }
 
     fn item_struct(&self, span: Span, name: Ident,
@@ -1038,7 +1036,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             span,
             name,
             struct_def,
-            ast_util::empty_generics()
+            Generics::default()
         )
     }
 
@@ -1086,7 +1084,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     }
 
     fn item_ty(&self, span: Span, name: Ident, ty: P<ast::Ty>) -> P<ast::Item> {
-        self.item_ty_poly(span, name, ty, ast_util::empty_generics())
+        self.item_ty_poly(span, name, ty, Generics::default())
     }
 
     fn attribute(&self, sp: Span, mi: P<ast::MetaItem>) -> ast::Attribute {
diff --git a/src/libsyntax/ext/cfg.rs b/src/libsyntax/ext/cfg.rs
deleted file mode 100644 (file)
index e100355..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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.
-
-/// The compiler code necessary to support the cfg! extension, which expands to
-/// a literal `true` or `false` based on whether the given cfg matches the
-/// current compilation environment.
-
-use ast;
-use codemap::Span;
-use ext::base::*;
-use ext::base;
-use ext::build::AstBuilder;
-use attr;
-use attr::*;
-use parse::token;
-use config::CfgDiagReal;
-
-pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
-                       sp: Span,
-                       tts: &[ast::TokenTree])
-                       -> Box<base::MacResult+'static> {
-    let mut p = cx.new_parser_from_tts(tts);
-    let cfg = panictry!(p.parse_meta_item());
-
-    if !panictry!(p.eat(&token::Eof)){
-        cx.span_err(sp, "expected 1 cfg-pattern");
-        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)
-    };
-    MacEager::expr(cx.expr_bool(sp, matches_cfg))
-}
diff --git a/src/libsyntax/ext/concat.rs b/src/libsyntax/ext/concat.rs
deleted file mode 100644 (file)
index 71430b7..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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.
-
-use ast;
-use codemap;
-use ext::base;
-use ext::build::AstBuilder;
-use parse::token;
-
-use std::string::String;
-
-pub fn expand_syntax_ext(cx: &mut base::ExtCtxt,
-                         sp: codemap::Span,
-                         tts: &[ast::TokenTree])
-                         -> Box<base::MacResult+'static> {
-    let es = match base::get_exprs_from_tts(cx, sp, tts) {
-        Some(e) => e,
-        None => return base::DummyResult::expr(sp)
-    };
-    let mut accumulator = String::new();
-    for e in es {
-        match e.node {
-            ast::ExprLit(ref lit) => {
-                match lit.node {
-                    ast::LitStr(ref s, _) |
-                    ast::LitFloat(ref s, _) |
-                    ast::LitFloatUnsuffixed(ref s) => {
-                        accumulator.push_str(&s);
-                    }
-                    ast::LitChar(c) => {
-                        accumulator.push(c);
-                    }
-                    ast::LitInt(i, ast::UnsignedIntLit(_)) |
-                    ast::LitInt(i, ast::SignedIntLit(_, ast::Plus)) |
-                    ast::LitInt(i, ast::UnsuffixedIntLit(ast::Plus)) => {
-                        accumulator.push_str(&format!("{}", i));
-                    }
-                    ast::LitInt(i, ast::SignedIntLit(_, ast::Minus)) |
-                    ast::LitInt(i, ast::UnsuffixedIntLit(ast::Minus)) => {
-                        accumulator.push_str(&format!("-{}", i));
-                    }
-                    ast::LitBool(b) => {
-                        accumulator.push_str(&format!("{}", b));
-                    }
-                    ast::LitByte(..) |
-                    ast::LitByteStr(..) => {
-                        cx.span_err(e.span, "cannot concatenate a byte string literal");
-                    }
-                }
-            }
-            _ => {
-                cx.span_err(e.span, "expected a literal");
-            }
-        }
-    }
-    base::MacEager::expr(cx.expr_str(
-            sp,
-            token::intern_and_get_ident(&accumulator[..])))
-}
diff --git a/src/libsyntax/ext/concat_idents.rs b/src/libsyntax/ext/concat_idents.rs
deleted file mode 100644 (file)
index c223320..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-// 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.
-
-use ast::{self, TokenTree};
-use codemap::Span;
-use ext::base::*;
-use ext::base;
-use feature_gate;
-use parse::token;
-use parse::token::str_to_ident;
-use ptr::P;
-
-pub fn expand_syntax_ext<'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,
-                                       "concat_idents",
-                                       sp,
-                                       feature_gate::GateIssue::Language,
-                                       feature_gate::EXPLAIN_CONCAT_IDENTS);
-        return base::DummyResult::expr(sp);
-    }
-
-    let mut res_str = String::new();
-    for (i, e) in tts.iter().enumerate() {
-        if i & 1 == 1 {
-            match *e {
-                TokenTree::Token(_, token::Comma) => {},
-                _ => {
-                    cx.span_err(sp, "concat_idents! expecting comma.");
-                    return DummyResult::expr(sp);
-                },
-            }
-        } else {
-            match *e {
-                TokenTree::Token(_, token::Ident(ident, _)) => {
-                    res_str.push_str(&ident.name.as_str())
-                },
-                _ => {
-                    cx.span_err(sp, "concat_idents! requires ident args.");
-                    return DummyResult::expr(sp);
-                },
-            }
-        }
-    }
-    let res = str_to_ident(&res_str);
-
-    let e = P(ast::Expr {
-        id: ast::DUMMY_NODE_ID,
-        node: ast::ExprPath(None,
-            ast::Path {
-                 span: sp,
-                 global: false,
-                 segments: vec!(
-                    ast::PathSegment {
-                        identifier: res,
-                        parameters: ast::PathParameters::none(),
-                    }
-                )
-            }
-        ),
-        span: sp,
-        attrs: None,
-    });
-    MacEager::expr(e)
-}
diff --git a/src/libsyntax/ext/deriving/bounds.rs b/src/libsyntax/ext/deriving/bounds.rs
deleted file mode 100644 (file)
index 87a6d08..0000000
+++ /dev/null
@@ -1,49 +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 ast::MetaItem;
-use codemap::Span;
-use ext::base::{ExtCtxt, Annotatable};
-use ext::deriving::generic::*;
-use ext::deriving::generic::ty::*;
-
-pub fn expand_deriving_unsafe_bound(cx: &mut ExtCtxt,
-                                    span: Span,
-                                    _: &MetaItem,
-                                    _: &Annotatable,
-                                    _: &mut FnMut(Annotatable))
-{
-    cx.span_err(span, "this unsafe trait should be implemented explicitly");
-}
-
-pub fn expand_deriving_copy(cx: &mut ExtCtxt,
-                            span: Span,
-                            mitem: &MetaItem,
-                            item: &Annotatable,
-                            push: &mut FnMut(Annotatable))
-{
-    let mut v = cx.crate_root.map(|s| vec![s]).unwrap_or(Vec::new());
-    v.push("marker");
-    v.push("Copy");
-    let path = Path::new(v);
-
-    let trait_def = TraitDef {
-        span: span,
-        attributes: Vec::new(),
-        path: path,
-        additional_bounds: Vec::new(),
-        generics: LifetimeBounds::empty(),
-        is_unsafe: false,
-        methods: Vec::new(),
-        associated_types: Vec::new(),
-    };
-
-    trait_def.expand(cx, mitem, item, push);
-}
diff --git a/src/libsyntax/ext/deriving/clone.rs b/src/libsyntax/ext/deriving/clone.rs
deleted file mode 100644 (file)
index f1a2983..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-// 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.
-
-use ast::{MetaItem, Expr};
-use codemap::Span;
-use ext::base::{ExtCtxt, Annotatable};
-use ext::build::AstBuilder;
-use ext::deriving::generic::*;
-use ext::deriving::generic::ty::*;
-use parse::token::InternedString;
-use ptr::P;
-
-pub fn expand_deriving_clone(cx: &mut ExtCtxt,
-                             span: Span,
-                             mitem: &MetaItem,
-                             item: &Annotatable,
-                             push: &mut FnMut(Annotatable))
-{
-    let inline = cx.meta_word(span, InternedString::new("inline"));
-    let attrs = vec!(cx.attribute(span, inline));
-    let trait_def = TraitDef {
-        span: span,
-        attributes: Vec::new(),
-        path: path_std!(cx, core::clone::Clone),
-        additional_bounds: Vec::new(),
-        generics: LifetimeBounds::empty(),
-        is_unsafe: false,
-        methods: vec!(
-            MethodDef {
-                name: "clone",
-                generics: LifetimeBounds::empty(),
-                explicit_self: borrowed_explicit_self(),
-                args: Vec::new(),
-                ret_ty: Self_,
-                attributes: attrs,
-                is_unsafe: false,
-                combine_substructure: combine_substructure(Box::new(|c, s, sub| {
-                    cs_clone("Clone", c, s, sub)
-                })),
-            }
-        ),
-        associated_types: Vec::new(),
-    };
-
-    trait_def.expand(cx, mitem, item, push)
-}
-
-fn cs_clone(
-    name: &str,
-    cx: &mut ExtCtxt, trait_span: Span,
-    substr: &Substructure) -> P<Expr> {
-    let ctor_path;
-    let all_fields;
-    let fn_path = cx.std_path(&["clone", "Clone", "clone"]);
-    let subcall = |field: &FieldInfo| {
-        let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
-
-        cx.expr_call_global(field.span, fn_path.clone(), args)
-    };
-
-    match *substr.fields {
-        Struct(ref af) => {
-            ctor_path = cx.path(trait_span, vec![substr.type_ident]);
-            all_fields = af;
-        }
-        EnumMatching(_, variant, ref af) => {
-            ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.name]);
-            all_fields = af;
-        },
-        EnumNonMatchingCollapsed (..) => {
-            cx.span_bug(trait_span,
-                        &format!("non-matching enum variants in \
-                                 `derive({})`", name))
-        }
-        StaticEnum(..) | StaticStruct(..) => {
-            cx.span_bug(trait_span,
-                        &format!("static method in `derive({})`", name))
-        }
-    }
-
-    if !all_fields.is_empty() && all_fields[0].name.is_none() {
-        // enum-like
-        let subcalls = all_fields.iter().map(subcall).collect();
-        let path = cx.expr_path(ctor_path);
-        cx.expr_call(trait_span, path, subcalls)
-    } else {
-        // struct-like
-        let fields = all_fields.iter().map(|field| {
-            let ident = match field.name {
-                Some(i) => i,
-                None => {
-                    cx.span_bug(trait_span,
-                                &format!("unnamed field in normal struct in \
-                                         `derive({})`", name))
-                }
-            };
-            cx.field_imm(field.span, ident, subcall(field))
-        }).collect::<Vec<_>>();
-
-        if fields.is_empty() {
-            // no fields, so construct like `None`
-            cx.expr_path(ctor_path)
-        } else {
-            cx.expr_struct(trait_span, ctor_path, fields)
-        }
-    }
-}
diff --git a/src/libsyntax/ext/deriving/cmp/eq.rs b/src/libsyntax/ext/deriving/cmp/eq.rs
deleted file mode 100644 (file)
index bd6b27f..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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.
-
-use ast::{MetaItem, Expr};
-use codemap::Span;
-use ext::base::{ExtCtxt, Annotatable};
-use ext::build::AstBuilder;
-use ext::deriving::generic::*;
-use ext::deriving::generic::ty::*;
-use parse::token::InternedString;
-use ptr::P;
-
-pub fn expand_deriving_eq(cx: &mut ExtCtxt,
-                          span: Span,
-                          mitem: &MetaItem,
-                          item: &Annotatable,
-                          push: &mut FnMut(Annotatable))
-{
-    fn cs_total_eq_assert(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
-        cs_same_method(
-            |cx, span, exprs| {
-                // 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);
-                cx.expr_block(block)
-            },
-            Box::new(|cx, sp, _, _| {
-                cx.span_bug(sp, "non matching enums in derive(Eq)?") }),
-            cx,
-            span,
-            substr
-        )
-    }
-
-    let inline = cx.meta_word(span, InternedString::new("inline"));
-    let hidden = cx.meta_word(span, InternedString::new("hidden"));
-    let doc = cx.meta_list(span, InternedString::new("doc"), vec!(hidden));
-    let attrs = vec!(cx.attribute(span, inline),
-                     cx.attribute(span, doc));
-    let trait_def = TraitDef {
-        span: span,
-        attributes: Vec::new(),
-        path: path_std!(cx, core::cmp::Eq),
-        additional_bounds: Vec::new(),
-        generics: LifetimeBounds::empty(),
-        is_unsafe: false,
-        methods: vec!(
-            MethodDef {
-                name: "assert_receiver_is_total_eq",
-                generics: LifetimeBounds::empty(),
-                explicit_self: borrowed_explicit_self(),
-                args: vec!(),
-                ret_ty: nil_ty(),
-                attributes: attrs,
-                is_unsafe: false,
-                combine_substructure: combine_substructure(Box::new(|a, b, c| {
-                    cs_total_eq_assert(a, b, c)
-                }))
-            }
-        ),
-        associated_types: Vec::new(),
-    };
-    trait_def.expand(cx, mitem, item, push)
-}
diff --git a/src/libsyntax/ext/deriving/cmp/ord.rs b/src/libsyntax/ext/deriving/cmp/ord.rs
deleted file mode 100644 (file)
index ff36e01..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-// 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.
-
-use ast;
-use ast::{MetaItem, Expr};
-use codemap::Span;
-use ext::base::{ExtCtxt, Annotatable};
-use ext::build::AstBuilder;
-use ext::deriving::generic::*;
-use ext::deriving::generic::ty::*;
-use parse::token::InternedString;
-use ptr::P;
-
-pub fn expand_deriving_ord(cx: &mut ExtCtxt,
-                           span: Span,
-                           mitem: &MetaItem,
-                           item: &Annotatable,
-                           push: &mut FnMut(Annotatable))
-{
-    let inline = cx.meta_word(span, InternedString::new("inline"));
-    let attrs = vec!(cx.attribute(span, inline));
-    let trait_def = TraitDef {
-        span: span,
-        attributes: Vec::new(),
-        path: path_std!(cx, core::cmp::Ord),
-        additional_bounds: Vec::new(),
-        generics: LifetimeBounds::empty(),
-        is_unsafe: false,
-        methods: vec!(
-            MethodDef {
-                name: "cmp",
-                generics: LifetimeBounds::empty(),
-                explicit_self: borrowed_explicit_self(),
-                args: vec!(borrowed_self()),
-                ret_ty: Literal(path_std!(cx, core::cmp::Ordering)),
-                attributes: attrs,
-                is_unsafe: false,
-                combine_substructure: combine_substructure(Box::new(|a, b, c| {
-                    cs_cmp(a, b, c)
-                })),
-            }
-        ),
-        associated_types: Vec::new(),
-    };
-
-    trait_def.expand(cx, mitem, item, push)
-}
-
-
-pub fn ordering_collapsed(cx: &mut ExtCtxt,
-                          span: Span,
-                          self_arg_tags: &[ast::Ident]) -> P<ast::Expr> {
-    let lft = cx.expr_ident(span, self_arg_tags[0]);
-    let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
-    cx.expr_method_call(span, lft, cx.ident_of("cmp"), vec![rgt])
-}
-
-pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
-              substr: &Substructure) -> P<Expr> {
-    let test_id = cx.ident_of("__test");
-    let equals_path = cx.path_global(span,
-                                     cx.std_path(&["cmp", "Ordering", "Equal"]));
-
-    let cmp_path = cx.std_path(&["cmp", "Ord", "cmp"]);
-
-    /*
-    Builds:
-
-    let __test = ::std::cmp::Ord::cmp(&self_field1, &other_field1);
-    if other == ::std::cmp::Ordering::Equal {
-        let __test = ::std::cmp::Ord::cmp(&self_field2, &other_field2);
-        if __test == ::std::cmp::Ordering::Equal {
-            ...
-        } else {
-            __test
-        }
-    } else {
-        __test
-    }
-
-    FIXME #6449: These `if`s could/should be `match`es.
-    */
-    cs_fold(
-        // foldr nests the if-elses correctly, leaving the first field
-        // as the outermost one, and the last as the innermost.
-        false,
-        |cx, span, old, self_f, other_fs| {
-            // let __test = new;
-            // if __test == ::std::cmp::Ordering::Equal {
-            //    old
-            // } else {
-            //    __test
-            // }
-
-            let new = {
-                let other_f = match (other_fs.len(), other_fs.get(0)) {
-                    (1, Some(o_f)) => o_f,
-                    _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
-                };
-
-                let args = vec![
-                    cx.expr_addr_of(span, self_f),
-                    cx.expr_addr_of(span, other_f.clone()),
-                ];
-
-                cx.expr_call_global(span, cmp_path.clone(), args)
-            };
-
-            let assign = cx.stmt_let(span, false, test_id, new);
-
-            let cond = cx.expr_binary(span, ast::BiEq,
-                                      cx.expr_ident(span, test_id),
-                                      cx.expr_path(equals_path.clone()));
-            let if_ = cx.expr_if(span,
-                                 cond,
-                                 old, Some(cx.expr_ident(span, test_id)));
-            cx.expr_block(cx.block(span, vec!(assign), Some(if_)))
-        },
-        cx.expr_path(equals_path.clone()),
-        Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
-            if self_args.len() != 2 {
-                cx.span_bug(span, "not exactly 2 arguments in `derives(Ord)`")
-            } else {
-                ordering_collapsed(cx, span, tag_tuple)
-            }
-        }),
-        cx, span, substr)
-}
diff --git a/src/libsyntax/ext/deriving/cmp/partial_eq.rs b/src/libsyntax/ext/deriving/cmp/partial_eq.rs
deleted file mode 100644 (file)
index 495761c..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-// 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.
-
-use ast::{MetaItem, Expr, self};
-use codemap::Span;
-use ext::base::{ExtCtxt, Annotatable};
-use ext::build::AstBuilder;
-use ext::deriving::generic::*;
-use ext::deriving::generic::ty::*;
-use parse::token::InternedString;
-use ptr::P;
-
-pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt,
-                                  span: Span,
-                                  mitem: &MetaItem,
-                                  item: &Annotatable,
-                                  push: &mut FnMut(Annotatable))
-{
-    // structures are equal if all fields are equal, and non equal, if
-    // any fields are not equal or if the enum variants are different
-    fn cs_eq(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
-        cs_fold(
-            true,  // use foldl
-            |cx, span, subexpr, self_f, other_fs| {
-                let other_f = match (other_fs.len(), other_fs.get(0)) {
-                    (1, Some(o_f)) => o_f,
-                    _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`")
-                };
-
-                let eq = cx.expr_binary(span, ast::BiEq, self_f, other_f.clone());
-
-                cx.expr_binary(span, ast::BiAnd, subexpr, eq)
-            },
-            cx.expr_bool(span, true),
-            Box::new(|cx, span, _, _| cx.expr_bool(span, false)),
-            cx, span, substr)
-    }
-    fn cs_ne(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
-        cs_fold(
-            true,  // use foldl
-            |cx, span, subexpr, self_f, other_fs| {
-                let other_f = match (other_fs.len(), other_fs.get(0)) {
-                    (1, Some(o_f)) => o_f,
-                    _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`")
-                };
-
-                let eq = cx.expr_binary(span, ast::BiNe, self_f, other_f.clone());
-
-                cx.expr_binary(span, ast::BiOr, subexpr, eq)
-            },
-            cx.expr_bool(span, false),
-            Box::new(|cx, span, _, _| cx.expr_bool(span, true)),
-            cx, span, substr)
-    }
-
-    macro_rules! md {
-        ($name:expr, $f:ident) => { {
-            let inline = cx.meta_word(span, InternedString::new("inline"));
-            let attrs = vec!(cx.attribute(span, inline));
-            MethodDef {
-                name: $name,
-                generics: LifetimeBounds::empty(),
-                explicit_self: borrowed_explicit_self(),
-                args: vec!(borrowed_self()),
-                ret_ty: Literal(path_local!(bool)),
-                attributes: attrs,
-                is_unsafe: false,
-                combine_substructure: combine_substructure(Box::new(|a, b, c| {
-                    $f(a, b, c)
-                }))
-            }
-        } }
-    }
-
-    let trait_def = TraitDef {
-        span: span,
-        attributes: Vec::new(),
-        path: path_std!(cx, core::cmp::PartialEq),
-        additional_bounds: Vec::new(),
-        generics: LifetimeBounds::empty(),
-        is_unsafe: false,
-        methods: vec!(
-            md!("eq", cs_eq),
-            md!("ne", cs_ne)
-        ),
-        associated_types: Vec::new(),
-    };
-    trait_def.expand(cx, mitem, item, push)
-}
diff --git a/src/libsyntax/ext/deriving/cmp/partial_ord.rs b/src/libsyntax/ext/deriving/cmp/partial_ord.rs
deleted file mode 100644 (file)
index 084e3ef..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-// 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.
-
-pub use self::OrderingOp::*;
-
-use ast;
-use ast::{MetaItem, Expr};
-use codemap::Span;
-use ext::base::{ExtCtxt, Annotatable};
-use ext::build::AstBuilder;
-use ext::deriving::generic::*;
-use ext::deriving::generic::ty::*;
-use parse::token::InternedString;
-use ptr::P;
-
-pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
-                                   span: Span,
-                                   mitem: &MetaItem,
-                                   item: &Annotatable,
-                                   push: &mut FnMut(Annotatable))
-{
-    macro_rules! md {
-        ($name:expr, $op:expr, $equal:expr) => { {
-            let inline = cx.meta_word(span, InternedString::new("inline"));
-            let attrs = vec!(cx.attribute(span, inline));
-            MethodDef {
-                name: $name,
-                generics: LifetimeBounds::empty(),
-                explicit_self: borrowed_explicit_self(),
-                args: vec!(borrowed_self()),
-                ret_ty: Literal(path_local!(bool)),
-                attributes: attrs,
-                is_unsafe: false,
-                combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
-                    cs_op($op, $equal, cx, span, substr)
-                }))
-            }
-        } }
-    }
-
-    let ordering_ty = Literal(path_std!(cx, core::cmp::Ordering));
-    let ret_ty = Literal(Path::new_(pathvec_std!(cx, core::option::Option),
-                                    None,
-                                    vec![Box::new(ordering_ty)],
-                                    true));
-
-    let inline = cx.meta_word(span, InternedString::new("inline"));
-    let attrs = vec!(cx.attribute(span, inline));
-
-    let partial_cmp_def = MethodDef {
-        name: "partial_cmp",
-        generics: LifetimeBounds::empty(),
-        explicit_self: borrowed_explicit_self(),
-        args: vec![borrowed_self()],
-        ret_ty: ret_ty,
-        attributes: attrs,
-        is_unsafe: false,
-        combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
-            cs_partial_cmp(cx, span, substr)
-        }))
-    };
-
-    let trait_def = TraitDef {
-        span: span,
-        attributes: vec![],
-        path: path_std!(cx, core::cmp::PartialOrd),
-        additional_bounds: vec![],
-        generics: LifetimeBounds::empty(),
-        is_unsafe: false,
-        methods: vec![
-            partial_cmp_def,
-            md!("lt", true, false),
-            md!("le", true, true),
-            md!("gt", false, false),
-            md!("ge", false, true)
-        ],
-        associated_types: Vec::new(),
-    };
-    trait_def.expand(cx, mitem, item, push)
-}
-
-#[derive(Copy, Clone)]
-pub enum OrderingOp {
-    PartialCmpOp, LtOp, LeOp, GtOp, GeOp,
-}
-
-pub fn some_ordering_collapsed(cx: &mut ExtCtxt,
-                               span: Span,
-                               op: OrderingOp,
-                               self_arg_tags: &[ast::Ident]) -> P<ast::Expr> {
-    let lft = cx.expr_ident(span, self_arg_tags[0]);
-    let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
-    let op_str = match op {
-        PartialCmpOp => "partial_cmp",
-        LtOp => "lt", LeOp => "le",
-        GtOp => "gt", GeOp => "ge",
-    };
-    cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt])
-}
-
-pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span,
-              substr: &Substructure) -> P<Expr> {
-    let test_id = cx.ident_of("__test");
-    let ordering = cx.path_global(span,
-                                  cx.std_path(&["cmp", "Ordering", "Equal"]));
-    let ordering = cx.expr_path(ordering);
-    let equals_expr = cx.expr_some(span, ordering);
-
-    let partial_cmp_path = cx.std_path(&["cmp", "PartialOrd", "partial_cmp"]);
-
-    /*
-    Builds:
-
-    let __test = ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1);
-    if __test == ::std::option::Option::Some(::std::cmp::Ordering::Equal) {
-        let __test = ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2);
-        if __test == ::std::option::Option::Some(::std::cmp::Ordering::Equal) {
-            ...
-        } else {
-            __test
-        }
-    } else {
-        __test
-    }
-
-    FIXME #6449: These `if`s could/should be `match`es.
-    */
-    cs_fold(
-        // foldr nests the if-elses correctly, leaving the first field
-        // as the outermost one, and the last as the innermost.
-        false,
-        |cx, span, old, self_f, other_fs| {
-            // let __test = new;
-            // if __test == Some(::std::cmp::Ordering::Equal) {
-            //    old
-            // } else {
-            //    __test
-            // }
-
-            let new = {
-                let other_f = match (other_fs.len(), other_fs.get(0)) {
-                    (1, Some(o_f)) => o_f,
-                    _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
-                };
-
-                let args = vec![
-                    cx.expr_addr_of(span, self_f),
-                    cx.expr_addr_of(span, other_f.clone()),
-                ];
-
-                cx.expr_call_global(span, partial_cmp_path.clone(), args)
-            };
-
-            let assign = cx.stmt_let(span, false, test_id, new);
-
-            let cond = cx.expr_binary(span, ast::BiEq,
-                                      cx.expr_ident(span, test_id),
-                                      equals_expr.clone());
-            let if_ = cx.expr_if(span,
-                                 cond,
-                                 old, Some(cx.expr_ident(span, test_id)));
-            cx.expr_block(cx.block(span, vec!(assign), Some(if_)))
-        },
-        equals_expr.clone(),
-        Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
-            if self_args.len() != 2 {
-                cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
-            } else {
-                some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple)
-            }
-        }),
-        cx, span, substr)
-}
-
-/// Strict inequality.
-fn cs_op(less: bool, equal: bool, cx: &mut ExtCtxt,
-         span: Span, substr: &Substructure) -> P<Expr> {
-    let op = if less {ast::BiLt} else {ast::BiGt};
-    cs_fold(
-        false, // need foldr,
-        |cx, span, subexpr, self_f, other_fs| {
-            /*
-            build up a series of chain ||'s and &&'s from the inside
-            out (hence foldr) to get lexical ordering, i.e. for op ==
-            `ast::lt`
-
-            ```
-            self.f1 < other.f1 || (!(other.f1 < self.f1) &&
-                (self.f2 < other.f2 || (!(other.f2 < self.f2) &&
-                    (false)
-                ))
-            )
-            ```
-
-            The optimiser should remove the redundancy. We explicitly
-            get use the binops to avoid auto-deref dereferencing too many
-            layers of pointers, if the type includes pointers.
-            */
-            let other_f = match (other_fs.len(), other_fs.get(0)) {
-                (1, Some(o_f)) => o_f,
-                _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
-            };
-
-            let cmp = cx.expr_binary(span, op, self_f.clone(), other_f.clone());
-
-            let not_cmp = cx.expr_unary(span, ast::UnNot,
-                                        cx.expr_binary(span, op, other_f.clone(), self_f));
-
-            let and = cx.expr_binary(span, ast::BiAnd, not_cmp, subexpr);
-            cx.expr_binary(span, ast::BiOr, cmp, and)
-        },
-        cx.expr_bool(span, equal),
-        Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
-            if self_args.len() != 2 {
-                cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
-            } else {
-                let op = match (less, equal) {
-                    (true,  true) => LeOp, (true,  false) => LtOp,
-                    (false, true) => GeOp, (false, false) => GtOp,
-                };
-                some_ordering_collapsed(cx, span, op, tag_tuple)
-            }
-        }),
-        cx, span, substr)
-}
diff --git a/src/libsyntax/ext/deriving/debug.rs b/src/libsyntax/ext/deriving/debug.rs
deleted file mode 100644 (file)
index 9488cfb..0000000
+++ /dev/null
@@ -1,155 +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 ast;
-use ast::{MetaItem, Expr};
-use codemap::{Span, respan};
-use ext::base::{ExtCtxt, Annotatable};
-use ext::build::AstBuilder;
-use ext::deriving::generic::*;
-use ext::deriving::generic::ty::*;
-use parse::token;
-use ptr::P;
-
-pub fn expand_deriving_debug(cx: &mut ExtCtxt,
-                            span: Span,
-                            mitem: &MetaItem,
-                            item: &Annotatable,
-                            push: &mut FnMut(Annotatable))
-{
-    // &mut ::std::fmt::Formatter
-    let fmtr = Ptr(Box::new(Literal(path_std!(cx, core::fmt::Formatter))),
-                   Borrowed(None, ast::MutMutable));
-
-    let trait_def = TraitDef {
-        span: span,
-        attributes: Vec::new(),
-        path: path_std!(cx, core::fmt::Debug),
-        additional_bounds: Vec::new(),
-        generics: LifetimeBounds::empty(),
-        is_unsafe: false,
-        methods: vec![
-            MethodDef {
-                name: "fmt",
-                generics: LifetimeBounds::empty(),
-                explicit_self: borrowed_explicit_self(),
-                args: vec!(fmtr),
-                ret_ty: Literal(path_std!(cx, core::fmt::Result)),
-                attributes: Vec::new(),
-                is_unsafe: false,
-                combine_substructure: combine_substructure(Box::new(|a, b, c| {
-                    show_substructure(a, b, c)
-                }))
-            }
-        ],
-        associated_types: Vec::new(),
-    };
-    trait_def.expand(cx, mitem, item, push)
-}
-
-/// We use the debug builders to do the heavy lifting here
-fn show_substructure(cx: &mut ExtCtxt, span: Span,
-                     substr: &Substructure) -> P<Expr> {
-    // build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build()
-    // or fmt.debug_tuple(<name>).field(&<fieldval>)....build()
-    // based on the "shape".
-    let ident = match *substr.fields {
-        Struct(_) => substr.type_ident,
-        EnumMatching(_, v, _) => v.node.name,
-        EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
-            cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
-        }
-    };
-
-    // We want to make sure we have the expn_id set so that we can use unstable methods
-    let span = Span { expn_id: cx.backtrace(), .. span };
-    let name = cx.expr_lit(span, ast::Lit_::LitStr(ident.name.as_str(),
-                                                   ast::StrStyle::CookedStr));
-    let builder = token::str_to_ident("builder");
-    let builder_expr = cx.expr_ident(span, builder.clone());
-
-    let fmt = substr.nonself_args[0].clone();
-
-    let stmts = match *substr.fields {
-        Struct(ref fields) | EnumMatching(_, _, ref fields) => {
-            let mut stmts = vec![];
-            if fields.is_empty() || fields[0].name.is_none() {
-                // tuple struct/"normal" variant
-                let expr = cx.expr_method_call(span,
-                                               fmt,
-                                               token::str_to_ident("debug_tuple"),
-                                               vec![name]);
-                stmts.push(cx.stmt_let(span, true, builder, expr));
-
-                for field in fields {
-                    // Use double indirection to make sure this works for unsized types
-                    let field = cx.expr_addr_of(field.span, field.self_.clone());
-                    let field = cx.expr_addr_of(field.span, field);
-
-                    let expr = cx.expr_method_call(span,
-                                                   builder_expr.clone(),
-                                                   token::str_to_ident("field"),
-                                                   vec![field]);
-
-                    // Use `let _ = expr;` to avoid triggering the
-                    // unused_results lint.
-                    stmts.push(stmt_let_undescore(cx, span, expr));
-                }
-            } else {
-                // normal struct/struct variant
-                let expr = cx.expr_method_call(span,
-                                               fmt,
-                                               token::str_to_ident("debug_struct"),
-                                               vec![name]);
-                stmts.push(cx.stmt_let(span, true, builder, expr));
-
-                for field in fields {
-                    let name = cx.expr_lit(field.span, ast::Lit_::LitStr(
-                            field.name.unwrap().name.as_str(),
-                            ast::StrStyle::CookedStr));
-
-                    // Use double indirection to make sure this works for unsized types
-                    let field = cx.expr_addr_of(field.span, field.self_.clone());
-                    let field = cx.expr_addr_of(field.span, field);
-                    let expr = cx.expr_method_call(span,
-                                                   builder_expr.clone(),
-                                                   token::str_to_ident("field"),
-                                                   vec![name, field]);
-                    stmts.push(stmt_let_undescore(cx, span, expr));
-                }
-            }
-            stmts
-        }
-        _ => unreachable!()
-    };
-
-    let expr = cx.expr_method_call(span,
-                                   builder_expr,
-                                   token::str_to_ident("finish"),
-                                   vec![]);
-
-    let block = cx.block(span, stmts, Some(expr));
-    cx.expr_block(block)
-}
-
-fn stmt_let_undescore(cx: &mut ExtCtxt,
-                      sp: Span,
-                      expr: P<ast::Expr>) -> P<ast::Stmt> {
-    let local = P(ast::Local {
-        pat: cx.pat_wild(sp),
-        ty: None,
-        init: Some(expr),
-        id: ast::DUMMY_NODE_ID,
-        span: sp,
-        attrs: None,
-    });
-    let decl = respan(sp, ast::DeclLocal(local));
-    P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
-}
diff --git a/src/libsyntax/ext/deriving/decodable.rs b/src/libsyntax/ext/deriving/decodable.rs
deleted file mode 100644 (file)
index 0fdcbec..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-// 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 compiler code necessary for `#[derive(Decodable)]`. See encodable.rs for more.
-
-use ast;
-use ast::{MetaItem, Expr, MutMutable};
-use codemap::Span;
-use ext::base::{ExtCtxt, Annotatable};
-use ext::build::AstBuilder;
-use ext::deriving::generic::*;
-use ext::deriving::generic::ty::*;
-use parse::token::InternedString;
-use parse::token;
-use ptr::P;
-
-pub fn expand_deriving_rustc_decodable(cx: &mut ExtCtxt,
-                                       span: Span,
-                                       mitem: &MetaItem,
-                                       item: &Annotatable,
-                                       push: &mut FnMut(Annotatable))
-{
-    expand_deriving_decodable_imp(cx, span, mitem, item, push, "rustc_serialize")
-}
-
-pub fn expand_deriving_decodable(cx: &mut ExtCtxt,
-                                 span: Span,
-                                 mitem: &MetaItem,
-                                 item: &Annotatable,
-                                 push: &mut FnMut(Annotatable))
-{
-    expand_deriving_decodable_imp(cx, span, mitem, item, push, "serialize")
-}
-
-fn expand_deriving_decodable_imp(cx: &mut ExtCtxt,
-                                 span: Span,
-                                 mitem: &MetaItem,
-                                 item: &Annotatable,
-                                 push: &mut FnMut(Annotatable),
-                                 krate: &'static str)
-{
-    if cx.crate_root != Some("std") {
-        // FIXME(#21880): lift this requirement.
-        cx.span_err(span, "this trait cannot be derived with #![no_std] \
-                           or #![no_core]");
-        return
-    }
-
-    let trait_def = TraitDef {
-        span: span,
-        attributes: Vec::new(),
-        path: Path::new_(vec!(krate, "Decodable"), None, vec!(), true),
-        additional_bounds: Vec::new(),
-        generics: LifetimeBounds::empty(),
-        is_unsafe: false,
-        methods: vec!(
-            MethodDef {
-                name: "decode",
-                generics: LifetimeBounds {
-                    lifetimes: Vec::new(),
-                    bounds: vec!(("__D", vec!(Path::new_(
-                                    vec!(krate, "Decoder"), None,
-                                    vec!(), true))))
-                },
-                explicit_self: None,
-                args: vec!(Ptr(Box::new(Literal(Path::new_local("__D"))),
-                            Borrowed(None, MutMutable))),
-                ret_ty: Literal(Path::new_(
-                    pathvec_std!(cx, core::result::Result),
-                    None,
-                    vec!(Box::new(Self_), Box::new(Literal(Path::new_(
-                        vec!["__D", "Error"], None, vec![], false
-                    )))),
-                    true
-                )),
-                attributes: Vec::new(),
-                is_unsafe: false,
-                combine_substructure: combine_substructure(Box::new(|a, b, c| {
-                    decodable_substructure(a, b, c, krate)
-                })),
-            }
-        ),
-        associated_types: Vec::new(),
-    };
-
-    trait_def.expand(cx, mitem, item, push)
-}
-
-fn decodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
-                          substr: &Substructure,
-                          krate: &str) -> P<Expr> {
-    let decoder = substr.nonself_args[0].clone();
-    let recurse = vec!(cx.ident_of(krate),
-                    cx.ident_of("Decodable"),
-                    cx.ident_of("decode"));
-    let exprdecode = cx.expr_path(cx.path_global(trait_span, recurse));
-    // throw an underscore in front to suppress unused variable warnings
-    let blkarg = cx.ident_of("_d");
-    let blkdecoder = cx.expr_ident(trait_span, blkarg);
-
-    return match *substr.fields {
-        StaticStruct(_, ref summary) => {
-            let nfields = match *summary {
-                Unnamed(ref fields) => fields.len(),
-                Named(ref fields) => fields.len()
-            };
-            let read_struct_field = cx.ident_of("read_struct_field");
-
-            let path = cx.path_ident(trait_span, substr.type_ident);
-            let result = decode_static_fields(cx,
-                                              trait_span,
-                                              path,
-                                              summary,
-                                              |cx, span, name, field| {
-                cx.expr_try(span,
-                    cx.expr_method_call(span, blkdecoder.clone(), read_struct_field,
-                                        vec!(cx.expr_str(span, name),
-                                          cx.expr_usize(span, field),
-                                          exprdecode.clone())))
-            });
-            let result = cx.expr_ok(trait_span, result);
-            cx.expr_method_call(trait_span,
-                                decoder,
-                                cx.ident_of("read_struct"),
-                                vec!(
-                cx.expr_str(trait_span, substr.type_ident.name.as_str()),
-                cx.expr_usize(trait_span, nfields),
-                cx.lambda_expr_1(trait_span, result, blkarg)
-            ))
-        }
-        StaticEnum(_, ref fields) => {
-            let variant = cx.ident_of("i");
-
-            let mut arms = Vec::new();
-            let mut variants = Vec::new();
-            let rvariant_arg = cx.ident_of("read_enum_variant_arg");
-
-            for (i, &(ident, v_span, ref parts)) in fields.iter().enumerate() {
-                variants.push(cx.expr_str(v_span, ident.name.as_str()));
-
-                let path = cx.path(trait_span, vec![substr.type_ident, ident]);
-                let decoded = decode_static_fields(cx,
-                                                   v_span,
-                                                   path,
-                                                   parts,
-                                                   |cx, span, _, field| {
-                    let idx = cx.expr_usize(span, field);
-                    cx.expr_try(span,
-                        cx.expr_method_call(span, blkdecoder.clone(), rvariant_arg,
-                                            vec!(idx, exprdecode.clone())))
-                });
-
-                arms.push(cx.arm(v_span,
-                                 vec!(cx.pat_lit(v_span, cx.expr_usize(v_span, i))),
-                                 decoded));
-            }
-
-            arms.push(cx.arm_unreachable(trait_span));
-
-            let result = cx.expr_ok(trait_span,
-                                    cx.expr_match(trait_span,
-                                                  cx.expr_ident(trait_span, variant), arms));
-            let lambda = cx.lambda_expr(trait_span, vec!(blkarg, variant), result);
-            let variant_vec = cx.expr_vec(trait_span, variants);
-            let variant_vec = cx.expr_addr_of(trait_span, variant_vec);
-            let result = cx.expr_method_call(trait_span, blkdecoder,
-                                             cx.ident_of("read_enum_variant"),
-                                             vec!(variant_vec, lambda));
-            cx.expr_method_call(trait_span,
-                                decoder,
-                                cx.ident_of("read_enum"),
-                                vec!(
-                cx.expr_str(trait_span, substr.type_ident.name.as_str()),
-                cx.lambda_expr_1(trait_span, result, blkarg)
-            ))
-        }
-        _ => cx.bug("expected StaticEnum or StaticStruct in derive(Decodable)")
-    };
-}
-
-/// Create a decoder for a single enum variant/struct:
-/// - `outer_pat_path` is the path to this enum variant/struct
-/// - `getarg` should retrieve the `usize`-th field with name `@str`.
-fn decode_static_fields<F>(cx: &mut ExtCtxt,
-                           trait_span: Span,
-                           outer_pat_path: ast::Path,
-                           fields: &StaticFields,
-                           mut getarg: F)
-                           -> P<Expr> where
-    F: FnMut(&mut ExtCtxt, Span, InternedString, usize) -> P<Expr>,
-{
-    match *fields {
-        Unnamed(ref fields) => {
-            let path_expr = cx.expr_path(outer_pat_path);
-            if fields.is_empty() {
-                path_expr
-            } else {
-                let fields = fields.iter().enumerate().map(|(i, &span)| {
-                    getarg(cx, span,
-                           token::intern_and_get_ident(&format!("_field{}", i)),
-                           i)
-                }).collect();
-
-                cx.expr_call(trait_span, path_expr, fields)
-            }
-        }
-        Named(ref fields) => {
-            // use the field's span to get nicer error messages.
-            let fields = fields.iter().enumerate().map(|(i, &(ident, span))| {
-                let arg = getarg(cx, span, ident.name.as_str(), i);
-                cx.field_imm(span, ident, arg)
-            }).collect();
-            cx.expr_struct(trait_span, outer_pat_path, fields)
-        }
-    }
-}
diff --git a/src/libsyntax/ext/deriving/default.rs b/src/libsyntax/ext/deriving/default.rs
deleted file mode 100644 (file)
index 6a25088..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-// 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.
-
-use ast::{MetaItem, Expr};
-use codemap::Span;
-use ext::base::{ExtCtxt, Annotatable};
-use ext::build::AstBuilder;
-use ext::deriving::generic::*;
-use ext::deriving::generic::ty::*;
-use parse::token::InternedString;
-use ptr::P;
-
-pub fn expand_deriving_default(cx: &mut ExtCtxt,
-                               span: Span,
-                               mitem: &MetaItem,
-                               item: &Annotatable,
-                               push: &mut FnMut(Annotatable))
-{
-    let inline = cx.meta_word(span, InternedString::new("inline"));
-    let attrs = vec!(cx.attribute(span, inline));
-    let trait_def = TraitDef {
-        span: span,
-        attributes: Vec::new(),
-        path: path_std!(cx, core::default::Default),
-        additional_bounds: Vec::new(),
-        generics: LifetimeBounds::empty(),
-        is_unsafe: false,
-        methods: vec!(
-            MethodDef {
-                name: "default",
-                generics: LifetimeBounds::empty(),
-                explicit_self: None,
-                args: Vec::new(),
-                ret_ty: Self_,
-                attributes: attrs,
-                is_unsafe: false,
-                combine_substructure: combine_substructure(Box::new(|a, b, c| {
-                    default_substructure(a, b, c)
-                }))
-            }
-        ),
-        associated_types: Vec::new(),
-    };
-    trait_def.expand(cx, mitem, item, push)
-}
-
-fn default_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
-    let default_ident = cx.std_path(&["default", "Default", "default"]);
-    let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new());
-
-    return match *substr.fields {
-        StaticStruct(_, ref summary) => {
-            match *summary {
-                Unnamed(ref fields) => {
-                    if fields.is_empty() {
-                        cx.expr_ident(trait_span, substr.type_ident)
-                    } else {
-                        let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
-                        cx.expr_call_ident(trait_span, substr.type_ident, exprs)
-                    }
-                }
-                Named(ref fields) => {
-                    let default_fields = fields.iter().map(|&(ident, span)| {
-                        cx.field_imm(span, ident, default_call(span))
-                    }).collect();
-                    cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
-                }
-            }
-        }
-        StaticEnum(..) => {
-            cx.span_err(trait_span, "`Default` cannot be derived for enums, only structs");
-            // let compilation continue
-            cx.expr_usize(trait_span, 0)
-        }
-        _ => cx.span_bug(trait_span, "Non-static method in `derive(Default)`")
-    };
-}
diff --git a/src/libsyntax/ext/deriving/encodable.rs b/src/libsyntax/ext/deriving/encodable.rs
deleted file mode 100644 (file)
index 7867399..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-// 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 compiler code necessary to implement the `#[derive(Encodable)]`
-//! (and `Decodable`, in decodable.rs) extension.  The idea here is that
-//! type-defining items may be tagged with `#[derive(Encodable, Decodable)]`.
-//!
-//! For example, a type like:
-//!
-//! ```ignore
-//! #[derive(Encodable, Decodable)]
-//! struct Node { id: usize }
-//! ```
-//!
-//! would generate two implementations like:
-//!
-//! ```ignore
-//! impl<S: Encoder<E>, E> Encodable<S, E> for Node {
-//!     fn encode(&self, s: &mut S) -> Result<(), E> {
-//!         s.emit_struct("Node", 1, |this| {
-//!             this.emit_struct_field("id", 0, |this| {
-//!                 Encodable::encode(&self.id, this)
-//!                 /* this.emit_usize(self.id) can also be used */
-//!             })
-//!         })
-//!     }
-//! }
-//!
-//! impl<D: Decoder<E>, E> Decodable<D, E> for Node {
-//!     fn decode(d: &mut D) -> Result<Node, E> {
-//!         d.read_struct("Node", 1, |this| {
-//!             match this.read_struct_field("id", 0, |this| Decodable::decode(this)) {
-//!                 Ok(id) => Ok(Node { id: id }),
-//!                 Err(e) => Err(e),
-//!             }
-//!         })
-//!     }
-//! }
-//! ```
-//!
-//! Other interesting scenarios are when the item has type parameters or
-//! references other non-built-in types.  A type definition like:
-//!
-//! ```ignore
-//! #[derive(Encodable, Decodable)]
-//! struct Spanned<T> { node: T, span: Span }
-//! ```
-//!
-//! would yield functions like:
-//!
-//! ```ignore
-//! impl<
-//!     S: Encoder<E>,
-//!     E,
-//!     T: Encodable<S, E>
-//! > Encodable<S, E> for Spanned<T> {
-//!     fn encode(&self, s: &mut S) -> Result<(), E> {
-//!         s.emit_struct("Spanned", 2, |this| {
-//!             this.emit_struct_field("node", 0, |this| self.node.encode(this))
-//!                 .unwrap();
-//!             this.emit_struct_field("span", 1, |this| self.span.encode(this))
-//!         })
-//!     }
-//! }
-//!
-//! impl<
-//!     D: Decoder<E>,
-//!     E,
-//!     T: Decodable<D, E>
-//! > Decodable<D, E> for Spanned<T> {
-//!     fn decode(d: &mut D) -> Result<Spanned<T>, E> {
-//!         d.read_struct("Spanned", 2, |this| {
-//!             Ok(Spanned {
-//!                 node: this.read_struct_field("node", 0, |this| Decodable::decode(this))
-//!                     .unwrap(),
-//!                 span: this.read_struct_field("span", 1, |this| Decodable::decode(this))
-//!                     .unwrap(),
-//!             })
-//!         })
-//!     }
-//! }
-//! ```
-
-use ast::{MetaItem, Expr, ExprRet, MutMutable};
-use codemap::Span;
-use ext::base::{ExtCtxt,Annotatable};
-use ext::build::AstBuilder;
-use ext::deriving::generic::*;
-use ext::deriving::generic::ty::*;
-use parse::token;
-use ptr::P;
-
-pub fn expand_deriving_rustc_encodable(cx: &mut ExtCtxt,
-                                       span: Span,
-                                       mitem: &MetaItem,
-                                       item: &Annotatable,
-                                       push: &mut FnMut(Annotatable))
-{
-    expand_deriving_encodable_imp(cx, span, mitem, item, push, "rustc_serialize")
-}
-
-pub fn expand_deriving_encodable(cx: &mut ExtCtxt,
-                                 span: Span,
-                                 mitem: &MetaItem,
-                                 item: &Annotatable,
-                                 push: &mut FnMut(Annotatable))
-{
-    expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize")
-}
-
-fn expand_deriving_encodable_imp(cx: &mut ExtCtxt,
-                                 span: Span,
-                                 mitem: &MetaItem,
-                                 item: &Annotatable,
-                                 push: &mut FnMut(Annotatable),
-                                 krate: &'static str)
-{
-    if cx.crate_root != Some("std") {
-        // FIXME(#21880): lift this requirement.
-        cx.span_err(span, "this trait cannot be derived with #![no_std] \
-                           or #![no_core]");
-        return;
-    }
-
-    let trait_def = TraitDef {
-        span: span,
-        attributes: Vec::new(),
-        path: Path::new_(vec!(krate, "Encodable"), None, vec!(), true),
-        additional_bounds: Vec::new(),
-        generics: LifetimeBounds::empty(),
-        is_unsafe: false,
-        methods: vec!(
-            MethodDef {
-                name: "encode",
-                generics: LifetimeBounds {
-                    lifetimes: Vec::new(),
-                    bounds: vec!(("__S", vec!(Path::new_(
-                                    vec!(krate, "Encoder"), None,
-                                    vec!(), true))))
-                },
-                explicit_self: borrowed_explicit_self(),
-                args: vec!(Ptr(Box::new(Literal(Path::new_local("__S"))),
-                            Borrowed(None, MutMutable))),
-                ret_ty: Literal(Path::new_(
-                    pathvec_std!(cx, core::result::Result),
-                    None,
-                    vec!(Box::new(Tuple(Vec::new())), Box::new(Literal(Path::new_(
-                        vec!["__S", "Error"], None, vec![], false
-                    )))),
-                    true
-                )),
-                attributes: Vec::new(),
-                is_unsafe: false,
-                combine_substructure: combine_substructure(Box::new(|a, b, c| {
-                    encodable_substructure(a, b, c)
-                })),
-            }
-        ),
-        associated_types: Vec::new(),
-    };
-
-    trait_def.expand(cx, mitem, item, push)
-}
-
-fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
-                          substr: &Substructure) -> P<Expr> {
-    let encoder = substr.nonself_args[0].clone();
-    // throw an underscore in front to suppress unused variable warnings
-    let blkarg = cx.ident_of("_e");
-    let blkencoder = cx.expr_ident(trait_span, blkarg);
-    let encode = cx.ident_of("encode");
-
-    return match *substr.fields {
-        Struct(ref fields) => {
-            let emit_struct_field = cx.ident_of("emit_struct_field");
-            let mut stmts = Vec::new();
-            for (i, &FieldInfo {
-                    name,
-                    ref self_,
-                    span,
-                    ..
-                }) in fields.iter().enumerate() {
-                let name = match name {
-                    Some(id) => id.name.as_str(),
-                    None => {
-                        token::intern_and_get_ident(&format!("_field{}", i))
-                    }
-                };
-                let enc = cx.expr_method_call(span, self_.clone(),
-                                              encode, vec!(blkencoder.clone()));
-                let lambda = cx.lambda_expr_1(span, enc, blkarg);
-                let call = cx.expr_method_call(span, blkencoder.clone(),
-                                               emit_struct_field,
-                                               vec!(cx.expr_str(span, name),
-                                                 cx.expr_usize(span, i),
-                                                 lambda));
-
-                // last call doesn't need a try!
-                let last = fields.len() - 1;
-                let call = if i != last {
-                    cx.expr_try(span, call)
-                } else {
-                    cx.expr(span, ExprRet(Some(call)))
-                };
-                stmts.push(cx.stmt_expr(call));
-            }
-
-            // unit structs have no fields and need to return Ok()
-            if stmts.is_empty() {
-                let ret_ok = cx.expr(trait_span,
-                                     ExprRet(Some(cx.expr_ok(trait_span,
-                                                             cx.expr_tuple(trait_span, vec![])))));
-                stmts.push(cx.stmt_expr(ret_ok));
-            }
-
-            let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
-            cx.expr_method_call(trait_span,
-                                encoder,
-                                cx.ident_of("emit_struct"),
-                                vec!(
-                cx.expr_str(trait_span, substr.type_ident.name.as_str()),
-                cx.expr_usize(trait_span, fields.len()),
-                blk
-            ))
-        }
-
-        EnumMatching(idx, variant, ref fields) => {
-            // We're not generating an AST that the borrow checker is expecting,
-            // so we need to generate a unique local variable to take the
-            // mutable loan out on, otherwise we get conflicts which don't
-            // actually exist.
-            let me = cx.stmt_let(trait_span, false, blkarg, encoder);
-            let encoder = cx.expr_ident(trait_span, blkarg);
-            let emit_variant_arg = cx.ident_of("emit_enum_variant_arg");
-            let mut stmts = Vec::new();
-            if !fields.is_empty() {
-                let last = fields.len() - 1;
-                for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() {
-                    let enc = cx.expr_method_call(span, self_.clone(),
-                                                  encode, vec!(blkencoder.clone()));
-                    let lambda = cx.lambda_expr_1(span, enc, blkarg);
-                    let call = cx.expr_method_call(span, blkencoder.clone(),
-                                                   emit_variant_arg,
-                                                   vec!(cx.expr_usize(span, i),
-                                                        lambda));
-                    let call = if i != last {
-                        cx.expr_try(span, call)
-                    } else {
-                        cx.expr(span, ExprRet(Some(call)))
-                    };
-                    stmts.push(cx.stmt_expr(call));
-                }
-            } else {
-                let ret_ok = cx.expr(trait_span,
-                                     ExprRet(Some(cx.expr_ok(trait_span,
-                                                             cx.expr_tuple(trait_span, vec![])))));
-                stmts.push(cx.stmt_expr(ret_ok));
-            }
-
-            let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
-            let name = cx.expr_str(trait_span, variant.node.name.name.as_str());
-            let call = cx.expr_method_call(trait_span, blkencoder,
-                                           cx.ident_of("emit_enum_variant"),
-                                           vec!(name,
-                                             cx.expr_usize(trait_span, idx),
-                                             cx.expr_usize(trait_span, fields.len()),
-                                             blk));
-            let blk = cx.lambda_expr_1(trait_span, call, blkarg);
-            let ret = cx.expr_method_call(trait_span,
-                                          encoder,
-                                          cx.ident_of("emit_enum"),
-                                          vec!(
-                cx.expr_str(trait_span, substr.type_ident.name.as_str()),
-                blk
-            ));
-            cx.expr_block(cx.block(trait_span, vec!(me), Some(ret)))
-        }
-
-        _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)")
-    };
-}
diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs
deleted file mode 100644 (file)
index bd89430..0000000
+++ /dev/null
@@ -1,1704 +0,0 @@
-// Copyright 2013-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.
-
-//! Some code that abstracts away much of the boilerplate of writing
-//! `derive` instances for traits. Among other things it manages getting
-//! access to the fields of the 4 different sorts of structs and enum
-//! variants, as well as creating the method and impl ast instances.
-//!
-//! Supported features (fairly exhaustive):
-//!
-//! - Methods taking any number of parameters of any type, and returning
-//!   any type, other than vectors, bottom and closures.
-//! - Generating `impl`s for types with type parameters and lifetimes
-//!   (e.g. `Option<T>`), the parameters are automatically given the
-//!   current trait as a bound. (This includes separate type parameters
-//!   and lifetimes for methods.)
-//! - Additional bounds on the type parameters (`TraitDef.additional_bounds`)
-//!
-//! The most important thing for implementers is the `Substructure` and
-//! `SubstructureFields` objects. The latter groups 5 possibilities of the
-//! arguments:
-//!
-//! - `Struct`, when `Self` is a struct (including tuple structs, e.g
-//!   `struct T(i32, char)`).
-//! - `EnumMatching`, when `Self` is an enum and all the arguments are the
-//!   same variant of the enum (e.g. `Some(1)`, `Some(3)` and `Some(4)`)
-//! - `EnumNonMatchingCollapsed` when `Self` is an enum and the arguments
-//!   are not the same variant (e.g. `None`, `Some(1)` and `None`).
-//! - `StaticEnum` and `StaticStruct` for static methods, where the type
-//!   being derived upon is either an enum or struct respectively. (Any
-//!   argument with type Self is just grouped among the non-self
-//!   arguments.)
-//!
-//! In the first two cases, the values from the corresponding fields in
-//! all the arguments are grouped together. For `EnumNonMatchingCollapsed`
-//! this isn't possible (different variants have different fields), so the
-//! fields are inaccessible. (Previous versions of the deriving infrastructure
-//! had a way to expand into code that could access them, at the cost of
-//! generating exponential amounts of code; see issue #15375). There are no
-//! fields with values in the static cases, so these are treated entirely
-//! differently.
-//!
-//! The non-static cases have `Option<ident>` in several places associated
-//! with field `expr`s. This represents the name of the field it is
-//! associated with. It is only not `None` when the associated field has
-//! an identifier in the source code. For example, the `x`s in the
-//! following snippet
-//!
-//! ```rust
-//! # #![allow(dead_code)]
-//! struct A { x : i32 }
-//!
-//! struct B(i32);
-//!
-//! enum C {
-//!     C0(i32),
-//!     C1 { x: i32 }
-//! }
-//! ```
-//!
-//! The `i32`s in `B` and `C0` don't have an identifier, so the
-//! `Option<ident>`s would be `None` for them.
-//!
-//! In the static cases, the structure is summarised, either into the just
-//! spans of the fields or a list of spans and the field idents (for tuple
-//! structs and record structs, respectively), or a list of these, for
-//! enums (one for each variant). For empty struct and empty enum
-//! variants, it is represented as a count of 0.
-//!
-//! # "`cs`" functions
-//!
-//! The `cs_...` functions ("combine substructure) are designed to
-//! make life easier by providing some pre-made recipes for common
-//! threads; mostly calling the function being derived on all the
-//! arguments and then combining them back together in some way (or
-//! letting the user chose that). They are not meant to be the only
-//! way to handle the structures that this code creates.
-//!
-//! # Examples
-//!
-//! The following simplified `PartialEq` is used for in-code examples:
-//!
-//! ```rust
-//! trait PartialEq {
-//!     fn eq(&self, other: &Self) -> bool;
-//! }
-//! impl PartialEq for i32 {
-//!     fn eq(&self, other: &i32) -> bool {
-//!         *self == *other
-//!     }
-//! }
-//! ```
-//!
-//! Some examples of the values of `SubstructureFields` follow, using the
-//! above `PartialEq`, `A`, `B` and `C`.
-//!
-//! ## Structs
-//!
-//! When generating the `expr` for the `A` impl, the `SubstructureFields` is
-//!
-//! ```{.text}
-//! Struct(vec![FieldInfo {
-//!            span: <span of x>
-//!            name: Some(<ident of x>),
-//!            self_: <expr for &self.x>,
-//!            other: vec![<expr for &other.x]
-//!          }])
-//! ```
-//!
-//! For the `B` impl, called with `B(a)` and `B(b)`,
-//!
-//! ```{.text}
-//! Struct(vec![FieldInfo {
-//!           span: <span of `i32`>,
-//!           name: None,
-//!           self_: <expr for &a>
-//!           other: vec![<expr for &b>]
-//!          }])
-//! ```
-//!
-//! ## Enums
-//!
-//! When generating the `expr` for a call with `self == C0(a)` and `other
-//! == C0(b)`, the SubstructureFields is
-//!
-//! ```{.text}
-//! EnumMatching(0, <ast::Variant for C0>,
-//!              vec![FieldInfo {
-//!                 span: <span of i32>
-//!                 name: None,
-//!                 self_: <expr for &a>,
-//!                 other: vec![<expr for &b>]
-//!               }])
-//! ```
-//!
-//! For `C1 {x}` and `C1 {x}`,
-//!
-//! ```{.text}
-//! EnumMatching(1, <ast::Variant for C1>,
-//!              vec![FieldInfo {
-//!                 span: <span of x>
-//!                 name: Some(<ident of x>),
-//!                 self_: <expr for &self.x>,
-//!                 other: vec![<expr for &other.x>]
-//!                }])
-//! ```
-//!
-//! For `C0(a)` and `C1 {x}` ,
-//!
-//! ```{.text}
-//! EnumNonMatchingCollapsed(
-//!     vec![<ident of self>, <ident of __arg_1>],
-//!     &[<ast::Variant for C0>, <ast::Variant for C1>],
-//!     &[<ident for self index value>, <ident of __arg_1 index value>])
-//! ```
-//!
-//! It is the same for when the arguments are flipped to `C1 {x}` and
-//! `C0(a)`; the only difference is what the values of the identifiers
-//! <ident for self index value> and <ident of __arg_1 index value> will
-//! be in the generated code.
-//!
-//! `EnumNonMatchingCollapsed` deliberately provides far less information
-//! than is generally available for a given pair of variants; see #15375
-//! for discussion.
-//!
-//! ## Static
-//!
-//! A static method on the types above would result in,
-//!
-//! ```{.text}
-//! StaticStruct(<ast::VariantData of A>, Named(vec![(<ident of x>, <span of x>)]))
-//!
-//! StaticStruct(<ast::VariantData of B>, Unnamed(vec![<span of x>]))
-//!
-//! StaticEnum(<ast::EnumDef of C>,
-//!            vec![(<ident of C0>, <span of C0>, Unnamed(vec![<span of i32>])),
-//!                 (<ident of C1>, <span of C1>, Named(vec![(<ident of x>, <span of x>)]))])
-//! ```
-
-pub use self::StaticFields::*;
-pub use self::SubstructureFields::*;
-use self::StructType::*;
-
-use std::cell::RefCell;
-use std::collections::HashSet;
-use std::vec;
-
-use abi::Abi;
-use abi;
-use ast;
-use ast::{EnumDef, Expr, Ident, Generics, VariantData};
-use ast_util;
-use attr;
-use attr::AttrMetaMethods;
-use ext::base::{ExtCtxt, Annotatable};
-use ext::build::AstBuilder;
-use codemap::{self, DUMMY_SP};
-use codemap::Span;
-use diagnostic::SpanHandler;
-use util::move_map::MoveMap;
-use owned_slice::OwnedSlice;
-use parse::token::{intern, InternedString};
-use parse::token::special_idents;
-use ptr::P;
-
-use self::ty::{LifetimeBounds, Path, Ptr, PtrTy, Self_, Ty};
-
-pub mod ty;
-
-pub struct TraitDef<'a> {
-    /// The span for the current #[derive(Foo)] header.
-    pub span: Span,
-
-    pub attributes: Vec<ast::Attribute>,
-
-    /// Path of the trait, including any type parameters
-    pub path: Path<'a>,
-
-    /// Additional bounds required of any type parameters of the type,
-    /// other than the current trait
-    pub additional_bounds: Vec<Ty<'a>>,
-
-    /// Any extra lifetimes and/or bounds, e.g. `D: serialize::Decoder`
-    pub generics: LifetimeBounds<'a>,
-
-    /// Is it an `unsafe` trait?
-    pub is_unsafe: bool,
-
-    pub methods: Vec<MethodDef<'a>>,
-
-    pub associated_types: Vec<(ast::Ident, Ty<'a>)>,
-}
-
-
-pub struct MethodDef<'a> {
-    /// name of the method
-    pub name: &'a str,
-    /// List of generics, e.g. `R: rand::Rng`
-    pub generics: LifetimeBounds<'a>,
-
-    /// Whether there is a self argument (outer Option) i.e. whether
-    /// this is a static function, and whether it is a pointer (inner
-    /// Option)
-    pub explicit_self: Option<Option<PtrTy<'a>>>,
-
-    /// Arguments other than the self argument
-    pub args: Vec<Ty<'a>>,
-
-    /// Return type
-    pub ret_ty: Ty<'a>,
-
-    pub attributes: Vec<ast::Attribute>,
-
-    // Is it an `unsafe fn`?
-    pub is_unsafe: bool,
-
-    pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>,
-}
-
-/// All the data about the data structure/method being derived upon.
-pub struct Substructure<'a> {
-    /// ident of self
-    pub type_ident: Ident,
-    /// ident of the method
-    pub method_ident: Ident,
-    /// dereferenced access to any `Self_` or `Ptr(Self_, _)` arguments
-    pub self_args: &'a [P<Expr>],
-    /// verbatim access to any other arguments
-    pub nonself_args: &'a [P<Expr>],
-    pub fields: &'a SubstructureFields<'a>
-}
-
-/// Summary of the relevant parts of a struct/enum field.
-pub struct FieldInfo<'a> {
-    pub span: Span,
-    /// None for tuple structs/normal enum variants, Some for normal
-    /// structs/struct enum variants.
-    pub name: Option<Ident>,
-    /// The expression corresponding to this field of `self`
-    /// (specifically, a reference to it).
-    pub self_: P<Expr>,
-    /// The expressions corresponding to references to this field in
-    /// the other `Self` arguments.
-    pub other: Vec<P<Expr>>,
-    /// The attributes on the field
-    pub attrs: &'a [ast::Attribute],
-}
-
-/// Fields for a static method
-pub enum StaticFields {
-    /// Tuple structs/enum variants like this.
-    Unnamed(Vec<Span>),
-    /// Normal structs/struct variants.
-    Named(Vec<(Ident, Span)>),
-}
-
-/// A summary of the possible sets of fields.
-pub enum SubstructureFields<'a> {
-    Struct(Vec<FieldInfo<'a>>),
-    /// Matching variants of the enum: variant index, ast::Variant,
-    /// fields: the field name is only non-`None` in the case of a struct
-    /// variant.
-    EnumMatching(usize, &'a ast::Variant, Vec<FieldInfo<'a>>),
-
-    /// Non-matching variants of the enum, but with all state hidden from
-    /// the consequent code.  The first component holds `Ident`s for all of
-    /// the `Self` arguments; the second component is a slice of all of the
-    /// variants for the enum itself, and the third component is a list of
-    /// `Ident`s bound to the variant index values for each of the actual
-    /// input `Self` arguments.
-    EnumNonMatchingCollapsed(Vec<Ident>, &'a [P<ast::Variant>], &'a [Ident]),
-
-    /// A static method where `Self` is a struct.
-    StaticStruct(&'a ast::VariantData, StaticFields),
-    /// A static method where `Self` is an enum.
-    StaticEnum(&'a ast::EnumDef, Vec<(Ident, Span, StaticFields)>),
-}
-
-
-
-/// Combine the values of all the fields together. The last argument is
-/// all the fields of all the structures.
-pub type CombineSubstructureFunc<'a> =
-    Box<FnMut(&mut ExtCtxt, Span, &Substructure) -> P<Expr> + 'a>;
-
-/// Deal with non-matching enum variants.  The tuple is a list of
-/// identifiers (one for each `Self` argument, which could be any of the
-/// variants since they have been collapsed together) and the identifiers
-/// holding the variant index value for each of the `Self` arguments.  The
-/// last argument is all the non-`Self` args of the method being derived.
-pub type EnumNonMatchCollapsedFunc<'a> =
-    Box<FnMut(&mut ExtCtxt, Span, (&[Ident], &[Ident]), &[P<Expr>]) -> P<Expr> + 'a>;
-
-pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>)
-    -> RefCell<CombineSubstructureFunc<'a>> {
-    RefCell::new(f)
-}
-
-/// 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>> {
-    use visit;
-
-    struct Visitor<'a> {
-        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) {
-            match ty.node {
-                ast::TyPath(_, 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()));
-                            }
-                        }
-                        None => {}
-                    }
-                }
-                _ => {}
-            }
-
-            visit::walk_ty(self, ty)
-        }
-    }
-
-    let mut visitor = Visitor {
-        ty_param_names: ty_param_names,
-        types: Vec::new(),
-    };
-
-    visit::Visitor::visit_ty(&mut visitor, ty);
-
-    visitor.types
-}
-
-impl<'a> TraitDef<'a> {
-    pub fn expand(&self,
-                  cx: &mut ExtCtxt,
-                  mitem: &ast::MetaItem,
-                  item: &'a Annotatable,
-                  push: &mut FnMut(Annotatable))
-    {
-        match *item {
-            Annotatable::Item(ref item) => {
-                let newitem = match item.node {
-                    ast::ItemStruct(ref struct_def, ref generics) => {
-                        self.expand_struct_def(cx,
-                                               &struct_def,
-                                               item.ident,
-                                               generics)
-                    }
-                    ast::ItemEnum(ref enum_def, ref generics) => {
-                        self.expand_enum_def(cx,
-                                             enum_def,
-                                             &item.attrs,
-                                             item.ident,
-                                             generics)
-                    }
-                    _ => {
-                        cx.span_err(mitem.span,
-                                    "`derive` may only be applied to structs and enums");
-                        return;
-                    }
-                };
-                // Keep the lint attributes of the previous item to control how the
-                // generated implementations are linted
-                let mut attrs = newitem.attrs.clone();
-                attrs.extend(item.attrs.iter().filter(|a| {
-                    match &a.name()[..] {
-                        "allow" | "warn" | "deny" | "forbid" | "stable" | "unstable" => true,
-                        _ => false,
-                    }
-                }).cloned());
-                push(Annotatable::Item(P(ast::Item {
-                    attrs: attrs,
-                    ..(*newitem).clone()
-                })))
-            }
-            _ => {
-                cx.span_err(mitem.span, "`derive` may only be applied to structs and enums");
-            }
-        }
-    }
-
-    /// Given that we are deriving a trait `DerivedTrait` for a type like:
-    ///
-    /// ```ignore
-    /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
-    ///     a: A,
-    ///     b: B::Item,
-    ///     b1: <B as DeclaredTrait>::Item,
-    ///     c1: <C as WhereTrait>::Item,
-    ///     c2: Option<<C as WhereTrait>::Item>,
-    ///     ...
-    /// }
-    /// ```
-    ///
-    /// create an impl like:
-    ///
-    /// ```ignore
-    /// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ...  Z> where
-    ///     C:                       WhereTrait,
-    ///     A: DerivedTrait + B1 + ... + BN,
-    ///     B: DerivedTrait + B1 + ... + BN,
-    ///     C: DerivedTrait + B1 + ... + BN,
-    ///     B::Item:                 DerivedTrait + B1 + ... + BN,
-    ///     <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
-    ///     ...
-    /// {
-    ///     ...
-    /// }
-    /// ```
-    ///
-    /// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and
-    /// therefore does not get bound by the derived trait.
-    fn create_derived_impl(&self,
-                           cx: &mut ExtCtxt,
-                           type_ident: Ident,
-                           generics: &Generics,
-                           field_tys: Vec<P<ast::Ty>>,
-                           methods: Vec<P<ast::ImplItem>>) -> P<ast::Item> {
-        let trait_path = self.path.to_path(cx, self.span, type_ident, generics);
-
-        // Transform associated types from `deriving::ty::Ty` into `ast::ImplItem`
-        let associated_types = self.associated_types.iter().map(|&(ident, ref type_def)| {
-            P(ast::ImplItem {
-                id: ast::DUMMY_NODE_ID,
-                span: self.span,
-                ident: ident,
-                vis: ast::Inherited,
-                attrs: Vec::new(),
-                node: ast::ImplItemKind::Type(type_def.to_ty(cx,
-                    self.span,
-                    type_ident,
-                    generics
-                )),
-            })
-        });
-
-        let Generics { mut lifetimes, ty_params, mut where_clause } =
-            self.generics.to_generics(cx, self.span, type_ident, generics);
-        let mut ty_params = ty_params.into_vec();
-
-        // Copy the lifetimes
-        lifetimes.extend(generics.lifetimes.iter().cloned());
-
-        // Create the type parameters.
-        ty_params.extend(generics.ty_params.iter().map(|ty_param| {
-            // I don't think this can be moved out of the loop, since
-            // a TyParamBound requires an ast id
-            let mut bounds: Vec<_> =
-                // extra restrictions on the generics parameters to the type being derived upon
-                self.additional_bounds.iter().map(|p| {
-                    cx.typarambound(p.to_path(cx, self.span,
-                                                  type_ident, generics))
-                }).collect();
-
-            // require the current trait
-            bounds.push(cx.typarambound(trait_path.clone()));
-
-            // also add in any bounds from the declaration
-            for declared_bound in ty_param.bounds.iter() {
-                bounds.push((*declared_bound).clone());
-            }
-
-            cx.typaram(self.span,
-                       ty_param.ident,
-                       OwnedSlice::from_vec(bounds),
-                       None)
-        }));
-
-        // and similarly for where clauses
-        where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| {
-            match *clause {
-                ast::WherePredicate::BoundPredicate(ref wb) => {
-                    ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
-                        span: self.span,
-                        bound_lifetimes: wb.bound_lifetimes.clone(),
-                        bounded_ty: wb.bounded_ty.clone(),
-                        bounds: OwnedSlice::from_vec(wb.bounds.iter().cloned().collect())
-                    })
-                }
-                ast::WherePredicate::RegionPredicate(ref rb) => {
-                    ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
-                        span: self.span,
-                        lifetime: rb.lifetime,
-                        bounds: rb.bounds.iter().cloned().collect()
-                    })
-                }
-                ast::WherePredicate::EqPredicate(ref we) => {
-                    ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
-                        id: ast::DUMMY_NODE_ID,
-                        span: self.span,
-                        path: we.path.clone(),
-                        ty: we.ty.clone()
-                    })
-                }
-            }
-        }));
-
-        if !ty_params.is_empty() {
-            let ty_param_names: Vec<ast::Name> = ty_params.iter()
-                .map(|ty_param| ty_param.ident.name)
-                .collect();
-
-            let mut processed_field_types = HashSet::new();
-            for field_ty in field_tys {
-                let tys = find_type_parameters(&*field_ty, &ty_param_names);
-
-                for ty in tys {
-                    // if we have already handled this type, skip it
-                    if let ast::TyPath(_, ref p) = ty.node {
-                        if p.segments.len() == 1
-                            && ty_param_names.contains(&p.segments[0].identifier.name)
-                            || processed_field_types.contains(&p.segments) {
-                            continue;
-                        };
-                        processed_field_types.insert(p.segments.clone());
-                    }
-                    let mut bounds: Vec<_> = self.additional_bounds.iter().map(|p| {
-                        cx.typarambound(p.to_path(cx, self.span, type_ident, generics))
-                    }).collect();
-
-                    // require the current trait
-                    bounds.push(cx.typarambound(trait_path.clone()));
-
-                    let predicate = ast::WhereBoundPredicate {
-                        span: self.span,
-                        bound_lifetimes: vec![],
-                        bounded_ty: ty,
-                        bounds: OwnedSlice::from_vec(bounds),
-                    };
-
-                    let predicate = ast::WherePredicate::BoundPredicate(predicate);
-                    where_clause.predicates.push(predicate);
-                }
-            }
-        }
-
-        let trait_generics = Generics {
-            lifetimes: lifetimes,
-            ty_params: OwnedSlice::from_vec(ty_params),
-            where_clause: where_clause
-        };
-
-        // Create the reference to the trait.
-        let trait_ref = cx.trait_ref(trait_path);
-
-        // Create the type parameters on the `self` path.
-        let self_ty_params = generics.ty_params.map(|ty_param| {
-            cx.ty_ident(self.span, ty_param.ident)
-        });
-
-        let self_lifetimes: Vec<ast::Lifetime> =
-            generics.lifetimes
-            .iter()
-            .map(|ld| ld.lifetime)
-            .collect();
-
-        // Create the type of `self`.
-        let self_type = cx.ty_path(
-            cx.path_all(self.span, false, vec!( type_ident ), self_lifetimes,
-                        self_ty_params.into_vec(), Vec::new()));
-
-        let attr = cx.attribute(
-            self.span,
-            cx.meta_word(self.span,
-                         InternedString::new("automatically_derived")));
-        // Just mark it now since we know that it'll end up used downstream
-        attr::mark_used(&attr);
-        let opt_trait_ref = Some(trait_ref);
-        let ident = ast_util::impl_pretty_name(&opt_trait_ref, Some(&*self_type));
-        let unused_qual = cx.attribute(
-            self.span,
-            cx.meta_list(self.span,
-                         InternedString::new("allow"),
-                         vec![cx.meta_word(self.span,
-                                           InternedString::new("unused_qualifications"))]));
-        let mut a = vec![attr, unused_qual];
-        a.extend(self.attributes.iter().cloned());
-
-        let unsafety = if self.is_unsafe {
-            ast::Unsafety::Unsafe
-        } else {
-            ast::Unsafety::Normal
-        };
-
-        cx.item(
-            self.span,
-            ident,
-            a,
-            ast::ItemImpl(unsafety,
-                          ast::ImplPolarity::Positive,
-                          trait_generics,
-                          opt_trait_ref,
-                          self_type,
-                          methods.into_iter().chain(associated_types).collect()))
-    }
-
-    fn expand_struct_def(&self,
-                         cx: &mut ExtCtxt,
-                         struct_def: &'a VariantData,
-                         type_ident: Ident,
-                         generics: &Generics) -> P<ast::Item> {
-        let field_tys: Vec<P<ast::Ty>> = struct_def.fields().iter()
-            .map(|field| field.node.ty.clone())
-            .collect();
-
-        let methods = self.methods.iter().map(|method_def| {
-            let (explicit_self, self_args, nonself_args, tys) =
-                method_def.split_self_nonself_args(
-                    cx, self, type_ident, generics);
-
-            let body = if method_def.is_static() {
-                method_def.expand_static_struct_method_body(
-                    cx,
-                    self,
-                    struct_def,
-                    type_ident,
-                    &self_args[..],
-                    &nonself_args[..])
-            } else {
-                method_def.expand_struct_method_body(cx,
-                                                     self,
-                                                     struct_def,
-                                                     type_ident,
-                                                     &self_args[..],
-                                                     &nonself_args[..])
-            };
-
-            method_def.create_method(cx,
-                                     self,
-                                     type_ident,
-                                     generics,
-                                     abi::Rust,
-                                     explicit_self,
-                                     tys,
-                                     body)
-        }).collect();
-
-        self.create_derived_impl(cx, type_ident, generics, field_tys, methods)
-    }
-
-    fn expand_enum_def(&self,
-                       cx: &mut ExtCtxt,
-                       enum_def: &'a EnumDef,
-                       type_attrs: &[ast::Attribute],
-                       type_ident: Ident,
-                       generics: &Generics) -> P<ast::Item> {
-        let mut field_tys = Vec::new();
-
-        for variant in &enum_def.variants {
-            field_tys.extend(variant.node.data.fields().iter()
-                .map(|field| field.node.ty.clone()));
-        }
-
-        let methods = self.methods.iter().map(|method_def| {
-            let (explicit_self, self_args, nonself_args, tys) =
-                method_def.split_self_nonself_args(cx, self,
-                                                   type_ident, generics);
-
-            let body = if method_def.is_static() {
-                method_def.expand_static_enum_method_body(
-                    cx,
-                    self,
-                    enum_def,
-                    type_ident,
-                    &self_args[..],
-                    &nonself_args[..])
-            } else {
-                method_def.expand_enum_method_body(cx,
-                                                   self,
-                                                   enum_def,
-                                                   type_attrs,
-                                                   type_ident,
-                                                   self_args,
-                                                   &nonself_args[..])
-            };
-
-            method_def.create_method(cx,
-                                     self,
-                                     type_ident,
-                                     generics,
-                                     abi::Rust,
-                                     explicit_self,
-                                     tys,
-                                     body)
-        }).collect();
-
-        self.create_derived_impl(cx, type_ident, generics, field_tys, methods)
-    }
-}
-
-fn find_repr_type_name(diagnostic: &SpanHandler,
-                       type_attrs: &[ast::Attribute]) -> &'static str {
-    let mut repr_type_name = "i32";
-    for a in type_attrs {
-        for r in &attr::find_repr_attrs(diagnostic, a) {
-            repr_type_name = match *r {
-                attr::ReprAny | attr::ReprPacked | attr::ReprSimd => continue,
-                attr::ReprExtern => "i32",
-
-                attr::ReprInt(_, attr::SignedInt(ast::TyIs)) => "isize",
-                attr::ReprInt(_, attr::SignedInt(ast::TyI8)) => "i8",
-                attr::ReprInt(_, attr::SignedInt(ast::TyI16)) => "i16",
-                attr::ReprInt(_, attr::SignedInt(ast::TyI32)) => "i32",
-                attr::ReprInt(_, attr::SignedInt(ast::TyI64)) => "i64",
-
-                attr::ReprInt(_, attr::UnsignedInt(ast::TyUs)) => "usize",
-                attr::ReprInt(_, attr::UnsignedInt(ast::TyU8)) => "u8",
-                attr::ReprInt(_, attr::UnsignedInt(ast::TyU16)) => "u16",
-                attr::ReprInt(_, attr::UnsignedInt(ast::TyU32)) => "u32",
-                attr::ReprInt(_, attr::UnsignedInt(ast::TyU64)) => "u64",
-            }
-        }
-    }
-    repr_type_name
-}
-
-impl<'a> MethodDef<'a> {
-    fn call_substructure_method(&self,
-                                cx: &mut ExtCtxt,
-                                trait_: &TraitDef,
-                                type_ident: Ident,
-                                self_args: &[P<Expr>],
-                                nonself_args: &[P<Expr>],
-                                fields: &SubstructureFields)
-        -> P<Expr> {
-        let substructure = Substructure {
-            type_ident: type_ident,
-            method_ident: cx.ident_of(self.name),
-            self_args: self_args,
-            nonself_args: nonself_args,
-            fields: fields
-        };
-        let mut f = self.combine_substructure.borrow_mut();
-        let f: &mut CombineSubstructureFunc = &mut *f;
-        f(cx, trait_.span, &substructure)
-    }
-
-    fn get_ret_ty(&self,
-                  cx: &mut ExtCtxt,
-                  trait_: &TraitDef,
-                  generics: &Generics,
-                  type_ident: Ident)
-                  -> P<ast::Ty> {
-        self.ret_ty.to_ty(cx, trait_.span, type_ident, generics)
-    }
-
-    fn is_static(&self) -> bool {
-        self.explicit_self.is_none()
-    }
-
-    fn split_self_nonself_args(&self,
-                               cx: &mut ExtCtxt,
-                               trait_: &TraitDef,
-                               type_ident: Ident,
-                               generics: &Generics)
-        -> (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);
-
-                self_args.push(self_expr);
-                nonstatic = true;
-
-                explicit_self
-            }
-            None => codemap::respan(trait_.span, ast::SelfStatic),
-        };
-
-        for (i, ty) in self.args.iter().enumerate() {
-            let ast_ty = ty.to_ty(cx, trait_.span, type_ident, generics);
-            let ident = cx.ident_of(&format!("__arg_{}", i));
-            arg_tys.push((ident, ast_ty));
-
-            let arg_expr = cx.expr_ident(trait_.span, ident);
-
-            match *ty {
-                // for static methods, just treat any Self
-                // arguments as a normal arg
-                Self_ if nonstatic  => {
-                    self_args.push(arg_expr);
-                }
-                Ptr(ref ty, _) if **ty == Self_ && nonstatic => {
-                    self_args.push(cx.expr_deref(trait_.span, arg_expr))
-                }
-                _ => {
-                    nonself_args.push(arg_expr);
-                }
-            }
-        }
-
-        (ast_explicit_self, self_args, nonself_args, arg_tys)
-    }
-
-    fn create_method(&self,
-                     cx: &mut ExtCtxt,
-                     trait_: &TraitDef,
-                     type_ident: Ident,
-                     generics: &Generics,
-                     abi: Abi,
-                     explicit_self: ast::ExplicitSelf,
-                     arg_types: Vec<(Ident, P<ast::Ty>)> ,
-                     body: P<Expr>) -> P<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::SelfStatic => None,
-            // creating fresh self id
-            _ => Some(ast::Arg::new_self(trait_.span, ast::MutImmutable, special_idents::self_))
-        };
-        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 ret_type = self.get_ret_ty(cx, trait_, generics, type_ident);
-
-        let method_ident = cx.ident_of(self.name);
-        let fn_decl = cx.fn_decl(args, ret_type);
-        let body_block = cx.block_expr(body);
-
-        let unsafety = if self.is_unsafe {
-            ast::Unsafety::Unsafe
-        } else {
-            ast::Unsafety::Normal
-        };
-
-        // Create the method.
-        P(ast::ImplItem {
-            id: ast::DUMMY_NODE_ID,
-            attrs: self.attributes.clone(),
-            span: trait_.span,
-            vis: ast::Inherited,
-            ident: method_ident,
-            node: ast::ImplItemKind::Method(ast::MethodSig {
-                generics: fn_generics,
-                abi: abi,
-                explicit_self: explicit_self,
-                unsafety: unsafety,
-                constness: ast::Constness::NotConst,
-                decl: fn_decl
-            }, body_block)
-        })
-    }
-
-    /// ```ignore
-    /// #[derive(PartialEq)]
-    /// struct A { x: i32, y: i32 }
-    ///
-    /// // equivalent to:
-    /// impl PartialEq for A {
-    ///     fn eq(&self, __arg_1: &A) -> bool {
-    ///         match *self {
-    ///             A {x: ref __self_0_0, y: ref __self_0_1} => {
-    ///                 match *__arg_1 {
-    ///                     A {x: ref __self_1_0, y: ref __self_1_1} => {
-    ///                         __self_0_0.eq(__self_1_0) && __self_0_1.eq(__self_1_1)
-    ///                     }
-    ///                 }
-    ///             }
-    ///         }
-    ///     }
-    /// }
-    /// ```
-    fn expand_struct_method_body<'b>(&self,
-                                 cx: &mut ExtCtxt,
-                                 trait_: &TraitDef<'b>,
-                                 struct_def: &'b VariantData,
-                                 type_ident: Ident,
-                                 self_args: &[P<Expr>],
-                                 nonself_args: &[P<Expr>])
-        -> P<Expr> {
-
-        let mut raw_fields = Vec::new(); // Vec<[fields of self],
-                                 // [fields of next Self arg], [etc]>
-        let mut patterns = Vec::new();
-        for i in 0..self_args.len() {
-            let struct_path= cx.path(DUMMY_SP, vec!( type_ident ));
-            let (pat, ident_expr) =
-                trait_.create_struct_pattern(cx,
-                                             struct_path,
-                                             struct_def,
-                                             &format!("__self_{}",
-                                                     i),
-                                             ast::MutImmutable);
-            patterns.push(pat);
-            raw_fields.push(ident_expr);
-        }
-
-        // transpose raw_fields
-        let fields = if !raw_fields.is_empty() {
-            let mut raw_fields = raw_fields.into_iter().map(|v| v.into_iter());
-            let first_field = raw_fields.next().unwrap();
-            let mut other_fields: Vec<vec::IntoIter<_>>
-                = raw_fields.collect();
-            first_field.map(|(span, opt_id, field, attrs)| {
-                FieldInfo {
-                    span: span,
-                    name: opt_id,
-                    self_: field,
-                    other: other_fields.iter_mut().map(|l| {
-                        match l.next().unwrap() {
-                            (_, _, ex, _) => ex
-                        }
-                    }).collect(),
-                    attrs: attrs,
-                }
-            }).collect()
-        } else {
-            cx.span_bug(trait_.span,
-                        "no self arguments to non-static method in generic \
-                         `derive`")
-        };
-
-        // body of the inner most destructuring match
-        let mut body = self.call_substructure_method(
-            cx,
-            trait_,
-            type_ident,
-            self_args,
-            nonself_args,
-            &Struct(fields));
-
-        // make a series of nested matches, to destructure the
-        // structs. This is actually right-to-left, but it shouldn't
-        // matter.
-        for (arg_expr, pat) in self_args.iter().zip(patterns) {
-            body = cx.expr_match(trait_.span, arg_expr.clone(),
-                                     vec!( cx.arm(trait_.span, vec!(pat.clone()), body) ))
-        }
-        body
-    }
-
-    fn expand_static_struct_method_body(&self,
-                                        cx: &mut ExtCtxt,
-                                        trait_: &TraitDef,
-                                        struct_def: &VariantData,
-                                        type_ident: Ident,
-                                        self_args: &[P<Expr>],
-                                        nonself_args: &[P<Expr>])
-        -> P<Expr> {
-        let summary = trait_.summarise_struct(cx, struct_def);
-
-        self.call_substructure_method(cx,
-                                      trait_,
-                                      type_ident,
-                                      self_args, nonself_args,
-                                      &StaticStruct(struct_def, summary))
-    }
-
-    /// ```ignore
-    /// #[derive(PartialEq)]
-    /// enum A {
-    ///     A1,
-    ///     A2(i32)
-    /// }
-    ///
-    /// // is equivalent to
-    ///
-    /// impl PartialEq for A {
-    ///     fn eq(&self, __arg_1: &A) -> ::bool {
-    ///         match (&*self, &*__arg_1) {
-    ///             (&A1, &A1) => true,
-    ///             (&A2(ref __self_0),
-    ///              &A2(ref __arg_1_0)) => (*__self_0).eq(&(*__arg_1_0)),
-    ///             _ => {
-    ///                 let __self_vi = match *self { A1(..) => 0, A2(..) => 1 };
-    ///                 let __arg_1_vi = match *__arg_1 { A1(..) => 0, A2(..) => 1 };
-    ///                 false
-    ///             }
-    ///         }
-    ///     }
-    /// }
-    /// ```
-    ///
-    /// (Of course `__self_vi` and `__arg_1_vi` are unused for
-    /// `PartialEq`, and those subcomputations will hopefully be removed
-    /// as their results are unused.  The point of `__self_vi` and
-    /// `__arg_1_vi` is for `PartialOrd`; see #15503.)
-    fn expand_enum_method_body<'b>(&self,
-                               cx: &mut ExtCtxt,
-                               trait_: &TraitDef<'b>,
-                               enum_def: &'b EnumDef,
-                               type_attrs: &[ast::Attribute],
-                               type_ident: Ident,
-                               self_args: Vec<P<Expr>>,
-                               nonself_args: &[P<Expr>])
-                               -> P<Expr> {
-        self.build_enum_match_tuple(
-            cx, trait_, enum_def, type_attrs, type_ident, self_args, nonself_args)
-    }
-
-
-    /// Creates a match for a tuple of all `self_args`, where either all
-    /// variants match, or it falls into a catch-all for when one variant
-    /// does not match.
-
-    /// There are N + 1 cases because is a case for each of the N
-    /// variants where all of the variants match, and one catch-all for
-    /// when one does not match.
-
-    /// As an optimization we generate code which checks whether all variants
-    /// match first which makes llvm see that C-like enums can be compiled into
-    /// a simple equality check (for PartialEq).
-
-    /// The catch-all handler is provided access the variant index values
-    /// for each of the self-args, carried in precomputed variables.
-
-    /// ```{.text}
-    /// let __self0_vi = unsafe {
-    ///     std::intrinsics::discriminant_value(&self) } as i32;
-    /// let __self1_vi = unsafe {
-    ///     std::intrinsics::discriminant_value(&__arg1) } as i32;
-    /// let __self2_vi = unsafe {
-    ///     std::intrinsics::discriminant_value(&__arg2) } as i32;
-    ///
-    /// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... {
-    ///     match (...) {
-    ///         (Variant1, Variant1, ...) => Body1
-    ///         (Variant2, Variant2, ...) => Body2,
-    ///         ...
-    ///         _ => ::core::intrinsics::unreachable()
-    ///     }
-    /// }
-    /// else {
-    ///     ... // catch-all remainder can inspect above variant index values.
-    /// }
-    /// ```
-    fn build_enum_match_tuple<'b>(
-        &self,
-        cx: &mut ExtCtxt,
-        trait_: &TraitDef<'b>,
-        enum_def: &'b EnumDef,
-        type_attrs: &[ast::Attribute],
-        type_ident: Ident,
-        self_args: Vec<P<Expr>>,
-        nonself_args: &[P<Expr>]) -> P<Expr> {
-
-        let sp = trait_.span;
-        let variants = &enum_def.variants;
-
-        let self_arg_names = self_args.iter().enumerate()
-            .map(|(arg_count, _self_arg)| {
-                if arg_count == 0 {
-                    "__self".to_string()
-                } else {
-                    format!("__arg_{}", arg_count)
-                }
-            })
-            .collect::<Vec<String>>();
-
-        let self_arg_idents = self_arg_names.iter()
-            .map(|name|cx.ident_of(&name[..]))
-            .collect::<Vec<ast::Ident>>();
-
-        // The `vi_idents` will be bound, solely in the catch-all, to
-        // a series of let statements mapping each self_arg to an int
-        // value corresponding to its discriminant.
-        let vi_idents: Vec<ast::Ident> = self_arg_names.iter()
-            .map(|name| { let vi_suffix = format!("{}_vi", &name[..]);
-                          cx.ident_of(&vi_suffix[..]) })
-            .collect::<Vec<ast::Ident>>();
-
-        // Builds, via callback to call_substructure_method, the
-        // delegated expression that handles the catch-all case,
-        // using `__variants_tuple` to drive logic if necessary.
-        let catch_all_substructure = EnumNonMatchingCollapsed(
-            self_arg_idents, &variants[..], &vi_idents[..]);
-
-        // These arms are of the form:
-        // (Variant1, Variant1, ...) => Body1
-        // (Variant2, Variant2, ...) => Body2
-        // ...
-        // where each tuple has length = self_args.len()
-        let mut match_arms: Vec<ast::Arm> = variants.iter().enumerate()
-            .map(|(index, variant)| {
-                let mk_self_pat = |cx: &mut ExtCtxt, self_arg_name: &str| {
-                    let (p, idents) = trait_.create_enum_variant_pattern(cx, type_ident,
-                                                                         &**variant,
-                                                                         self_arg_name,
-                                                                         ast::MutImmutable);
-                    (cx.pat(sp, ast::PatRegion(p, ast::MutImmutable)), idents)
-                };
-
-                // A single arm has form (&VariantK, &VariantK, ...) => BodyK
-                // (see "Final wrinkle" note below for why.)
-                let mut subpats = Vec::with_capacity(self_arg_names.len());
-                let mut self_pats_idents = Vec::with_capacity(self_arg_names.len() - 1);
-                let first_self_pat_idents = {
-                    let (p, idents) = mk_self_pat(cx, &self_arg_names[0]);
-                    subpats.push(p);
-                    idents
-                };
-                for self_arg_name in &self_arg_names[1..] {
-                    let (p, idents) = mk_self_pat(cx, &self_arg_name[..]);
-                    subpats.push(p);
-                    self_pats_idents.push(idents);
-                }
-
-                // Here is the pat = `(&VariantK, &VariantK, ...)`
-                let single_pat = cx.pat_tuple(sp, subpats);
-
-                // For the BodyK, we need to delegate to our caller,
-                // passing it an EnumMatching to indicate which case
-                // we are in.
-
-                // All of the Self args have the same variant in these
-                // cases.  So we transpose the info in self_pats_idents
-                // to gather the getter expressions together, in the
-                // form that EnumMatching expects.
-
-                // The transposition is driven by walking across the
-                // arg fields of the variant for the first self pat.
-                let field_tuples = first_self_pat_idents.into_iter().enumerate()
-                    // For each arg field of self, pull out its getter expr ...
-                    .map(|(field_index, (sp, opt_ident, self_getter_expr, attrs))| {
-                        // ... but FieldInfo also wants getter expr
-                        // for matching other arguments of Self type;
-                        // so walk across the *other* self_pats_idents
-                        // and pull out getter for same field in each
-                        // of them (using `field_index` tracked above).
-                        // That is the heart of the transposition.
-                        let others = self_pats_idents.iter().map(|fields| {
-                            let (_, _opt_ident, ref other_getter_expr, _) =
-                                fields[field_index];
-
-                            // All Self args have same variant, so
-                            // opt_idents are the same.  (Assert
-                            // here to make it self-evident that
-                            // it is okay to ignore `_opt_ident`.)
-                            assert!(opt_ident == _opt_ident);
-
-                            other_getter_expr.clone()
-                        }).collect::<Vec<P<Expr>>>();
-
-                        FieldInfo { span: sp,
-                                    name: opt_ident,
-                                    self_: self_getter_expr,
-                                    other: others,
-                                    attrs: attrs,
-                        }
-                    }).collect::<Vec<FieldInfo>>();
-
-                // Now, for some given VariantK, we have built up
-                // expressions for referencing every field of every
-                // Self arg, assuming all are instances of VariantK.
-                // Build up code associated with such a case.
-                let substructure = EnumMatching(index,
-                                                &**variant,
-                                                field_tuples);
-                let arm_expr = self.call_substructure_method(
-                    cx, trait_, type_ident, &self_args[..], nonself_args,
-                    &substructure);
-
-                cx.arm(sp, vec![single_pat], arm_expr)
-            }).collect();
-        // We will usually need the catch-all after matching the
-        // tuples `(VariantK, VariantK, ...)` for each VariantK of the
-        // enum.  But:
-        //
-        // * when there is only one Self arg, the arms above suffice
-        // (and the deriving we call back into may not be prepared to
-        // handle EnumNonMatchCollapsed), and,
-        //
-        // * when the enum has only one variant, the single arm that
-        // is already present always suffices.
-        //
-        // * In either of the two cases above, if we *did* add a
-        //   catch-all `_` match, it would trigger the
-        //   unreachable-pattern error.
-        //
-        if variants.len() > 1 && self_args.len() > 1 {
-            // Build a series of let statements mapping each self_arg
-            // to its discriminant value. If this is a C-style enum
-            // with a specific repr type, then casts the values to
-            // that type.  Otherwise casts to `i32` (the default repr
-            // type).
-            //
-            // i.e. for `enum E<T> { A, B(1), C(T, T) }`, and a deriving
-            // with three Self args, builds three statements:
-            //
-            // ```
-            // let __self0_vi = unsafe {
-            //     std::intrinsics::discriminant_value(&self) } as i32;
-            // let __self1_vi = unsafe {
-            //     std::intrinsics::discriminant_value(&__arg1) } as i32;
-            // let __self2_vi = unsafe {
-            //     std::intrinsics::discriminant_value(&__arg2) } as i32;
-            // ```
-            let mut index_let_stmts: Vec<P<ast::Stmt>> = Vec::new();
-
-            //We also build an expression which checks whether all discriminants are equal
-            // discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ...
-            let mut discriminant_test = cx.expr_bool(sp, true);
-
-            let target_type_name =
-                find_repr_type_name(&cx.parse_sess.span_diagnostic, type_attrs);
-
-            let mut first_ident = None;
-            for (&ident, self_arg) in vi_idents.iter().zip(&self_args) {
-                let path = cx.std_path(&["intrinsics", "discriminant_value"]);
-                let call = cx.expr_call_global(
-                    sp, path, vec![cx.expr_addr_of(sp, self_arg.clone())]);
-                let variant_value = cx.expr_block(P(ast::Block {
-                    stmts: vec![],
-                    expr: Some(call),
-                    id: ast::DUMMY_NODE_ID,
-                    rules: ast::UnsafeBlock(ast::CompilerGenerated),
-                    span: sp }));
-
-                let target_ty = cx.ty_ident(sp, cx.ident_of(target_type_name));
-                let variant_disr = cx.expr_cast(sp, variant_value, target_ty);
-                let let_stmt = cx.stmt_let(sp, false, ident, variant_disr);
-                index_let_stmts.push(let_stmt);
-
-                match first_ident {
-                    Some(first) => {
-                        let first_expr = cx.expr_ident(sp, first);
-                        let id = cx.expr_ident(sp, ident);
-                        let test = cx.expr_binary(sp, ast::BiEq, first_expr, id);
-                        discriminant_test = cx.expr_binary(sp, ast::BiAnd, discriminant_test, test)
-                    }
-                    None => {
-                        first_ident = Some(ident);
-                    }
-                }
-            }
-
-            let arm_expr = self.call_substructure_method(
-                cx, trait_, type_ident, &self_args[..], nonself_args,
-                &catch_all_substructure);
-
-            //Since we know that all the arguments will match if we reach the match expression we
-            //add the unreachable intrinsics as the result of the catch all which should help llvm
-            //in optimizing it
-            let path = cx.std_path(&["intrinsics", "unreachable"]);
-            let call = cx.expr_call_global(
-                sp, path, vec![]);
-            let unreachable = cx.expr_block(P(ast::Block {
-                stmts: vec![],
-                expr: Some(call),
-                id: ast::DUMMY_NODE_ID,
-                rules: ast::UnsafeBlock(ast::CompilerGenerated),
-                span: sp }));
-            match_arms.push(cx.arm(sp, vec![cx.pat_wild(sp)], unreachable));
-
-            // Final wrinkle: the self_args are expressions that deref
-            // down to desired l-values, but we cannot actually deref
-            // them when they are fed as r-values into a tuple
-            // expression; here add a layer of borrowing, turning
-            // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
-            let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg));
-            let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
-
-            //Lastly we create an expression which branches on all discriminants being equal
-            //  if discriminant_test {
-            //      match (...) {
-            //          (Variant1, Variant1, ...) => Body1
-            //          (Variant2, Variant2, ...) => Body2,
-            //          ...
-            //          _ => ::core::intrinsics::unreachable()
-            //      }
-            //  }
-            //  else {
-            //      <delegated expression referring to __self0_vi, et al.>
-            //  }
-            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)))
-        } else if variants.is_empty() {
-            // As an additional wrinkle, For a zero-variant enum A,
-            // currently the compiler
-            // will accept `fn (a: &Self) { match   *a   { } }`
-            // but rejects `fn (a: &Self) { match (&*a,) { } }`
-            // as well as  `fn (a: &Self) { match ( *a,) { } }`
-            //
-            // This means that the strategy of building up a tuple of
-            // all Self arguments fails when Self is a zero variant
-            // enum: rustc rejects the expanded program, even though
-            // the actual code tends to be impossible to execute (at
-            // least safely), according to the type system.
-            //
-            // The most expedient fix for this is to just let the
-            // code fall through to the catch-all.  But even this is
-            // error-prone, since the catch-all as defined above would
-            // generate code like this:
-            //
-            //     _ => { let __self0 = match *self { };
-            //            let __self1 = match *__arg_0 { };
-            //            <catch-all-expr> }
-            //
-            // Which is yields bindings for variables which type
-            // inference cannot resolve to unique types.
-            //
-            // One option to the above might be to add explicit type
-            // annotations.  But the *only* reason to go down that path
-            // would be to try to make the expanded output consistent
-            // with the case when the number of enum variants >= 1.
-            //
-            // That just isn't worth it.  In fact, trying to generate
-            // sensible code for *any* deriving on a zero-variant enum
-            // does not make sense.  But at the same time, for now, we
-            // do not want to cause a compile failure just because the
-            // user happened to attach a deriving to their
-            // zero-variant enum.
-            //
-            // Instead, just generate a failing expression for the
-            // zero variant case, skipping matches and also skipping
-            // delegating back to the end user code entirely.
-            //
-            // (See also #4499 and #12609; note that some of the
-            // discussions there influence what choice we make here;
-            // e.g. if we feature-gate `match x { ... }` when x refers
-            // to an uninhabited type (e.g. a zero-variant enum or a
-            // type holding such an enum), but do not feature-gate
-            // zero-variant enums themselves, then attempting to
-            // derive Debug on such a type could here generate code
-            // that needs the feature gate enabled.)
-
-            cx.expr_unreachable(sp)
-        }
-        else {
-
-            // Final wrinkle: the self_args are expressions that deref
-            // down to desired l-values, but we cannot actually deref
-            // them when they are fed as r-values into a tuple
-            // expression; here add a layer of borrowing, turning
-            // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
-            let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg));
-            let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
-            cx.expr_match(sp, match_arg, match_arms)
-        }
-    }
-
-    fn expand_static_enum_method_body(&self,
-                                      cx: &mut ExtCtxt,
-                                      trait_: &TraitDef,
-                                      enum_def: &EnumDef,
-                                      type_ident: Ident,
-                                      self_args: &[P<Expr>],
-                                      nonself_args: &[P<Expr>])
-        -> P<Expr> {
-        let summary = enum_def.variants.iter().map(|v| {
-            let ident = v.node.name;
-            let summary = trait_.summarise_struct(cx, &v.node.data);
-            (ident, v.span, summary)
-        }).collect();
-        self.call_substructure_method(cx, trait_, type_ident,
-                                      self_args, nonself_args,
-                                      &StaticEnum(enum_def, summary))
-    }
-}
-
-#[derive(PartialEq)] // dogfooding!
-enum StructType {
-    Unknown, Record, Tuple
-}
-
-// 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);
-            match field.node.kind {
-                ast::NamedField(ident, _) => named_idents.push((ident, sp)),
-                ast::UnnamedField(..) => just_spans.push(sp),
-            }
-        }
-
-        match (just_spans.is_empty(), named_idents.is_empty()) {
-            (false, false) => cx.span_bug(self.span,
-                                          "a struct with named and unnamed \
-                                          fields in generic `derive`"),
-            // named fields
-            (_, false) => Named(named_idents),
-            // tuple structs (includes empty structs)
-            (_, _)     => Unnamed(just_spans)
-        }
-    }
-
-    fn create_subpatterns(&self,
-                          cx: &mut ExtCtxt,
-                          field_paths: Vec<ast::SpannedIdent> ,
-                          mutbl: ast::Mutability)
-                          -> Vec<P<ast::Pat>> {
-        field_paths.iter().map(|path| {
-            cx.pat(path.span,
-                        ast::PatIdent(ast::BindByRef(mutbl), (*path).clone(), None))
-        }).collect()
-    }
-
-    fn create_struct_pattern(&self,
-                             cx: &mut ExtCtxt,
-                             struct_path: ast::Path,
-                             struct_def: &'a VariantData,
-                             prefix: &str,
-                             mutbl: ast::Mutability)
-                             -> (P<ast::Pat>, Vec<(Span, Option<Ident>,
-                                                   P<Expr>,
-                                                   &'a [ast::Attribute])>) {
-        if struct_def.fields().is_empty() {
-            return (cx.pat_enum(self.span, struct_path, vec![]), vec![]);
-        }
-
-        let mut paths = Vec::new();
-        let mut ident_expr = Vec::new();
-        let mut struct_type = Unknown;
-
-        for (i, struct_field) in struct_def.fields().iter().enumerate() {
-            let sp = self.set_expn_info(cx, struct_field.span);
-            let opt_id = match struct_field.node.kind {
-                ast::NamedField(ident, _) if (struct_type == Unknown ||
-                                              struct_type == Record) => {
-                    struct_type = Record;
-                    Some(ident)
-                }
-                ast::UnnamedField(..) if (struct_type == Unknown ||
-                                          struct_type == Tuple) => {
-                    struct_type = Tuple;
-                    None
-                }
-                _ => {
-                    cx.span_bug(sp, "a struct with named and unnamed fields in `derive`");
-                }
-            };
-            let ident = cx.ident_of(&format!("{}_{}", prefix, i));
-            paths.push(codemap::Spanned{span: sp, node: ident});
-            let val = cx.expr(
-                sp, ast::ExprParen(cx.expr_deref(sp, cx.expr_path(cx.path_ident(sp,ident)))));
-            ident_expr.push((sp, opt_id, val, &struct_field.node.attrs[..]));
-        }
-
-        let subpats = self.create_subpatterns(cx, paths, mutbl);
-
-        // struct_type is definitely not Unknown, since struct_def.fields
-        // must be nonempty to reach here
-        let pattern = if struct_type == Record {
-            let field_pats = subpats.into_iter().zip(&ident_expr)
-                                    .map(|(pat, &(_, id, _, _))| {
-                // id is guaranteed to be Some
-                codemap::Spanned {
-                    span: pat.span,
-                    node: ast::FieldPat { ident: id.unwrap(), pat: pat, is_shorthand: false },
-                }
-            }).collect();
-            cx.pat_struct(self.span, struct_path, field_pats)
-        } else {
-            cx.pat_enum(self.span, struct_path, subpats)
-        };
-
-        (pattern, ident_expr)
-    }
-
-    fn create_enum_variant_pattern(&self,
-                                   cx: &mut ExtCtxt,
-                                   enum_ident: ast::Ident,
-                                   variant: &'a ast::Variant,
-                                   prefix: &str,
-                                   mutbl: ast::Mutability)
-        -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) {
-        let variant_ident = variant.node.name;
-        let variant_path = cx.path(variant.span, vec![enum_ident, variant_ident]);
-        self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl)
-    }
-}
-
-/* helpful premade recipes */
-
-/// Fold the fields. `use_foldl` controls whether this is done
-/// left-to-right (`true`) or right-to-left (`false`).
-pub fn cs_fold<F>(use_foldl: bool,
-                  mut f: F,
-                  base: P<Expr>,
-                  mut enum_nonmatch_f: EnumNonMatchCollapsedFunc,
-                  cx: &mut ExtCtxt,
-                  trait_span: Span,
-                  substructure: &Substructure)
-                  -> P<Expr> where
-    F: FnMut(&mut ExtCtxt, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>,
-{
-    match *substructure.fields {
-        EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
-            if use_foldl {
-                all_fields.iter().fold(base, |old, field| {
-                    f(cx,
-                      field.span,
-                      old,
-                      field.self_.clone(),
-                      &field.other)
-                })
-            } else {
-                all_fields.iter().rev().fold(base, |old, field| {
-                    f(cx,
-                      field.span,
-                      old,
-                      field.self_.clone(),
-                      &field.other)
-                })
-            }
-        },
-        EnumNonMatchingCollapsed(ref all_args, _, tuple) =>
-            enum_nonmatch_f(cx, trait_span, (&all_args[..], tuple),
-                            substructure.nonself_args),
-        StaticEnum(..) | StaticStruct(..) => {
-            cx.span_bug(trait_span, "static function in `derive`")
-        }
-    }
-}
-
-
-/// Call the method that is being derived on all the fields, and then
-/// process the collected results. i.e.
-///
-/// ```ignore
-/// f(cx, span, vec![self_1.method(__arg_1_1, __arg_2_1),
-///                  self_2.method(__arg_1_2, __arg_2_2)])
-/// ```
-#[inline]
-pub fn cs_same_method<F>(f: F,
-                         mut enum_nonmatch_f: EnumNonMatchCollapsedFunc,
-                         cx: &mut ExtCtxt,
-                         trait_span: Span,
-                         substructure: &Substructure)
-                         -> P<Expr> where
-    F: FnOnce(&mut ExtCtxt, Span, Vec<P<Expr>>) -> P<Expr>,
-{
-    match *substructure.fields {
-        EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
-            // call self_n.method(other_1_n, other_2_n, ...)
-            let called = all_fields.iter().map(|field| {
-                cx.expr_method_call(field.span,
-                                    field.self_.clone(),
-                                    substructure.method_ident,
-                                    field.other.iter()
-                                               .map(|e| cx.expr_addr_of(field.span, e.clone()))
-                                               .collect())
-            }).collect();
-
-            f(cx, trait_span, called)
-        },
-        EnumNonMatchingCollapsed(ref all_self_args, _, tuple) =>
-            enum_nonmatch_f(cx, trait_span, (&all_self_args[..], tuple),
-                            substructure.nonself_args),
-        StaticEnum(..) | StaticStruct(..) => {
-            cx.span_bug(trait_span, "static function in `derive`")
-        }
-    }
-}
-
-/// Fold together the results of calling the derived method on all the
-/// fields. `use_foldl` controls whether this is done left-to-right
-/// (`true`) or right-to-left (`false`).
-#[inline]
-pub fn cs_same_method_fold<F>(use_foldl: bool,
-                              mut f: F,
-                              base: P<Expr>,
-                              enum_nonmatch_f: EnumNonMatchCollapsedFunc,
-                              cx: &mut ExtCtxt,
-                              trait_span: Span,
-                              substructure: &Substructure)
-                              -> P<Expr> where
-    F: FnMut(&mut ExtCtxt, Span, P<Expr>, P<Expr>) -> P<Expr>,
-{
-    cs_same_method(
-        |cx, span, vals| {
-            if use_foldl {
-                vals.into_iter().fold(base.clone(), |old, new| {
-                    f(cx, span, old, new)
-                })
-            } else {
-                vals.into_iter().rev().fold(base.clone(), |old, new| {
-                    f(cx, span, old, new)
-                })
-            }
-        },
-        enum_nonmatch_f,
-        cx, trait_span, substructure)
-}
-
-/// Use a given binop to combine the result of calling the derived method
-/// on all the fields.
-#[inline]
-pub fn cs_binop(binop: ast::BinOp_, base: P<Expr>,
-                enum_nonmatch_f: EnumNonMatchCollapsedFunc,
-                cx: &mut ExtCtxt, trait_span: Span,
-                substructure: &Substructure) -> P<Expr> {
-    cs_same_method_fold(
-        true, // foldl is good enough
-        |cx, span, old, new| {
-            cx.expr_binary(span,
-                           binop,
-                           old, new)
-
-        },
-        base,
-        enum_nonmatch_f,
-        cx, trait_span, substructure)
-}
-
-/// cs_binop with binop == or
-#[inline]
-pub fn cs_or(enum_nonmatch_f: EnumNonMatchCollapsedFunc,
-             cx: &mut ExtCtxt, span: Span,
-             substructure: &Substructure) -> P<Expr> {
-    cs_binop(ast::BiOr, cx.expr_bool(span, false),
-             enum_nonmatch_f,
-             cx, span, substructure)
-}
-
-/// cs_binop with binop == and
-#[inline]
-pub fn cs_and(enum_nonmatch_f: EnumNonMatchCollapsedFunc,
-              cx: &mut ExtCtxt, span: Span,
-              substructure: &Substructure) -> P<Expr> {
-    cs_binop(ast::BiAnd, cx.expr_bool(span, true),
-             enum_nonmatch_f,
-             cx, span, substructure)
-}
diff --git a/src/libsyntax/ext/deriving/generic/ty.rs b/src/libsyntax/ext/deriving/generic/ty.rs
deleted file mode 100644 (file)
index 67826c9..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-// 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.
-
-//! A mini version of ast::Ty, which is easier to use, and features an explicit `Self` type to use
-//! when specifying impls to be derived.
-
-pub use self::PtrTy::*;
-pub use self::Ty::*;
-
-use ast;
-use ast::{Expr,Generics,Ident};
-use ext::base::ExtCtxt;
-use ext::build::AstBuilder;
-use codemap::{Span,respan};
-use owned_slice::OwnedSlice;
-use parse::token::special_idents;
-use ptr::P;
-
-/// The types of pointers
-#[derive(Clone, Eq, PartialEq)]
-pub enum PtrTy<'a> {
-    /// &'lifetime mut
-    Borrowed(Option<&'a str>, ast::Mutability),
-    /// *mut
-    Raw(ast::Mutability),
-}
-
-/// A path, e.g. `::std::option::Option::<i32>` (global). Has support
-/// for type parameters and a lifetime.
-#[derive(Clone, Eq, PartialEq)]
-pub struct Path<'a> {
-    pub path: Vec<&'a str> ,
-    pub lifetime: Option<&'a str>,
-    pub params: Vec<Box<Ty<'a>>>,
-    pub global: bool,
-}
-
-impl<'a> Path<'a> {
-    pub fn new<'r>(path: Vec<&'r str> ) -> Path<'r> {
-        Path::new_(path, None, Vec::new(), true)
-    }
-    pub fn new_local<'r>(path: &'r str) -> Path<'r> {
-        Path::new_(vec!( path ), None, Vec::new(), false)
-    }
-    pub fn new_<'r>(path: Vec<&'r str> ,
-                    lifetime: Option<&'r str>,
-                    params: Vec<Box<Ty<'r>>>,
-                    global: bool)
-                    -> Path<'r> {
-        Path {
-            path: path,
-            lifetime: lifetime,
-            params: params,
-            global: global
-        }
-    }
-
-    pub fn to_ty(&self,
-                 cx: &ExtCtxt,
-                 span: Span,
-                 self_ty: Ident,
-                 self_generics: &Generics)
-                 -> P<ast::Ty> {
-        cx.ty_path(self.to_path(cx, span, self_ty, self_generics))
-    }
-    pub fn to_path(&self,
-                   cx: &ExtCtxt,
-                   span: Span,
-                   self_ty: Ident,
-                   self_generics: &Generics)
-                   -> ast::Path {
-        let idents = self.path.iter().map(|s| cx.ident_of(*s)).collect();
-        let lt = mk_lifetimes(cx, span, &self.lifetime);
-        let tys = self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics)).collect();
-
-        cx.path_all(span, self.global, idents, lt, tys, Vec::new())
-    }
-}
-
-/// A type. Supports pointers, Self, and literals
-#[derive(Clone, Eq, PartialEq)]
-pub enum Ty<'a> {
-    Self_,
-    /// &/Box/ Ty
-    Ptr(Box<Ty<'a>>, PtrTy<'a>),
-    /// mod::mod::Type<[lifetime], [Params...]>, including a plain type
-    /// parameter, and things like `i32`
-    Literal(Path<'a>),
-    /// includes unit
-    Tuple(Vec<Ty<'a>> )
-}
-
-pub fn borrowed_ptrty<'r>() -> PtrTy<'r> {
-    Borrowed(None, ast::MutImmutable)
-}
-pub fn borrowed<'r>(ty: Box<Ty<'r>>) -> Ty<'r> {
-    Ptr(ty, borrowed_ptrty())
-}
-
-pub fn borrowed_explicit_self<'r>() -> Option<Option<PtrTy<'r>>> {
-    Some(Some(borrowed_ptrty()))
-}
-
-pub fn borrowed_self<'r>() -> Ty<'r> {
-    borrowed(Box::new(Self_))
-}
-
-pub fn nil_ty<'r>() -> Ty<'r> {
-    Tuple(Vec::new())
-}
-
-fn mk_lifetime(cx: &ExtCtxt, span: Span, lt: &Option<&str>) -> Option<ast::Lifetime> {
-    match *lt {
-        Some(ref s) => Some(cx.lifetime(span, cx.ident_of(*s).name)),
-        None => None
-    }
-}
-
-fn mk_lifetimes(cx: &ExtCtxt, span: Span, lt: &Option<&str>) -> Vec<ast::Lifetime> {
-    match *lt {
-        Some(ref s) => vec!(cx.lifetime(span, cx.ident_of(*s).name)),
-        None => vec!()
-    }
-}
-
-impl<'a> Ty<'a> {
-    pub fn to_ty(&self,
-                 cx: &ExtCtxt,
-                 span: Span,
-                 self_ty: Ident,
-                 self_generics: &Generics)
-                 -> P<ast::Ty> {
-        match *self {
-            Ptr(ref ty, ref ptr) => {
-                let raw_ty = ty.to_ty(cx, span, self_ty, self_generics);
-                match *ptr {
-                    Borrowed(ref lt, mutbl) => {
-                        let lt = mk_lifetime(cx, span, lt);
-                        cx.ty_rptr(span, raw_ty, lt, mutbl)
-                    }
-                    Raw(mutbl) => cx.ty_ptr(span, raw_ty, mutbl)
-                }
-            }
-            Literal(ref p) => { p.to_ty(cx, span, self_ty, self_generics) }
-            Self_  => {
-                cx.ty_path(self.to_path(cx, span, self_ty, self_generics))
-            }
-            Tuple(ref fields) => {
-                let ty = ast::TyTup(fields.iter()
-                    .map(|f| f.to_ty(cx, span, self_ty, self_generics))
-                    .collect());
-                cx.ty(span, ty)
-            }
-        }
-    }
-
-    pub fn to_path(&self,
-                   cx: &ExtCtxt,
-                   span: Span,
-                   self_ty: Ident,
-                   self_generics: &Generics)
-                   -> ast::Path {
-        match *self {
-            Self_ => {
-                let self_params = self_generics.ty_params.map(|ty_param| {
-                    cx.ty_ident(span, ty_param.ident)
-                });
-                let lifetimes = self_generics.lifetimes.iter()
-                                                       .map(|d| d.lifetime)
-                                                       .collect();
-
-                cx.path_all(span, false, vec!(self_ty), lifetimes,
-                            self_params.into_vec(), Vec::new())
-            }
-            Literal(ref p) => {
-                p.to_path(cx, span, self_ty, self_generics)
-            }
-            Ptr(..) => { cx.span_bug(span, "pointer in a path in generic `derive`") }
-            Tuple(..) => { cx.span_bug(span, "tuple in a path in generic `derive`") }
-        }
-    }
-}
-
-
-fn mk_ty_param(cx: &ExtCtxt,
-               span: Span,
-               name: &str,
-               bounds: &[Path],
-               self_ident: Ident,
-               self_generics: &Generics)
-               -> ast::TyParam {
-    let bounds =
-        bounds.iter().map(|b| {
-            let path = b.to_path(cx, span, self_ident, self_generics);
-            cx.typarambound(path)
-        }).collect();
-    cx.typaram(span, cx.ident_of(name), bounds, None)
-}
-
-fn mk_generics(lifetimes: Vec<ast::LifetimeDef>, ty_params: Vec<ast::TyParam>)
-               -> Generics {
-    Generics {
-        lifetimes: lifetimes,
-        ty_params: OwnedSlice::from_vec(ty_params),
-        where_clause: ast::WhereClause {
-            id: ast::DUMMY_NODE_ID,
-            predicates: Vec::new(),
-        },
-    }
-}
-
-/// Lifetimes and bounds on type parameters
-#[derive(Clone)]
-pub struct LifetimeBounds<'a> {
-    pub lifetimes: Vec<(&'a str, Vec<&'a str>)>,
-    pub bounds: Vec<(&'a str, Vec<Path<'a>>)>,
-}
-
-impl<'a> LifetimeBounds<'a> {
-    pub fn empty() -> LifetimeBounds<'a> {
-        LifetimeBounds {
-            lifetimes: Vec::new(), bounds: Vec::new()
-        }
-    }
-    pub fn to_generics(&self,
-                       cx: &ExtCtxt,
-                       span: Span,
-                       self_ty: Ident,
-                       self_generics: &Generics)
-                       -> Generics {
-        let lifetimes = self.lifetimes.iter().map(|&(ref lt, ref bounds)| {
-            let bounds =
-                bounds.iter().map(
-                    |b| cx.lifetime(span, cx.ident_of(*b).name)).collect();
-            cx.lifetime_def(span, cx.ident_of(*lt).name, bounds)
-        }).collect();
-        let ty_params = self.bounds.iter().map(|t| {
-            match *t {
-                (ref name, ref bounds) => {
-                    mk_ty_param(cx,
-                                span,
-                                *name,
-                                bounds,
-                                self_ty,
-                                self_generics)
-                }
-            }
-        }).collect();
-        mk_generics(lifetimes, ty_params)
-    }
-}
-
-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.
-    let self_path = cx.expr_self(span);
-    match *self_ptr {
-        None => {
-            (self_path, respan(span, ast::SelfValue(special_idents::self_)))
-        }
-        Some(ref ptr) => {
-            let self_ty = respan(
-                span,
-                match *ptr {
-                    Borrowed(ref lt, mutbl) => {
-                        let lt = lt.map(|s| cx.lifetime(span, cx.ident_of(s).name));
-                        ast::SelfRegion(lt, mutbl, special_idents::self_)
-                    }
-                    Raw(_) => cx.span_bug(span, "attempted to use *self in deriving definition")
-                });
-            let self_expr = cx.expr_deref(span, self_path);
-            (self_expr, self_ty)
-        }
-    }
-}
diff --git a/src/libsyntax/ext/deriving/hash.rs b/src/libsyntax/ext/deriving/hash.rs
deleted file mode 100644 (file)
index 96be774..0000000
+++ /dev/null
@@ -1,99 +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 ast::{MetaItem, Expr, MutMutable};
-use codemap::Span;
-use ext::base::{ExtCtxt, Annotatable};
-use ext::build::AstBuilder;
-use ext::deriving::generic::*;
-use ext::deriving::generic::ty::*;
-use ptr::P;
-
-pub fn expand_deriving_hash(cx: &mut ExtCtxt,
-                            span: Span,
-                            mitem: &MetaItem,
-                            item: &Annotatable,
-                            push: &mut FnMut(Annotatable))
-{
-
-    let path = Path::new_(pathvec_std!(cx, core::hash::Hash), None,
-                          vec!(), true);
-    let arg = Path::new_local("__H");
-    let hash_trait_def = TraitDef {
-        span: span,
-        attributes: Vec::new(),
-        path: path,
-        additional_bounds: Vec::new(),
-        generics: LifetimeBounds::empty(),
-        is_unsafe: false,
-        methods: vec!(
-            MethodDef {
-                name: "hash",
-                generics: LifetimeBounds {
-                    lifetimes: Vec::new(),
-                    bounds: vec![("__H",
-                                  vec![path_std!(cx, core::hash::Hasher)])],
-                },
-                explicit_self: borrowed_explicit_self(),
-                args: vec!(Ptr(Box::new(Literal(arg)), Borrowed(None, MutMutable))),
-                ret_ty: nil_ty(),
-                attributes: vec![],
-                is_unsafe: false,
-                combine_substructure: combine_substructure(Box::new(|a, b, c| {
-                    hash_substructure(a, b, c)
-                }))
-            }
-        ),
-        associated_types: Vec::new(),
-    };
-
-    hash_trait_def.expand(cx, mitem, item, push);
-}
-
-fn hash_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
-    let state_expr = match (substr.nonself_args.len(), substr.nonself_args.get(0)) {
-        (1, Some(o_f)) => o_f,
-        _ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`")
-    };
-    let call_hash = |span, thing_expr| {
-        let hash_path = {
-            let strs = cx.std_path(&["hash", "Hash", "hash"]);
-
-            cx.expr_path(cx.path_global(span, strs))
-        };
-        let ref_thing = cx.expr_addr_of(span, thing_expr);
-        let expr = cx.expr_call(span, hash_path, vec!(ref_thing, state_expr.clone()));
-        cx.stmt_expr(expr)
-    };
-    let mut stmts = Vec::new();
-
-    let fields = match *substr.fields {
-        Struct(ref fs) => fs,
-        EnumMatching(index, variant, ref fs) => {
-            // Determine the discriminant. We will feed this value to the byte
-            // iteration function.
-            let discriminant = match variant.node.disr_expr {
-                Some(ref d) => d.clone(),
-                None => cx.expr_usize(trait_span, index)
-            };
-
-            stmts.push(call_hash(trait_span, discriminant));
-
-            fs
-        }
-        _ => cx.span_bug(trait_span, "impossible substructure in `derive(Hash)`")
-    };
-
-    for &FieldInfo { ref self_, span, .. } in fields {
-        stmts.push(call_hash(span, self_.clone()));
-    }
-
-    cx.expr_block(cx.block(trait_span, stmts, None))
-}
diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs
deleted file mode 100644 (file)
index d26bb79..0000000
+++ /dev/null
@@ -1,201 +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.
-
-//! The compiler code necessary to implement the `#[derive]` extensions.
-//!
-//! FIXME (#2810): hygiene. Search for "__" strings (in other files too). We also assume "extra" is
-//! the standard library, and "std" is the core library.
-
-use ast::{MetaItem, MetaWord};
-use attr::AttrMetaMethods;
-use ext::base::{ExtCtxt, SyntaxEnv, MultiDecorator, MultiItemDecorator, MultiModifier, Annotatable};
-use ext::build::AstBuilder;
-use feature_gate;
-use codemap::Span;
-use parse::token::{intern, intern_and_get_ident};
-
-macro_rules! pathvec {
-    ($($x:ident)::+) => (
-        vec![ $( stringify!($x) ),+ ]
-    )
-}
-
-macro_rules! path {
-    ($($x:tt)*) => (
-        ::ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) )
-    )
-}
-
-macro_rules! path_local {
-    ($x:ident) => (
-        ::ext::deriving::generic::ty::Path::new_local(stringify!($x))
-    )
-}
-
-macro_rules! pathvec_std {
-    ($cx:expr, $first:ident :: $($rest:ident)::+) => ({
-        let mut v = pathvec!($($rest)::+);
-        if let Some(s) = $cx.crate_root {
-            v.insert(0, s);
-        }
-        v
-    })
-}
-
-macro_rules! path_std {
-    ($($x:tt)*) => (
-        ::ext::deriving::generic::ty::Path::new( pathvec_std!( $($x)* ) )
-    )
-}
-
-pub mod bounds;
-pub mod clone;
-pub mod encodable;
-pub mod decodable;
-pub mod hash;
-pub mod debug;
-pub mod default;
-pub mod primitive;
-
-#[path="cmp/partial_eq.rs"]
-pub mod partial_eq;
-#[path="cmp/eq.rs"]
-pub mod eq;
-#[path="cmp/partial_ord.rs"]
-pub mod partial_ord;
-#[path="cmp/ord.rs"]
-pub mod ord;
-
-
-pub mod generic;
-
-fn expand_derive(cx: &mut ExtCtxt,
-                 span: Span,
-                 mitem: &MetaItem,
-                 annotatable: Annotatable)
-                 -> Annotatable {
-    annotatable.map_item_or(|item| {
-        item.map(|mut item| {
-            if mitem.value_str().is_some() {
-                cx.span_err(mitem.span, "unexpected value in `derive`");
-            }
-
-            let traits = mitem.meta_item_list().unwrap_or(&[]);
-            if traits.is_empty() {
-                cx.span_warn(mitem.span, "empty trait list in `derive`");
-            }
-
-            for titem in traits.iter().rev() {
-                let tname = match titem.node {
-                    MetaWord(ref tname) => tname,
-                    _ => {
-                        cx.span_err(titem.span, "malformed `derive` entry");
-                        continue;
-                    }
-                };
-
-                if !(is_builtin_trait(tname) || cx.ecfg.enable_custom_derive()) {
-                    feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
-                                                   "custom_derive",
-                                                   titem.span,
-                                                   feature_gate::GateIssue::Language,
-                                                   feature_gate::EXPLAIN_CUSTOM_DERIVE);
-                    continue;
-                }
-
-                // #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar]
-                item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span,
-                    intern_and_get_ident(&format!("derive_{}", tname)))));
-            }
-
-            item
-        })
-    }, |a| {
-        cx.span_err(span, "`derive` can only be applied to items");
-        a
-    })
-}
-
-macro_rules! derive_traits {
-    ($( $name:expr => $func:path, )+) => {
-        pub fn register_all(env: &mut SyntaxEnv) {
-            // Define the #[derive_*] extensions.
-            $({
-                struct DeriveExtension;
-
-                impl MultiItemDecorator for DeriveExtension {
-                    fn expand(&self,
-                              ecx: &mut ExtCtxt,
-                              sp: Span,
-                              mitem: &MetaItem,
-                              annotatable: &Annotatable,
-                              push: &mut FnMut(Annotatable)) {
-                        warn_if_deprecated(ecx, sp, $name);
-                        $func(ecx, sp, mitem, annotatable, push);
-                    }
-                }
-
-                env.insert(intern(concat!("derive_", $name)),
-                           MultiDecorator(Box::new(DeriveExtension)));
-            })+
-
-            env.insert(intern("derive"),
-                       MultiModifier(Box::new(expand_derive)));
-        }
-
-        fn is_builtin_trait(name: &str) -> bool {
-            match name {
-                $( $name )|+ => true,
-                _ => false,
-            }
-        }
-    }
-}
-
-derive_traits! {
-    "Clone" => clone::expand_deriving_clone,
-
-    "Hash" => hash::expand_deriving_hash,
-
-    "RustcEncodable" => encodable::expand_deriving_rustc_encodable,
-
-    "RustcDecodable" => decodable::expand_deriving_rustc_decodable,
-
-    "PartialEq" => partial_eq::expand_deriving_partial_eq,
-    "Eq" => eq::expand_deriving_eq,
-    "PartialOrd" => partial_ord::expand_deriving_partial_ord,
-    "Ord" => ord::expand_deriving_ord,
-
-    "Debug" => debug::expand_deriving_debug,
-
-    "Default" => default::expand_deriving_default,
-
-    "FromPrimitive" => primitive::expand_deriving_from_primitive,
-
-    "Send" => bounds::expand_deriving_unsafe_bound,
-    "Sync" => bounds::expand_deriving_unsafe_bound,
-    "Copy" => bounds::expand_deriving_copy,
-
-    // deprecated
-    "Encodable" => encodable::expand_deriving_encodable,
-    "Decodable" => decodable::expand_deriving_decodable,
-}
-
-#[inline] // because `name` is a compile-time constant
-fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) {
-    if let Some(replacement) = match name {
-        "Encodable" => Some("RustcEncodable"),
-        "Decodable" => Some("RustcDecodable"),
-        _ => None,
-    } {
-        ecx.span_warn(sp, &format!("derive({}) is deprecated in favor of derive({})",
-                                   name, replacement));
-    }
-}
diff --git a/src/libsyntax/ext/deriving/primitive.rs b/src/libsyntax/ext/deriving/primitive.rs
deleted file mode 100644 (file)
index 07b5877..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-// 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.
-
-use ast::{MetaItem, Expr};
-use ast;
-use codemap::Span;
-use ext::base::{ExtCtxt, Annotatable};
-use ext::build::AstBuilder;
-use ext::deriving::generic::*;
-use ext::deriving::generic::ty::*;
-use parse::token::InternedString;
-use ptr::P;
-
-pub fn expand_deriving_from_primitive(cx: &mut ExtCtxt,
-                                      span: Span,
-                                      mitem: &MetaItem,
-                                      item: &Annotatable,
-                                      push: &mut FnMut(Annotatable))
-{
-    let inline = cx.meta_word(span, InternedString::new("inline"));
-    let attrs = vec!(cx.attribute(span, inline));
-    let trait_def = TraitDef {
-        span: span,
-        attributes: Vec::new(),
-        path: path_std!(cx, core::num::FromPrimitive),
-        additional_bounds: Vec::new(),
-        generics: LifetimeBounds::empty(),
-        is_unsafe: false,
-        methods: vec!(
-            MethodDef {
-                name: "from_i64",
-                generics: LifetimeBounds::empty(),
-                explicit_self: None,
-                args: vec!(Literal(path_local!(i64))),
-                ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option),
-                                           None,
-                                           vec!(Box::new(Self_)),
-                                           true)),
-                // #[inline] liable to cause code-bloat
-                attributes: attrs.clone(),
-                is_unsafe: false,
-                combine_substructure: combine_substructure(Box::new(|c, s, sub| {
-                    cs_from("i64", c, s, sub)
-                })),
-            },
-            MethodDef {
-                name: "from_u64",
-                generics: LifetimeBounds::empty(),
-                explicit_self: None,
-                args: vec!(Literal(path_local!(u64))),
-                ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option),
-                                           None,
-                                           vec!(Box::new(Self_)),
-                                           true)),
-                // #[inline] liable to cause code-bloat
-                attributes: attrs,
-                is_unsafe: false,
-                combine_substructure: combine_substructure(Box::new(|c, s, sub| {
-                    cs_from("u64", c, s, sub)
-                })),
-            }
-        ),
-        associated_types: Vec::new(),
-    };
-
-    trait_def.expand(cx, mitem, item, push)
-}
-
-fn cs_from(name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
-    let n = match (substr.nonself_args.len(), substr.nonself_args.get(0)) {
-        (1, Some(o_f)) => o_f,
-        _ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(FromPrimitive)`")
-    };
-
-    match *substr.fields {
-        StaticStruct(..) => {
-            cx.span_err(trait_span, "`FromPrimitive` cannot be derived for structs");
-            return cx.expr_fail(trait_span, InternedString::new(""));
-        }
-        StaticEnum(enum_def, _) => {
-            if enum_def.variants.is_empty() {
-                cx.span_err(trait_span,
-                            "`FromPrimitive` cannot be derived for enums with no variants");
-                return cx.expr_fail(trait_span, InternedString::new(""));
-            }
-
-            let mut arms = Vec::new();
-
-            for variant in &enum_def.variants {
-                let def = &variant.node.data;
-                if !def.is_unit() {
-                    cx.span_err(trait_span, "`FromPrimitive` cannot be derived \
-                                             for enums with non-unit variants");
-                    return cx.expr_fail(trait_span,
-                                        InternedString::new(""));
-                }
-
-                let span = variant.span;
-
-                // expr for `$n == $variant as $name`
-                let path = cx.path(span, vec![substr.type_ident, variant.node.name]);
-                let variant = cx.expr_path(path);
-                let ty = cx.ty_ident(span, cx.ident_of(name));
-                let cast = cx.expr_cast(span, variant.clone(), ty);
-                let guard = cx.expr_binary(span, ast::BiEq, n.clone(), cast);
-
-                // expr for `Some($variant)`
-                let body = cx.expr_some(span, variant);
-
-                // arm for `_ if $guard => $body`
-                let arm = ast::Arm {
-                    attrs: vec!(),
-                    pats: vec!(cx.pat_wild(span)),
-                    guard: Some(guard),
-                    body: body,
-                };
-
-                arms.push(arm);
-            }
-
-            // arm for `_ => None`
-            let arm = ast::Arm {
-                attrs: vec!(),
-                pats: vec!(cx.pat_wild(trait_span)),
-                guard: None,
-                body: cx.expr_none(trait_span),
-            };
-            arms.push(arm);
-
-            cx.expr_match(trait_span, n.clone(), arms)
-        }
-        _ => cx.span_bug(trait_span, "expected StaticEnum in derive(FromPrimitive)")
-    }
-}
diff --git a/src/libsyntax/ext/env.rs b/src/libsyntax/ext/env.rs
deleted file mode 100644 (file)
index d85071e..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-// 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 compiler code necessary to support the env! extension.  Eventually this
- * should all get sucked into either the compiler syntax extension plugin
- * interface.
- */
-
-use ast;
-use codemap::Span;
-use ext::base::*;
-use ext::base;
-use ext::build::AstBuilder;
-use parse::token;
-
-use std::env;
-
-pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-                              -> Box<base::MacResult+'cx> {
-    let var = match get_single_str_from_tts(cx, sp, tts, "option_env!") {
-        None => return DummyResult::expr(sp),
-        Some(v) => v
-    };
-
-    let e = match env::var(&var[..]) {
-      Err(..) => {
-          cx.expr_path(cx.path_all(sp,
-                                   true,
-                                   cx.std_path(&["option", "Option", "None"]),
-                                   Vec::new(),
-                                   vec!(cx.ty_rptr(sp,
-                                                   cx.ty_ident(sp,
-                                                        cx.ident_of("str")),
-                                                   Some(cx.lifetime(sp,
-                                                        cx.ident_of(
-                                                            "'static").name)),
-                                                   ast::MutImmutable)),
-                                   Vec::new()))
-      }
-      Ok(s) => {
-          cx.expr_call_global(sp,
-                              cx.std_path(&["option", "Option", "Some"]),
-                              vec!(cx.expr_str(sp,
-                                               token::intern_and_get_ident(
-                                          &s[..]))))
-      }
-    };
-    MacEager::expr(e)
-}
-
-pub fn expand_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-                       -> Box<base::MacResult+'cx> {
-    let mut exprs = match get_exprs_from_tts(cx, sp, tts) {
-        Some(ref exprs) if exprs.is_empty() => {
-            cx.span_err(sp, "env! takes 1 or 2 arguments");
-            return DummyResult::expr(sp);
-        }
-        None => return DummyResult::expr(sp),
-        Some(exprs) => exprs.into_iter()
-    };
-
-    let var = match expr_to_string(cx,
-                                exprs.next().unwrap(),
-                                "expected string literal") {
-        None => return DummyResult::expr(sp),
-        Some((v, _style)) => v
-    };
-    let msg = match exprs.next() {
-        None => {
-            token::intern_and_get_ident(&format!("environment variable `{}` \
-                                                 not defined",
-                                                var))
-        }
-        Some(second) => {
-            match expr_to_string(cx, second, "expected string literal") {
-                None => return DummyResult::expr(sp),
-                Some((s, _style)) => s
-            }
-        }
-    };
-
-    match exprs.next() {
-        None => {}
-        Some(_) => {
-            cx.span_err(sp, "env! takes 1 or 2 arguments");
-            return DummyResult::expr(sp);
-        }
-    }
-
-    let e = match env::var(&var[..]) {
-        Err(_) => {
-            cx.span_err(sp, &msg);
-            cx.expr_usize(sp, 0)
-        }
-        Ok(s) => cx.expr_str(sp, token::intern_and_get_ident(&s))
-    };
-    MacEager::expr(e)
-}
index 573f4cfe8fa5d8d811872e32d5725e77a455bba3..5f27bdfc98a41bac1f9ff0b814ab89e49a515cbc 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use ast::{Block, Crate, DeclLocal, ExprMac, PatMac};
+use ast::{Block, Crate, DeclLocal, PatMac};
 use ast::{Local, Ident, Mac_, Name};
 use ast::{ItemMac, MacStmtWithSemicolon, Mrk, Stmt, StmtDecl, StmtMac};
 use ast::{StmtExpr, StmtSemi};
@@ -21,7 +21,7 @@ use attr::{AttrMetaMethods, WithAttrs};
 use codemap;
 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
 use ext::base::*;
-use feature_gate::{self, Features, GatedCfgAttr};
+use feature_gate::{self, Features};
 use fold;
 use fold::*;
 use util::move_map::MoveMap;
@@ -201,11 +201,12 @@ fn expand_mac_invoc<T, F, G>(mac: ast::Mac,
     let extname = pth.segments[0].identifier.name;
     match fld.cx.syntax_env.find(extname) {
         None => {
-            fld.cx.span_err(
+            let mut err = fld.cx.struct_span_err(
                 pth.span,
                 &format!("macro undefined: '{}!'",
                         &extname));
-            fld.cx.suggest_macro_name(&extname.as_str(), pth.span);
+            fld.cx.suggest_macro_name(&extname.as_str(), pth.span, &mut err);
+            err.emit();
 
             // let compilation continue
             None
@@ -334,11 +335,15 @@ fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool
     for attr in attrs {
         let mut is_use = attr.check_name("macro_use");
         if attr.check_name("macro_escape") {
-            fld.cx.span_warn(attr.span, "macro_escape is a deprecated synonym for macro_use");
+            let mut err =
+                fld.cx.struct_span_warn(attr.span,
+                                        "macro_escape is a deprecated synonym for macro_use");
             is_use = true;
             if let ast::AttrStyle::Inner = attr.node.style {
-                fld.cx.fileline_help(attr.span, "consider an outer attribute, \
-                                             #[macro_use] mod ...");
+                err.fileline_help(attr.span, "consider an outer attribute, \
+                                              #[macro_use] mod ...").emit();
+            } else {
+                err.emit();
             }
         };
 
@@ -1276,15 +1281,11 @@ impl<'feat> ExpansionConfig<'feat> {
     }
 }
 
-pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
-                           cfg: ExpansionConfig<'feat>,
-                           // these are the macros being imported to this crate:
-                           imported_macros: Vec<ast::MacroDef>,
-                           user_exts: Vec<NamedSyntaxExtension>,
-                           feature_gated_cfgs: &mut Vec<GatedCfgAttr>,
-                           c: Crate) -> (Crate, HashSet<Name>) {
-    let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg,
-                              feature_gated_cfgs);
+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>) {
     if std_inject::no_core(&c) {
         cx.crate_root = None;
     } else if std_inject::no_std(&c) {
@@ -1305,7 +1306,7 @@ pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
 
         let mut ret = expander.fold_crate(c);
         ret.exported_macros = expander.cx.exported_macros.clone();
-        parse_sess.span_diagnostic.handler().abort_if_errors();
+        cx.parse_sess.span_diagnostic.abort_if_errors();
         ret
     };
     return (ret, cx.syntax_env.names);
@@ -1401,6 +1402,7 @@ mod tests {
     use ast;
     use ast::Name;
     use codemap;
+    use ext::base::ExtCtxt;
     use ext::mtwt;
     use fold::Folder;
     use parse;
@@ -1471,7 +1473,9 @@ mod tests {
             src,
             Vec::new(), &sess);
         // should fail:
-        expand_crate(&sess,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast);
+        let mut gated_cfgs = vec![];
+        let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs);
+        expand_crate(ecx, vec![], vec![], crate_ast);
     }
 
     // make sure that macros can't escape modules
@@ -1484,7 +1488,9 @@ mod tests {
             "<test>".to_string(),
             src,
             Vec::new(), &sess);
-        expand_crate(&sess,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast);
+        let mut gated_cfgs = vec![];
+        let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs);
+        expand_crate(ecx, vec![], vec![], crate_ast);
     }
 
     // macro_use modules should allow macros to escape
@@ -1496,14 +1502,18 @@ mod tests {
             "<test>".to_string(),
             src,
             Vec::new(), &sess);
-        expand_crate(&sess, test_ecfg(), vec!(), vec!(), &mut vec![], crate_ast);
+        let mut gated_cfgs = vec![];
+        let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs);
+        expand_crate(ecx, vec![], 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...
-        expand_crate(&ps,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast).0
+        let mut gated_cfgs = vec![];
+        let ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut gated_cfgs);
+        expand_crate(ecx, vec![], vec![], crate_ast).0
     }
 
     // find the pat_ident paths in a crate
diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs
deleted file mode 100644 (file)
index 904cf0c..0000000
+++ /dev/null
@@ -1,715 +0,0 @@
-// 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.
-
-use self::ArgumentType::*;
-use self::Position::*;
-
-use ast;
-use codemap::{Span, respan};
-use ext::base::*;
-use ext::base;
-use ext::build::AstBuilder;
-use fmt_macros as parse;
-use fold::Folder;
-use parse::token::special_idents;
-use parse::token;
-use ptr::P;
-
-use std::collections::HashMap;
-
-#[derive(PartialEq)]
-enum ArgumentType {
-    Known(String),
-    Unsigned
-}
-
-enum Position {
-    Exact(usize),
-    Named(String),
-}
-
-struct Context<'a, 'b:'a> {
-    ecx: &'a mut ExtCtxt<'b>,
-    /// The macro's call site. References to unstable formatting internals must
-    /// use this span to pass the stability checker.
-    macsp: Span,
-    /// The span of the format string literal.
-    fmtsp: Span,
-
-    /// Parsed argument expressions and the types that we've found so far for
-    /// them.
-    args: Vec<P<ast::Expr>>,
-    arg_types: Vec<Option<ArgumentType>>,
-    /// Parsed named expressions and the types that we've found for them so far.
-    /// Note that we keep a side-array of the ordering of the named arguments
-    /// found to be sure that we can translate them in the same order that they
-    /// were declared in.
-    names: HashMap<String, P<ast::Expr>>,
-    name_types: HashMap<String, ArgumentType>,
-    name_ordering: Vec<String>,
-
-    /// The latest consecutive literal strings, or empty if there weren't any.
-    literal: String,
-
-    /// Collection of the compiled `rt::Argument` structures
-    pieces: Vec<P<ast::Expr>>,
-    /// Collection of string literals
-    str_pieces: Vec<P<ast::Expr>>,
-    /// Stays `true` if all formatting parameters are default (as in "{}{}").
-    all_pieces_simple: bool,
-
-    name_positions: HashMap<String, usize>,
-
-    /// Updated as arguments are consumed or methods are entered
-    nest_level: usize,
-    next_arg: usize,
-}
-
-/// Parses the arguments from the given list of tokens, returning None
-/// if there's a parse error so we can continue parsing other format!
-/// expressions.
-///
-/// If parsing succeeds, the return value is:
-/// ```ignore
-/// Some((fmtstr, unnamed arguments, ordering of named arguments,
-///       named arguments))
-/// ```
-fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-              -> Option<(P<ast::Expr>, Vec<P<ast::Expr>>, Vec<String>,
-                         HashMap<String, P<ast::Expr>>)> {
-    let mut args = Vec::new();
-    let mut names = HashMap::<String, P<ast::Expr>>::new();
-    let mut order = Vec::new();
-
-    let mut p = ecx.new_parser_from_tts(tts);
-
-    if p.token == token::Eof {
-        ecx.span_err(sp, "requires at least a format string argument");
-        return None;
-    }
-    let fmtstr = panictry!(p.parse_expr());
-    let mut named = false;
-    while p.token != token::Eof {
-        if !panictry!(p.eat(&token::Comma)) {
-            ecx.span_err(sp, "expected token: `,`");
-            return None;
-        }
-        if p.token == token::Eof { break } // accept trailing commas
-        if named || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) {
-            named = true;
-            let ident = match p.token {
-                token::Ident(i, _) => {
-                    panictry!(p.bump());
-                    i
-                }
-                _ if named => {
-                    ecx.span_err(p.span,
-                                 "expected ident, positional arguments \
-                                 cannot follow named arguments");
-                    return None;
-                }
-                _ => {
-                    ecx.span_err(p.span,
-                                 &format!("expected ident for named argument, found `{}`",
-                                         p.this_token_to_string()));
-                    return None;
-                }
-            };
-            let name: &str = &ident.name.as_str();
-
-            panictry!(p.expect(&token::Eq));
-            let e = panictry!(p.parse_expr());
-            match names.get(name) {
-                None => {}
-                Some(prev) => {
-                    ecx.span_err(e.span,
-                                 &format!("duplicate argument named `{}`",
-                                         name));
-                    ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here");
-                    continue
-                }
-            }
-            order.push(name.to_string());
-            names.insert(name.to_string(), e);
-        } else {
-            args.push(panictry!(p.parse_expr()));
-        }
-    }
-    Some((fmtstr, args, order, names))
-}
-
-impl<'a, 'b> Context<'a, 'b> {
-    /// Verifies one piece of a parse string. All errors are not emitted as
-    /// fatal so we can continue giving errors about this and possibly other
-    /// format strings.
-    fn verify_piece(&mut self, p: &parse::Piece) {
-        match *p {
-            parse::String(..) => {}
-            parse::NextArgument(ref arg) => {
-                // width/precision first, if they have implicit positional
-                // parameters it makes more sense to consume them first.
-                self.verify_count(arg.format.width);
-                self.verify_count(arg.format.precision);
-
-                // argument second, if it's an implicit positional parameter
-                // it's written second, so it should come after width/precision.
-                let pos = match arg.position {
-                    parse::ArgumentNext => {
-                        let i = self.next_arg;
-                        if self.check_positional_ok() {
-                            self.next_arg += 1;
-                        }
-                        Exact(i)
-                    }
-                    parse::ArgumentIs(i) => Exact(i),
-                    parse::ArgumentNamed(s) => Named(s.to_string()),
-                };
-
-                let ty = Known(arg.format.ty.to_string());
-                self.verify_arg_type(pos, ty);
-            }
-        }
-    }
-
-    fn verify_count(&mut self, c: parse::Count) {
-        match c {
-            parse::CountImplied | parse::CountIs(..) => {}
-            parse::CountIsParam(i) => {
-                self.verify_arg_type(Exact(i), Unsigned);
-            }
-            parse::CountIsName(s) => {
-                self.verify_arg_type(Named(s.to_string()), Unsigned);
-            }
-            parse::CountIsNextParam => {
-                if self.check_positional_ok() {
-                    let next_arg = self.next_arg;
-                    self.verify_arg_type(Exact(next_arg), Unsigned);
-                    self.next_arg += 1;
-                }
-            }
-        }
-    }
-
-    fn check_positional_ok(&mut self) -> bool {
-        if self.nest_level != 0 {
-            self.ecx.span_err(self.fmtsp, "cannot use implicit positional \
-                                           arguments nested inside methods");
-            false
-        } else {
-            true
-        }
-    }
-
-    fn describe_num_args(&self) -> String {
-        match self.args.len() {
-            0 => "no arguments given".to_string(),
-            1 => "there is 1 argument".to_string(),
-            x => format!("there are {} arguments", x),
-        }
-    }
-
-    fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
-        match arg {
-            Exact(arg) => {
-                if self.args.len() <= arg {
-                    let msg = format!("invalid reference to argument `{}` ({})",
-                                      arg, self.describe_num_args());
-
-                    self.ecx.span_err(self.fmtsp, &msg[..]);
-                    return;
-                }
-                {
-                    let arg_type = match self.arg_types[arg] {
-                        None => None,
-                        Some(ref x) => Some(x)
-                    };
-                    self.verify_same(self.args[arg].span, &ty, arg_type);
-                }
-                if self.arg_types[arg].is_none() {
-                    self.arg_types[arg] = Some(ty);
-                }
-            }
-
-            Named(name) => {
-                let span = match self.names.get(&name) {
-                    Some(e) => e.span,
-                    None => {
-                        let msg = format!("there is no argument named `{}`", name);
-                        self.ecx.span_err(self.fmtsp, &msg[..]);
-                        return;
-                    }
-                };
-                self.verify_same(span, &ty, self.name_types.get(&name));
-                if !self.name_types.contains_key(&name) {
-                    self.name_types.insert(name.clone(), ty);
-                }
-                // Assign this named argument a slot in the arguments array if
-                // it hasn't already been assigned a slot.
-                if !self.name_positions.contains_key(&name) {
-                    let slot = self.name_positions.len();
-                    self.name_positions.insert(name, slot);
-                }
-            }
-        }
-    }
-
-    /// When we're keeping track of the types that are declared for certain
-    /// arguments, we assume that `None` means we haven't seen this argument
-    /// yet, `Some(None)` means that we've seen the argument, but no format was
-    /// specified, and `Some(Some(x))` means that the argument was declared to
-    /// have type `x`.
-    ///
-    /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
-    /// that: `Some(None) == Some(Some(x))`
-    fn verify_same(&self,
-                   sp: Span,
-                   ty: &ArgumentType,
-                   before: Option<&ArgumentType>) {
-        let cur = match before {
-            None => return,
-            Some(t) => t,
-        };
-        if *ty == *cur {
-            return
-        }
-        match (cur, ty) {
-            (&Known(ref cur), &Known(ref ty)) => {
-                self.ecx.span_err(sp,
-                                  &format!("argument redeclared with type `{}` when \
-                                           it was previously `{}`",
-                                          *ty,
-                                          *cur));
-            }
-            (&Known(ref cur), _) => {
-                self.ecx.span_err(sp,
-                                  &format!("argument used to format with `{}` was \
-                                           attempted to not be used for formatting",
-                                           *cur));
-            }
-            (_, &Known(ref ty)) => {
-                self.ecx.span_err(sp,
-                                  &format!("argument previously used as a format \
-                                           argument attempted to be used as `{}`",
-                                           *ty));
-            }
-            (_, _) => {
-                self.ecx.span_err(sp, "argument declared with multiple formats");
-            }
-        }
-    }
-
-    fn rtpath(ecx: &ExtCtxt, s: &str) -> Vec<ast::Ident> {
-        ecx.std_path(&["fmt", "rt", "v1", s])
-    }
-
-    fn trans_count(&self, c: parse::Count) -> P<ast::Expr> {
-        let sp = self.macsp;
-        let count = |c, arg| {
-            let mut path = Context::rtpath(self.ecx, "Count");
-            path.push(self.ecx.ident_of(c));
-            match arg {
-                Some(arg) => self.ecx.expr_call_global(sp, path, vec![arg]),
-                None => self.ecx.expr_path(self.ecx.path_global(sp, path)),
-            }
-        };
-        match c {
-            parse::CountIs(i) => count("Is", Some(self.ecx.expr_usize(sp, i))),
-            parse::CountIsParam(i) => {
-                count("Param", Some(self.ecx.expr_usize(sp, i)))
-            }
-            parse::CountImplied => count("Implied", None),
-            parse::CountIsNextParam => count("NextParam", None),
-            parse::CountIsName(n) => {
-                let i = match self.name_positions.get(n) {
-                    Some(&i) => i,
-                    None => 0, // error already emitted elsewhere
-                };
-                let i = i + self.args.len();
-                count("Param", Some(self.ecx.expr_usize(sp, i)))
-            }
-        }
-    }
-
-    /// Translate the accumulated string literals to a literal expression
-    fn trans_literal_string(&mut self) -> P<ast::Expr> {
-        let sp = self.fmtsp;
-        let s = token::intern_and_get_ident(&self.literal);
-        self.literal.clear();
-        self.ecx.expr_str(sp, s)
-    }
-
-    /// Translate a `parse::Piece` to a static `rt::Argument` or append
-    /// to the `literal` string.
-    fn trans_piece(&mut self, piece: &parse::Piece) -> Option<P<ast::Expr>> {
-        let sp = self.macsp;
-        match *piece {
-            parse::String(s) => {
-                self.literal.push_str(s);
-                None
-            }
-            parse::NextArgument(ref arg) => {
-                // Translate the position
-                let pos = {
-                    let pos = |c, arg| {
-                        let mut path = Context::rtpath(self.ecx, "Position");
-                        path.push(self.ecx.ident_of(c));
-                        match arg {
-                            Some(i) => {
-                                let arg = self.ecx.expr_usize(sp, i);
-                                self.ecx.expr_call_global(sp, path, vec![arg])
-                            }
-                            None => {
-                                self.ecx.expr_path(self.ecx.path_global(sp, path))
-                            }
-                        }
-                    };
-                    match arg.position {
-                        // These two have a direct mapping
-                        parse::ArgumentNext => pos("Next", None),
-                        parse::ArgumentIs(i) => pos("At", Some(i)),
-
-                        // Named arguments are converted to positional arguments
-                        // at the end of the list of arguments
-                        parse::ArgumentNamed(n) => {
-                            let i = match self.name_positions.get(n) {
-                                Some(&i) => i,
-                                None => 0, // error already emitted elsewhere
-                            };
-                            let i = i + self.args.len();
-                            pos("At", Some(i))
-                        }
-                    }
-                };
-
-                let simple_arg = parse::Argument {
-                    position: parse::ArgumentNext,
-                    format: parse::FormatSpec {
-                        fill: arg.format.fill,
-                        align: parse::AlignUnknown,
-                        flags: 0,
-                        precision: parse::CountImplied,
-                        width: parse::CountImplied,
-                        ty: arg.format.ty
-                    }
-                };
-
-                let fill = match arg.format.fill { Some(c) => c, None => ' ' };
-
-                if *arg != simple_arg || fill != ' ' {
-                    self.all_pieces_simple = false;
-                }
-
-                // Translate the format
-                let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
-                let align = |name| {
-                    let mut p = Context::rtpath(self.ecx, "Alignment");
-                    p.push(self.ecx.ident_of(name));
-                    self.ecx.path_global(sp, p)
-                };
-                let align = match arg.format.align {
-                    parse::AlignLeft => align("Left"),
-                    parse::AlignRight => align("Right"),
-                    parse::AlignCenter => align("Center"),
-                    parse::AlignUnknown => align("Unknown"),
-                };
-                let align = self.ecx.expr_path(align);
-                let flags = self.ecx.expr_u32(sp, arg.format.flags);
-                let prec = self.trans_count(arg.format.precision);
-                let width = self.trans_count(arg.format.width);
-                let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec"));
-                let fmt = self.ecx.expr_struct(sp, path, vec!(
-                    self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
-                    self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
-                    self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags),
-                    self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec),
-                    self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
-
-                let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "Argument"));
-                Some(self.ecx.expr_struct(sp, path, vec!(
-                    self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
-                    self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt))))
-            }
-        }
-    }
-
-    fn static_array(ecx: &mut ExtCtxt,
-                    name: &str,
-                    piece_ty: P<ast::Ty>,
-                    pieces: Vec<P<ast::Expr>>)
-                    -> P<ast::Expr> {
-        let sp = piece_ty.span;
-        let ty = ecx.ty_rptr(sp,
-            ecx.ty(sp, ast::TyVec(piece_ty)),
-            Some(ecx.lifetime(sp, special_idents::static_lifetime.name)),
-            ast::MutImmutable);
-        let slice = ecx.expr_vec_slice(sp, pieces);
-        // static instead of const to speed up codegen by not requiring this to be inlined
-        let st = ast::ItemStatic(ty, ast::MutImmutable, slice);
-
-        let name = ecx.ident_of(name);
-        let item = ecx.item(sp, name, vec![], st);
-        let decl = respan(sp, ast::DeclItem(item));
-
-        // Wrap the declaration in a block so that it forms a single expression.
-        ecx.expr_block(ecx.block(sp,
-            vec![P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))],
-            Some(ecx.expr_ident(sp, name))))
-    }
-
-    /// Actually builds the expression which the iformat! block will be expanded
-    /// to
-    fn into_expr(mut self) -> P<ast::Expr> {
-        let mut locals = Vec::new();
-        let mut names = vec![None; self.name_positions.len()];
-        let mut pats = Vec::new();
-        let mut heads = Vec::new();
-
-        // First, build up the static array which will become our precompiled
-        // format "string"
-        let static_lifetime = self.ecx.lifetime(self.fmtsp, special_idents::static_lifetime.name);
-        let piece_ty = self.ecx.ty_rptr(
-                self.fmtsp,
-                self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
-                Some(static_lifetime),
-                ast::MutImmutable);
-        let pieces = Context::static_array(self.ecx,
-                                           "__STATIC_FMTSTR",
-                                           piece_ty,
-                                           self.str_pieces);
-
-
-        // Right now there is a bug such that for the expression:
-        //      foo(bar(&1))
-        // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
-        // valid for the call to `foo`. To work around this all arguments to the
-        // format! string are shoved into locals. Furthermore, we shove the address
-        // of each variable because we don't want to move out of the arguments
-        // passed to this function.
-        for (i, e) in self.args.into_iter().enumerate() {
-            let arg_ty = match self.arg_types[i].as_ref() {
-                Some(ty) => ty,
-                None => continue // error already generated
-            };
-
-            let name = self.ecx.ident_of(&format!("__arg{}", i));
-            pats.push(self.ecx.pat_ident(e.span, name));
-            locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty,
-                                            self.ecx.expr_ident(e.span, name)));
-            heads.push(self.ecx.expr_addr_of(e.span, e));
-        }
-        for name in &self.name_ordering {
-            let e = match self.names.remove(name) {
-                Some(e) => e,
-                None => continue
-            };
-            let arg_ty = match self.name_types.get(name) {
-                Some(ty) => ty,
-                None => continue
-            };
-
-            let lname = self.ecx.ident_of(&format!("__arg{}",
-                                                  *name));
-            pats.push(self.ecx.pat_ident(e.span, lname));
-            names[*self.name_positions.get(name).unwrap()] =
-                Some(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty,
-                                         self.ecx.expr_ident(e.span, lname)));
-            heads.push(self.ecx.expr_addr_of(e.span, e));
-        }
-
-        // Now create a vector containing all the arguments
-        let args = locals.into_iter().chain(names.into_iter().map(|a| a.unwrap()));
-
-        let args_array = self.ecx.expr_vec(self.fmtsp, args.collect());
-
-        // Constructs an AST equivalent to:
-        //
-        //      match (&arg0, &arg1) {
-        //          (tmp0, tmp1) => args_array
-        //      }
-        //
-        // It was:
-        //
-        //      let tmp0 = &arg0;
-        //      let tmp1 = &arg1;
-        //      args_array
-        //
-        // Because of #11585 the new temporary lifetime rule, the enclosing
-        // statements for these temporaries become the let's themselves.
-        // If one or more of them are RefCell's, RefCell borrow() will also
-        // end there; they don't last long enough for args_array to use them.
-        // The match expression solves the scope problem.
-        //
-        // Note, it may also very well be transformed to:
-        //
-        //      match arg0 {
-        //          ref tmp0 => {
-        //              match arg1 => {
-        //                  ref tmp1 => args_array } } }
-        //
-        // But the nested match expression is proved to perform not as well
-        // as series of let's; the first approach does.
-        let pat = self.ecx.pat_tuple(self.fmtsp, pats);
-        let arm = self.ecx.arm(self.fmtsp, vec!(pat), args_array);
-        let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads));
-        let result = self.ecx.expr_match(self.fmtsp, head, vec!(arm));
-
-        let args_slice = self.ecx.expr_addr_of(self.fmtsp, result);
-
-        // Now create the fmt::Arguments struct with all our locals we created.
-        let (fn_name, fn_args) = if self.all_pieces_simple {
-            ("new_v1", vec![pieces, args_slice])
-        } else {
-            // Build up the static array which will store our precompiled
-            // nonstandard placeholders, if there are any.
-            let piece_ty = self.ecx.ty_path(self.ecx.path_global(
-                    self.macsp,
-                    Context::rtpath(self.ecx, "Argument")));
-            let fmt = Context::static_array(self.ecx,
-                                            "__STATIC_FMTARGS",
-                                            piece_ty,
-                                            self.pieces);
-
-            ("new_v1_formatted", vec![pieces, args_slice, fmt])
-        };
-
-        let path = self.ecx.std_path(&["fmt", "Arguments", fn_name]);
-        self.ecx.expr_call_global(self.macsp, path, fn_args)
-    }
-
-    fn format_arg(ecx: &ExtCtxt, macsp: Span, sp: Span,
-                  ty: &ArgumentType, arg: P<ast::Expr>)
-                  -> P<ast::Expr> {
-        let trait_ = match *ty {
-            Known(ref tyname) => {
-                match &tyname[..] {
-                    ""  => "Display",
-                    "?" => "Debug",
-                    "e" => "LowerExp",
-                    "E" => "UpperExp",
-                    "o" => "Octal",
-                    "p" => "Pointer",
-                    "b" => "Binary",
-                    "x" => "LowerHex",
-                    "X" => "UpperHex",
-                    _ => {
-                        ecx.span_err(sp,
-                                     &format!("unknown format trait `{}`",
-                                             *tyname));
-                        "Dummy"
-                    }
-                }
-            }
-            Unsigned => {
-                let path = ecx.std_path(&["fmt", "ArgumentV1", "from_usize"]);
-                return ecx.expr_call_global(macsp, path, vec![arg])
-            }
-        };
-
-        let path = ecx.std_path(&["fmt", trait_, "fmt"]);
-        let format_fn = ecx.path_global(sp, path);
-        let path = ecx.std_path(&["fmt", "ArgumentV1", "new"]);
-        ecx.expr_call_global(macsp, path, vec![arg, ecx.expr_path(format_fn)])
-    }
-}
-
-pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
-                               tts: &[ast::TokenTree])
-                               -> Box<base::MacResult+'cx> {
-
-    match parse_args(ecx, sp, tts) {
-        Some((efmt, args, order, names)) => {
-            MacEager::expr(expand_preparsed_format_args(ecx, sp, efmt,
-                                                      args, order, names))
-        }
-        None => DummyResult::expr(sp)
-    }
-}
-
-/// Take the various parts of `format_args!(efmt, args..., name=names...)`
-/// and construct the appropriate formatting expression.
-pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
-                                    efmt: P<ast::Expr>,
-                                    args: Vec<P<ast::Expr>>,
-                                    name_ordering: Vec<String>,
-                                    names: HashMap<String, P<ast::Expr>>)
-                                    -> P<ast::Expr> {
-    let arg_types: Vec<_> = (0..args.len()).map(|_| None).collect();
-    let macsp = ecx.call_site();
-    // Expand the format literal so that efmt.span will have a backtrace. This
-    // is essential for locating a bug when the format literal is generated in
-    // a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")).
-    let efmt = ecx.expander().fold_expr(efmt);
-    let mut cx = Context {
-        ecx: ecx,
-        args: args,
-        arg_types: arg_types,
-        names: names,
-        name_positions: HashMap::new(),
-        name_types: HashMap::new(),
-        name_ordering: name_ordering,
-        nest_level: 0,
-        next_arg: 0,
-        literal: String::new(),
-        pieces: Vec::new(),
-        str_pieces: Vec::new(),
-        all_pieces_simple: true,
-        macsp: macsp,
-        fmtsp: efmt.span,
-    };
-    let fmt = match expr_to_string(cx.ecx,
-                                   efmt,
-                                   "format argument must be a string literal.") {
-        Some((fmt, _)) => fmt,
-        None => return DummyResult::raw_expr(sp)
-    };
-
-    let mut parser = parse::Parser::new(&fmt);
-
-    loop {
-        match parser.next() {
-            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 => {}
-                }
-            }
-            None => break
-        }
-    }
-    if !parser.errors.is_empty() {
-        cx.ecx.span_err(cx.fmtsp, &format!("invalid format string: {}",
-                                          parser.errors.remove(0)));
-        return DummyResult::raw_expr(sp);
-    }
-    if !cx.literal.is_empty() {
-        let s = cx.trans_literal_string();
-        cx.str_pieces.push(s);
-    }
-
-    // Make sure that all arguments were used and all arguments have types.
-    for (i, ty) in cx.arg_types.iter().enumerate() {
-        if ty.is_none() {
-            cx.ecx.span_err(cx.args[i].span, "argument never used");
-        }
-    }
-    for (name, e) in &cx.names {
-        if !cx.name_types.contains_key(name) {
-            cx.ecx.span_err(e.span, "named argument never used");
-        }
-    }
-
-    cx.into_expr()
-}
diff --git a/src/libsyntax/ext/log_syntax.rs b/src/libsyntax/ext/log_syntax.rs
deleted file mode 100644 (file)
index 5f7ce8d..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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.
-
-use ast;
-use codemap;
-use ext::base;
-use feature_gate;
-use print;
-
-pub fn expand_syntax_ext<'cx>(cx: &'cx mut base::ExtCtxt,
-                              sp: codemap::Span,
-                              tts: &[ast::TokenTree])
-                              -> Box<base::MacResult+'cx> {
-    if !cx.ecfg.enable_log_syntax() {
-        feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
-                                       "log_syntax",
-                                       sp,
-                                       feature_gate::GateIssue::Language,
-                                       feature_gate::EXPLAIN_LOG_SYNTAX);
-        return base::DummyResult::any(sp);
-    }
-
-    println!("{}", print::pprust::tts_to_string(tts));
-
-    // any so that `log_syntax` can be invoked as an expression and item.
-    base::DummyResult::any(sp)
-}
index 496f6a429a31fd4cf4f09c3478b9f1c78db73bf1..bc7dc67e1bae5d68e7d2eb56143f664ddb3998eb 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use ast::{self, Arg, Arm, Block, Expr, Item, Pat, Path, Stmt, TokenTree, Ty};
+use ast::{self, Arg, Arm, Block, Expr, Item, Pat, Stmt, TokenTree, Ty};
 use codemap::Span;
 use ext::base::ExtCtxt;
 use ext::base;
@@ -33,7 +33,7 @@ pub mod rt {
     use ptr::P;
     use std::rc::Rc;
 
-    use ast::{TokenTree, Expr};
+    use ast::TokenTree;
 
     pub use parse::new_parser_from_tts;
     pub use codemap::{BytePos, Span, dummy_spanned, DUMMY_SP};
@@ -801,8 +801,8 @@ fn parse_arguments_to_quote(cx: &ExtCtxt, tts: &[TokenTree])
     p.quote_depth += 1;
 
     let cx_expr = panictry!(p.parse_expr());
-    if !panictry!(p.eat(&token::Comma)) {
-        panic!(p.fatal("expected token `,`"));
+    if !p.eat(&token::Comma) {
+        let _ = p.diagnostic().fatal("expected token `,`");
     }
 
     let tts = panictry!(p.parse_all_token_trees());
index 7899e170ecf195f322da65a4af938fa55be0fec0..f00224bacdd2d13459933750ef81c7d5076bd6a4 100644 (file)
@@ -117,11 +117,9 @@ pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree
             while self.p.token != token::Eof {
                 match panictry!(self.p.parse_item()) {
                     Some(item) => ret.push(item),
-                    None => panic!(self.p.span_fatal(
-                        self.p.span,
-                        &format!("expected item, found `{}`",
-                                 self.p.this_token_to_string())
-                    ))
+                    None => panic!(self.p.diagnostic().span_fatal(self.p.span,
+                                                           &format!("expected item, found `{}`",
+                                                                    self.p.this_token_to_string())))
                 }
             }
             Some(ret)
diff --git a/src/libsyntax/ext/trace_macros.rs b/src/libsyntax/ext/trace_macros.rs
deleted file mode 100644 (file)
index 628b88d..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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.
-
-use ast::TokenTree;
-use codemap::Span;
-use ext::base::ExtCtxt;
-use ext::base;
-use feature_gate;
-use parse::token::keywords;
-
-
-pub fn expand_trace_macros(cx: &mut ExtCtxt,
-                           sp: Span,
-                           tt: &[TokenTree])
-                           -> Box<base::MacResult+'static> {
-    if !cx.ecfg.enable_trace_macros() {
-        feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
-                                       "trace_macros",
-                                       sp,
-                                       feature_gate::GateIssue::Language,
-                                       feature_gate::EXPLAIN_TRACE_MACROS);
-        return base::DummyResult::any(sp);
-    }
-
-    match (tt.len(), tt.first()) {
-        (1, Some(&TokenTree::Token(_, ref tok))) if tok.is_keyword(keywords::True) => {
-            cx.set_trace_macros(true);
-        }
-        (1, Some(&TokenTree::Token(_, ref tok))) if tok.is_keyword(keywords::False) => {
-            cx.set_trace_macros(false);
-        }
-        _ => cx.span_err(sp, "trace_macros! accepts only `true` or `false`"),
-    }
-
-    base::DummyResult::any(sp)
-}
index 5b8307eb6c6f2a1f4e4a93dbdea595b8971ec3c6..ae8ab0541050c0be5b251f4a1fda0e77de690876 100644 (file)
@@ -79,13 +79,14 @@ pub use self::ParseResult::*;
 use self::TokenTreeOrTokenTreeVec::*;
 
 use ast;
-use ast::{TokenTree, Name};
-use codemap::{BytePos, mk_sp, Span};
+use ast::{TokenTree, Name, Ident};
+use codemap::{BytePos, mk_sp, Span, Spanned};
 use codemap;
+use errors::FatalError;
 use parse::lexer::*; //resolve bug?
 use parse::ParseSess;
 use parse::parser::{LifetimeAndTypesWithoutColons, Parser};
-use parse::token::{Eof, DocComment, MatchNt, SubstNt};
+use parse::token::{DocComment, MatchNt, SubstNt};
 use parse::token::{Token, Nonterminal};
 use parse::token;
 use print::pprust;
@@ -499,38 +500,49 @@ pub fn parse(sess: &ParseSess,
     }
 }
 
-pub fn parse_nt(p: &mut Parser, sp: Span, name: &str) -> Nonterminal {
+pub fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
     match name {
         "tt" => {
             p.quote_depth += 1; //but in theory, non-quoted tts might be useful
-            let res = token::NtTT(P(panictry!(p.parse_token_tree())));
+            let res: ::parse::PResult<'a, _> = p.parse_token_tree();
+            let res = token::NtTT(P(panictry!(res)));
             p.quote_depth -= 1;
             return res;
         }
         _ => {}
     }
     // check at the beginning and the parser checks after each bump
-    panictry!(p.check_unknown_macro_variable());
+    p.check_unknown_macro_variable();
     match name {
         "item" => match panictry!(p.parse_item()) {
             Some(i) => token::NtItem(i),
-            None => panic!(p.fatal("expected an item keyword"))
+            None => {
+                p.fatal("expected an item keyword").emit();
+                panic!(FatalError);
+            }
         },
         "block" => token::NtBlock(panictry!(p.parse_block())),
         "stmt" => match panictry!(p.parse_stmt()) {
             Some(s) => token::NtStmt(s),
-            None => panic!(p.fatal("expected a statement"))
+            None => {
+                p.fatal("expected a statement").emit();
+                panic!(FatalError);
+            }
         },
         "pat" => token::NtPat(panictry!(p.parse_pat())),
         "expr" => token::NtExpr(panictry!(p.parse_expr())),
         "ty" => token::NtTy(panictry!(p.parse_ty())),
         // this could be handled like a token, since it is one
         "ident" => match p.token {
-            token::Ident(sn,b) => { panictry!(p.bump()); token::NtIdent(Box::new(sn),b) }
+            token::Ident(sn,b) => {
+                p.bump();
+                token::NtIdent(Box::new(Spanned::<Ident>{node: sn, span: p.span}),b)
+            }
             _ => {
                 let token_str = pprust::token_to_string(&p.token);
-                panic!(p.fatal(&format!("expected ident, found {}",
-                                 &token_str[..])))
+                p.fatal(&format!("expected ident, found {}",
+                                 &token_str[..])).emit();
+                panic!(FatalError)
             }
         },
         "path" => {
@@ -538,11 +550,12 @@ pub fn parse_nt(p: &mut Parser, sp: Span, name: &str) -> Nonterminal {
         },
         "meta" => token::NtMeta(panictry!(p.parse_meta_item())),
         _ => {
-            panic!(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`"))
+            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);
         }
     }
 }
index 24439e918dd55ef873e15d977fdb4f557127afbf..82fa0f8a8b2a46fc4070c1f2c2d3f2dec5af44c3 100644 (file)
@@ -25,8 +25,9 @@ use ptr::P;
 use util::small_vector::SmallVector;
 
 use std::cell::RefCell;
+use std::collections::{HashMap};
+use std::collections::hash_map::{Entry};
 use std::rc::Rc;
-use std::iter::once;
 
 struct ParserAnyMacro<'a> {
     parser: RefCell<Parser<'a>>,
@@ -47,7 +48,7 @@ impl<'a> ParserAnyMacro<'a> {
     fn ensure_complete_parse(&self, allow_semi: bool, context: &str) {
         let mut parser = self.parser.borrow_mut();
         if allow_semi && parser.token == token::Semi {
-            panictry!(parser.bump())
+            parser.bump();
         }
         if parser.token != token::Eof {
             let token_str = parser.this_token_to_string();
@@ -55,12 +56,12 @@ impl<'a> ParserAnyMacro<'a> {
                                following",
                               token_str);
             let span = parser.span;
-            parser.span_err(span, &msg[..]);
-
+            let mut err = parser.diagnostic().struct_span_err(span, &msg[..]);
             let msg = format!("caused by the macro expansion here; the usage \
                                of `{}!` is likely invalid in {} context",
                                self.macro_ident, context);
-            parser.span_note(self.site_span, &msg[..]);
+            err.span_note(self.site_span, &msg[..])
+               .emit();
         }
     }
 }
@@ -111,7 +112,10 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
                         Some(stmt) => ret.push(stmt),
                         None => (),
                     },
-                    Err(_) => break,
+                    Err(mut e) => {
+                        e.emit();
+                        break;
+                    }
                 }
             }
         }
@@ -191,7 +195,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
                                            imported_from,
                                            rhs);
                 let mut p = Parser::new(cx.parse_sess(), cx.cfg(), Box::new(trncbr));
-                panictry!(p.check_unknown_macro_variable());
+                p.check_unknown_macro_variable();
                 // Let the context choose how to interpret the result.
                 // Weird, but useful for X-macros.
                 return Box::new(ParserAnyMacro {
@@ -317,15 +321,18 @@ 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) {
     // 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.iter(), &Eof);
+            check_matcher(cx, &tts.tts);
         },
         tt @ &TokenTree::Sequence(..) => {
-            check_matcher(cx, Some(tt).into_iter(), &Eof);
+            check_matcher(cx, ref_slice(tt));
         },
         _ => cx.span_err(sp, "invalid macro matcher; matchers must be contained \
                               in balanced delimiters or a repetition indicator")
@@ -342,10 +349,59 @@ fn check_rhs(cx: &mut ExtCtxt, rhs: &TokenTree) -> bool {
     false
 }
 
-// returns the last token that was checked, for TokenTree::Sequence. this gets used later on.
-fn check_matcher<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token)
+// 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;
 
@@ -372,7 +428,7 @@ fn check_matcher<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token)
                             // look at the token that follows the
                             // sequence, which may itself be a sequence,
                             // and so on).
-                            cx.span_err(sp,
+                            on_fail.react(cx, sp,
                                         &format!("`${0}:{1}` is followed by a \
                                                   sequence repetition, which is not \
                                                   allowed for `{1}` fragments",
@@ -395,13 +451,13 @@ fn check_matcher<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token)
                     // 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)) => {
-                            cx.span_err(sp, &msg);
+                            on_fail.react(cx, sp, &msg);
                             continue
                         }
                         (&Eof, _) => return Some((sp, tok.clone())),
                         (_, Ok(true)) => continue,
                         (next, Ok(false)) => {
-                            cx.span_err(sp, &format!("`${0}:{1}` is followed by `{2}`, which \
+                            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)));
@@ -417,7 +473,7 @@ fn check_matcher<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token)
                     // run the algorithm on the contents with F set to U. If it
                     // accepts, continue, else, reject.
                     Some(ref u) => {
-                        let last = check_matcher(cx, seq.tts.iter(), 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
@@ -431,14 +487,14 @@ fn check_matcher<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token)
                                     Some(&&TokenTree::Delimited(_, ref delim)) =>
                                         delim.close_token(),
                                     Some(_) => {
-                                        cx.span_err(sp, "sequence repetition followed by \
+                                        on_fail.react(cx, sp, "sequence repetition followed by \
                                                 another sequence repetition, which is not allowed");
                                         Eof
                                     },
                                     None => Eof
                                 };
-                                check_matcher(cx, once(&TokenTree::Token(span, tok.clone())),
-                                              &fol)
+                                check_matcher_old(cx, once(&TokenTree::Token(span, tok.clone())),
+                                                  &fol, on_fail)
                             },
                             None => last,
                         }
@@ -451,13 +507,13 @@ fn check_matcher<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token)
                             Some(&&TokenTree::Token(_, ref tok)) => tok.clone(),
                             Some(&&TokenTree::Delimited(_, ref delim)) => delim.close_token(),
                             Some(_) => {
-                                cx.span_err(sp, "sequence repetition followed by another \
+                                on_fail.react(cx, sp, "sequence repetition followed by another \
                                              sequence repetition, which is not allowed");
                                 Eof
                             },
                             None => Eof
                         };
-                        check_matcher(cx, seq.tts.iter(), &fol)
+                        check_matcher_old(cx, seq.tts.iter(), &fol, on_fail)
                     }
                 }
             },
@@ -468,13 +524,425 @@ fn check_matcher<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token)
             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(cx, tts.tts.iter(), &tts.close_token())
+                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) {
+    let first_sets = FirstSets::new(matcher);
+    let empty_suffix = TokenSet::empty();
+    check_matcher_core(cx, &first_sets, matcher, &empty_suffix, on_fail);
+}
+
+// The FirstSets for a matcher is a mapping from subsequences in the
+// matcher to the FIRST set for that subsequence.
+//
+// This mapping is partially precomputed via a backwards scan over the
+// token trees of the matcher, which provides a mapping from each
+// repetition sequence to its FIRST set.
+//
+// (Hypothetically sequences should be uniquely identifiable via their
+// spans, though perhaps that is false e.g. for macro-generated macros
+// that do not try to inject artificial span information. My plan is
+// to try to catch such cases ahead of time and not include them in
+// the precomputed mapping.)
+struct FirstSets {
+    // this maps each TokenTree::Sequence `$(tt ...) SEP OP` that is uniquely identified by its
+    // span in the original matcher to the First set for the inner sequence `tt ...`.
+    //
+    // If two sequences have the same span in a matcher, then map that
+    // span to None (invalidating the mapping here and forcing the code to
+    // use a slow path).
+    first: HashMap<Span, Option<TokenSet>>,
+}
+
+impl FirstSets {
+    fn new(tts: &[TokenTree]) -> FirstSets {
+        let mut sets = FirstSets { first: HashMap::new() };
+        build_recur(&mut sets, tts);
+        return sets;
+
+        // walks backward over `tts`, returning the FIRST for `tts`
+        // and updating `sets` at the same time for all sequence
+        // substructure we find within `tts`.
+        fn build_recur(sets: &mut FirstSets, tts: &[TokenTree]) -> TokenSet {
+            let mut first = TokenSet::empty();
+            for tt in tts.iter().rev() {
+                match *tt {
+                    TokenTree::Token(sp, ref tok) => {
+                        first.replace_with((sp, tok.clone()));
+                    }
+                    TokenTree::Delimited(_, ref delimited) => {
+                        build_recur(sets, &delimited.tts[..]);
+                        first.replace_with((delimited.open_span,
+                                            Token::OpenDelim(delimited.delim)));
+                    }
+                    TokenTree::Sequence(sp, ref seq_rep) => {
+                        let subfirst = build_recur(sets, &seq_rep.tts[..]);
+
+                        match sets.first.entry(sp) {
+                            Entry::Vacant(vac) => {
+                                vac.insert(Some(subfirst.clone()));
+                            }
+                            Entry::Occupied(mut occ) => {
+                                // if there is already an entry, then a span must have collided.
+                                // This should not happen with typical macro_rules macros,
+                                // but syntax extensions need not maintain distinct spans,
+                                // so distinct syntax trees can be assigned the same span.
+                                // In such a case, the map cannot be trusted; so mark this
+                                // entry as unusable.
+                                occ.insert(None);
+                            }
+                        }
+
+                        // If the sequence contents can be empty, then the first
+                        // token could be the separator token itself.
+
+                        if let (Some(ref sep), true) = (seq_rep.separator.clone(),
+                                                        subfirst.maybe_empty) {
+                            first.add_one_maybe((sp, sep.clone()));
+                        }
+
+                        // Reverse scan: Sequence comes before `first`.
+                        if subfirst.maybe_empty || seq_rep.op == ast::KleeneOp::ZeroOrMore {
+                            // If sequence is potentially empty, then
+                            // union them (preserving first emptiness).
+                            first.add_all(&TokenSet { maybe_empty: true, ..subfirst });
+                        } else {
+                            // Otherwise, sequence guaranteed
+                            // non-empty; replace first.
+                            first = subfirst;
+                        }
+                    }
+                }
+            }
+
+            return first;
+        }
+    }
+
+    // walks forward over `tts` until all potential FIRST tokens are
+    // identified.
+    fn first(&self, tts: &[TokenTree]) -> TokenSet {
+        let mut first = TokenSet::empty();
+        for tt in tts.iter() {
+            assert!(first.maybe_empty);
+            match *tt {
+                TokenTree::Token(sp, ref tok) => {
+                    first.add_one((sp, tok.clone()));
+                    return first;
+                }
+                TokenTree::Delimited(_, ref delimited) => {
+                    first.add_one((delimited.open_span,
+                                   Token::OpenDelim(delimited.delim)));
+                    return first;
+                }
+                TokenTree::Sequence(sp, ref seq_rep) => {
+                    match self.first.get(&sp) {
+                        Some(&Some(ref subfirst)) => {
+
+                            // If the sequence contents can be empty, then the first
+                            // token could be the separator token itself.
+
+                            if let (Some(ref sep), true) = (seq_rep.separator.clone(),
+                                                            subfirst.maybe_empty) {
+                                first.add_one_maybe((sp, sep.clone()));
+                            }
+
+                            assert!(first.maybe_empty);
+                            first.add_all(subfirst);
+                            if subfirst.maybe_empty || seq_rep.op == ast::KleeneOp::ZeroOrMore {
+                                // continue scanning for more first
+                                // tokens, but also make sure we
+                                // restore empty-tracking state
+                                first.maybe_empty = true;
+                                continue;
+                            } else {
+                                return first;
+                            }
+                        }
+
+                        Some(&None) => {
+                            panic!("assume all sequences have (unique) spans for now");
+                        }
+
+                        None => {
+                            panic!("We missed a sequence during FirstSets construction");
+                        }
+                    }
+                }
+            }
+        }
+
+        // we only exit the loop if `tts` was empty or if every
+        // element of `tts` matches the empty sequence.
+        assert!(first.maybe_empty);
+        return first;
+    }
+}
+
+// A set of Tokens, which may include MatchNt tokens (for
+// macro-by-example syntactic variables). It also carries the
+// `maybe_empty` flag; that is true if and only if the matcher can
+// match an empty token sequence.
+//
+// The First set is computed on submatchers like `$($a:expr b),* $(c)* d`,
+// which has corresponding FIRST = {$a:expr, c, d}.
+// Likewise, `$($a:expr b),* $(c)+ d` has FIRST = {$a:expr, c}.
+//
+// (Notably, we must allow for *-op to occur zero times.)
+#[derive(Clone, Debug)]
+struct TokenSet {
+    tokens: Vec<(Span, Token)>,
+    maybe_empty: bool,
+}
+
+impl TokenSet {
+    // Returns a set for the empty sequence.
+    fn empty() -> Self { TokenSet { tokens: Vec::new(), maybe_empty: true } }
+
+    // Returns the set `{ tok }` for the single-token (and thus
+    // non-empty) sequence [tok].
+    fn singleton(tok: (Span, Token)) -> Self {
+        TokenSet { tokens: vec![tok], maybe_empty: false }
+    }
+
+    // Changes self to be the set `{ tok }`.
+    // Since `tok` is always present, marks self as non-empty.
+    fn replace_with(&mut self, tok: (Span, Token)) {
+        self.tokens.clear();
+        self.tokens.push(tok);
+        self.maybe_empty = false;
+    }
+
+    // Changes self to be the empty set `{}`; meant for use when
+    // the particular token does not matter, but we want to
+    // record that it occurs.
+    fn replace_with_irrelevant(&mut self) {
+        self.tokens.clear();
+        self.maybe_empty = false;
+    }
+
+    // Adds `tok` to the set for `self`, marking sequence as non-empy.
+    fn add_one(&mut self, tok: (Span, Token)) {
+        if !self.tokens.contains(&tok) {
+            self.tokens.push(tok);
+        }
+        self.maybe_empty = false;
+    }
+
+    // Adds `tok` to the set for `self`. (Leaves `maybe_empty` flag alone.)
+    fn add_one_maybe(&mut self, tok: (Span, Token)) {
+        if !self.tokens.contains(&tok) {
+            self.tokens.push(tok);
+        }
+    }
+
+    // Adds all elements of `other` to this.
+    //
+    // (Since this is a set, we filter out duplicates.)
+    //
+    // If `other` is potentially empty, then preserves the previous
+    // setting of the empty flag of `self`. If `other` is guaranteed
+    // non-empty, then `self` is marked non-empty.
+    fn add_all(&mut self, other: &Self) {
+        for tok in &other.tokens {
+            if !self.tokens.contains(tok) {
+                self.tokens.push(tok.clone());
+            }
+        }
+        if !other.maybe_empty {
+            self.maybe_empty = false;
+        }
+    }
+}
+
+// Checks that `matcher` is internally consistent and that it
+// can legally by followed by a token N, for all N in `follow`.
+// (If `follow` is empty, then it imposes no constraint on
+// the `matcher`.)
+//
+// Returns the set of NT tokens that could possibly come last in
+// `matcher`. (If `matcher` matches the empty sequence, then
+// `maybe_empty` will be set to true.)
+//
+// Requires that `first_sets` is pre-computed for `matcher`;
+// see `FirstSets::new`.
+fn check_matcher_core(cx: &mut ExtCtxt,
+                      first_sets: &FirstSets,
+                      matcher: &[TokenTree],
+                      follow: &TokenSet,
+                      on_fail: &mut OnFail) -> TokenSet {
+    use print::pprust::token_to_string;
+
+    let mut last = TokenSet::empty();
+
+    // 2. For each token and suffix  [T, SUFFIX] in M:
+    // ensure that T can be followed by SUFFIX, and if SUFFIX may be empty,
+    // then ensure T can also be followed by any element of FOLLOW.
+    'each_token: for i in 0..matcher.len() {
+        let token = &matcher[i];
+        let suffix = &matcher[i+1..];
+
+        let build_suffix_first = || {
+            let mut s = first_sets.first(suffix);
+            if s.maybe_empty { s.add_all(follow); }
+            return s;
+        };
+
+        // (we build `suffix_first` on demand below; you can tell
+        // which cases are supposed to fall through by looking for the
+        // initialization of this variable.)
+        let suffix_first;
+
+        // First, update `last` so that it corresponds to the set
+        // of NT tokens that might end the sequence `... token`.
+        match *token {
+            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));
+                    // (This eliminates false positives and duplicates
+                    // from error messages.)
+                    can_be_followed_by_any = true;
+                } else {
+                    can_be_followed_by_any = token_can_be_followed_by_any(tok);
+                }
+
+                if can_be_followed_by_any {
+                    // don't need to track tokens that work with any,
+                    last.replace_with_irrelevant();
+                    // ... and don't need to check tokens that can be
+                    // followed by anything against SUFFIX.
+                    continue 'each_token;
+                } else {
+                    last.replace_with((sp, tok.clone()));
+                    suffix_first = build_suffix_first();
+                }
+            }
+            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);
+                // don't track non NT tokens
+                last.replace_with_irrelevant();
+
+                // also, we don't need to check delimited sequences
+                // against SUFFIX
+                continue 'each_token;
+            }
+            TokenTree::Sequence(sp, ref seq_rep) => {
+                suffix_first = build_suffix_first();
+                // The trick here: when we check the interior, we want
+                // to include the separator (if any) as a potential
+                // (but not guaranteed) element of FOLLOW. So in that
+                // case, we make a temp copy of suffix and stuff
+                // delimiter in there.
+                //
+                // FIXME: Should I first scan suffix_first to see if
+                // delimiter is already in it before I go through the
+                // work of cloning it? But then again, this way I may
+                // get a "tighter" span?
+                let mut new;
+                let my_suffix = if let Some(ref u) = seq_rep.separator {
+                    new = suffix_first.clone();
+                    new.add_one_maybe((sp, u.clone()));
+                    &new
+                } else {
+                    &suffix_first
+                };
+
+                // 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);
+                if next.maybe_empty {
+                    last.add_all(&next);
+                } else {
+                    last = next;
+                }
+
+                // the recursive call to check_matcher_core already ran the 'each_last
+                // check below, so we can just keep going forward here.
+                continue 'each_token;
+            }
+        }
+
+        // (`suffix_first` guaranteed initialized once reaching here.)
+
+        // Now `last` holds the complete set of NT tokens that could
+        // end the sequence before SUFFIX. Check that every one works with `suffix`.
+        'each_last: for &(_sp, ref t) in &last.tokens {
+            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);
+                            // don't bother reporting every source of
+                            // conflict for a particular element of `last`.
+                            continue 'each_last;
+                        }
+                        Ok(true) => {}
+                        Ok(false) => {
+                            let may_be = if last.tokens.len() == 1 &&
+                                suffix_first.tokens.len() == 1
+                            {
+                                "is"
+                            } else {
+                                "may be"
+                            };
+
+                            on_fail.react(
+                                cx, 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));
+                        }
+                    }
+                }
+            }
+        }
+    }
+    last
+}
+
+
+fn token_can_be_followed_by_any(tok: &Token) -> bool {
+    if let &MatchNt(_, ref frag_spec, _, _) = tok {
+        frag_can_be_followed_by_any(&frag_spec.name.as_str())
+    } else {
+        // (Non NT's can always be followed by anthing in matchers.)
+        true
+    }
+}
+
+/// 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 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
@@ -498,12 +966,13 @@ fn can_be_followed_by_any(frag: &str) -> bool {
 }
 
 /// True if `frag` can legally be followed by the token `tok`. For
-/// fragments that can consume an unbounded numbe of tokens, `tok`
+/// fragments that can consume an unbounded number of tokens, `tok`
 /// must be within a well-defined follow set. This is intended to
 /// guarantee future compatibility: for example, without this rule, if
 /// we expanded `expr` to include a new binary operator, we might
 /// 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> {
     if let &CloseDelim(_) = tok {
         // closing a token tree can never be matched by any fragment;
@@ -529,15 +998,18 @@ fn is_in_follow(_: &ExtCtxt, tok: &Token, frag: &str) -> Result<bool, String> {
             },
             "pat" => {
                 match *tok {
-                    FatArrow | Comma | Eq => Ok(true),
-                    Ident(i, _) if i.name.as_str() == "if" || i.name.as_str() == "in" => Ok(true),
+                    FatArrow | Comma | Eq | BinOp(token::Or) => Ok(true),
+                    Ident(i, _) if (i.name.as_str() == "if" ||
+                                    i.name.as_str() == "in") => Ok(true),
                     _ => Ok(false)
                 }
             },
             "path" | "ty" => {
                 match *tok {
-                    Comma | FatArrow | Colon | Eq | Gt | Semi => Ok(true),
-                    Ident(i, _) if i.name.as_str() == "as" => Ok(true),
+                    OpenDelim(token::DelimToken::Brace) | OpenDelim(token::DelimToken::Bracket) |
+                    Comma | FatArrow | Colon | Eq | Gt | Semi | BinOp(token::Or) => Ok(true),
+                    Ident(i, _) if (i.name.as_str() == "as" ||
+                                    i.name.as_str() == "where") => Ok(true),
                     _ => Ok(false)
                 }
             },
@@ -554,3 +1026,22 @@ fn is_in_follow(_: &ExtCtxt, tok: &Token, frag: &str) -> Result<bool, String> {
         }
     }
 }
+
+fn has_legal_fragment_specifier(tok: &Token) -> Result<(), String> {
+    debug!("has_legal_fragment_specifier({:?})", tok);
+    if let &MatchNt(_, ref frag_spec, _, _) = tok {
+        let s = &frag_spec.name.as_str();
+        if !is_legal_fragment_specifier(s) {
+            return Err(s.to_string());
+        }
+    }
+    Ok(())
+}
+
+fn is_legal_fragment_specifier(frag: &str) -> bool {
+    match frag {
+        "item" | "block" | "stmt" | "expr" | "pat" |
+        "path" | "ty" | "ident" | "meta" | "tt" => true,
+        _ => false,
+    }
+}
index 0fc31f3fd08af95871b5df5c0a090a72f8499cdf..8878c606d6a2c80a0c4b86b1d956adf50511308d 100644 (file)
@@ -12,9 +12,9 @@ use self::LockstepIterSize::*;
 use ast;
 use ast::{TokenTree, Ident, Name};
 use codemap::{Span, DUMMY_SP};
-use diagnostic::SpanHandler;
+use errors::Handler;
 use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
-use parse::token::{Eof, DocComment, Interpolated, MatchNt, SubstNt};
+use parse::token::{DocComment, MatchNt, SubstNt};
 use parse::token::{Token, NtIdent, SpecialMacroVar};
 use parse::token;
 use parse::lexer::TokenAndSpan;
@@ -34,7 +34,7 @@ struct TtFrame {
 
 #[derive(Clone)]
 pub struct TtReader<'a> {
-    pub sp_diag: &'a SpanHandler,
+    pub sp_diag: &'a Handler,
     /// the unzipped tree:
     stack: Vec<TtFrame>,
     /* for MBE-style macro transcription */
@@ -55,7 +55,7 @@ pub struct TtReader<'a> {
 /// This can do Macro-By-Example transcription. On the other hand, if
 /// `src` contains no `TokenTree::Sequence`s, `MatchNt`s or `SubstNt`s, `interp` can
 /// (and should) be None.
-pub fn new_tt_reader<'a>(sp_diag: &'a SpanHandler,
+pub fn new_tt_reader<'a>(sp_diag: &'a Handler,
                          interp: Option<HashMap<Name, Rc<NamedMatch>>>,
                          imported_from: Option<Ident>,
                          src: Vec<ast::TokenTree>)
@@ -69,7 +69,7 @@ pub fn new_tt_reader<'a>(sp_diag: &'a SpanHandler,
 /// This can do Macro-By-Example transcription. On the other hand, if
 /// `src` contains no `TokenTree::Sequence`s, `MatchNt`s or `SubstNt`s, `interp` can
 /// (and should) be None.
-pub fn new_tt_reader_with_doc_flag<'a>(sp_diag: &'a SpanHandler,
+pub fn new_tt_reader_with_doc_flag<'a>(sp_diag: &'a Handler,
                                        interp: Option<HashMap<Name, Rc<NamedMatch>>>,
                                        imported_from: Option<Ident>,
                                        src: Vec<ast::TokenTree>,
@@ -293,8 +293,8 @@ pub fn tt_next_token(r: &mut TtReader) -> TokenAndSpan {
                             // (a) idents can be in lots of places, so it'd be a pain
                             // (b) we actually can, since it's a token.
                             MatchedNonterminal(NtIdent(ref sn, b)) => {
-                                r.cur_span = sp;
-                                r.cur_tok = token::Ident(**sn, b);
+                                r.cur_span = sn.span;
+                                r.cur_tok = token::Ident(sn.node, b);
                                 return ret_val;
                             }
                             MatchedNonterminal(ref other_whole_nt) => {
index c456b7dc8b91c333ff121a235698f59a06859db6..55087c257033756c01e2707ec32d69f066a8c5b3 100644 (file)
@@ -32,7 +32,7 @@ use ast;
 use attr;
 use attr::AttrMetaMethods;
 use codemap::{CodeMap, Span};
-use diagnostic::SpanHandler;
+use errors::Handler;
 use visit;
 use visit::{FnKind, Visitor};
 use parse::token::InternedString;
@@ -82,7 +82,7 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
     ("advanced_slice_patterns", "1.0.0", Some(23121), Active),
     ("tuple_indexing", "1.0.0", None, Accepted),
     ("associated_types", "1.0.0", None, Accepted),
-    ("visible_private_types", "1.0.0", Some(29627), Active),
+    ("visible_private_types", "1.0.0", None, Removed),
     ("slicing_syntax", "1.0.0", None, Accepted),
     ("box_syntax", "1.0.0", Some(27779), Active),
     ("placement_in_syntax", "1.0.0", Some(27779), Active),
@@ -170,7 +170,7 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
     ("slice_patterns", "1.0.0", Some(23121), Active),
 
     // Allows use of unary negate on unsigned integers, e.g. -e for e: u8
-    ("negate_unsigned", "1.0.0", Some(29645), Active),
+    ("negate_unsigned", "1.0.0", Some(29645), Removed),
 
     // Allows the definition of associated constants in `trait` or `impl`
     // blocks.
@@ -230,6 +230,18 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
 
     // Allow attributes on expressions and non-item statements
     ("stmt_expr_attributes", "1.6.0", Some(15701), Active),
+
+    // Allows `#[deprecated]` attribute
+    ("deprecated", "1.6.0", Some(29935), Active),
+
+    // allow using type ascription in expressions
+    ("type_ascription", "1.6.0", Some(23416), Active),
+
+    // Allows cfg(target_thread_local)
+    ("cfg_target_thread_local", "1.7.0", Some(29594), Active),
+
+    // rustc internal
+    ("abi_vectorcall", "1.7.0", None, Active)
 ];
 // (changing above list without updating src/doc/reference.md makes @cmr sad)
 
@@ -317,6 +329,14 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
                                        "the `#[rustc_error]` attribute \
                                         is just used for rustc unit tests \
                                         and will never be stable")),
+    ("rustc_if_this_changed", Whitelisted, Gated("rustc_attrs",
+                                       "the `#[rustc_if_this_changed]` attribute \
+                                        is just used for rustc unit tests \
+                                        and will never be stable")),
+    ("rustc_then_this_would_need", Whitelisted, Gated("rustc_attrs",
+                                       "the `#[rustc_if_this_changed]` attribute \
+                                        is just used for rustc unit tests \
+                                        and will never be stable")),
     ("rustc_move_fragments", Normal, Gated("rustc_attrs",
                                            "the `#[rustc_move_fragments]` attribute \
                                             is just used for rustc unit tests \
@@ -377,6 +397,7 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
     ("must_use", Whitelisted, Ungated),
     ("stable", Whitelisted, Ungated),
     ("unstable", Whitelisted, Ungated),
+    ("deprecated", Normal, Gated("deprecated", "`#[deprecated]` attribute is unstable")),
 
     ("rustc_paren_sugar", Normal, Gated("unboxed_closures",
                                         "unboxed_closures are still evolving")),
@@ -407,6 +428,8 @@ const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)]
     // (name in cfg, feature, function to check if the feature is enabled)
     ("target_feature", "cfg_target_feature", cfg_fn!(|x| x.cfg_target_feature)),
     ("target_vendor", "cfg_target_vendor", cfg_fn!(|x| x.cfg_target_vendor)),
+    ("target_thread_local", "cfg_target_thread_local",
+     cfg_fn!(|x| x.cfg_target_thread_local)),
 ];
 
 #[derive(Debug, Eq, PartialEq)]
@@ -442,10 +465,13 @@ impl PartialOrd for GatedCfgAttr {
 }
 
 impl GatedCfgAttr {
-    pub fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) {
+    pub fn check_and_emit(&self,
+                          diagnostic: &Handler,
+                          features: &Features,
+                          codemap: &CodeMap) {
         match *self {
             GatedCfgAttr::GatedCfg(ref cfg) => {
-                cfg.check_and_emit(diagnostic, features);
+                cfg.check_and_emit(diagnostic, features, codemap);
             }
             GatedCfgAttr::GatedAttr(span) => {
                 if !features.stmt_expr_attributes {
@@ -472,9 +498,12 @@ impl GatedCfg {
                       }
                   })
     }
-    fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) {
+    fn check_and_emit(&self,
+                      diagnostic: &Handler,
+                      features: &Features,
+                      codemap: &CodeMap) {
         let (cfg, feature, has_feature) = GATED_CFGS[self.index];
-        if !has_feature(features) {
+        if !has_feature(features) && !codemap.span_allows_unstable(self.span) {
             let explain = format!("`cfg({})` is experimental and subject to change", cfg);
             emit_feature_err(diagnostic, feature, self.span, GateIssue::Language, &explain);
         }
@@ -510,7 +539,6 @@ pub enum AttributeGate {
 pub struct Features {
     pub unboxed_closures: bool,
     pub rustc_diagnostic_macros: bool,
-    pub visible_private_types: bool,
     pub allow_quote: bool,
     pub allow_asm: bool,
     pub allow_log_syntax: bool,
@@ -523,7 +551,6 @@ pub struct Features {
     pub allow_pushpop_unsafe: bool,
     pub simd_ffi: bool,
     pub unmarked_api: bool,
-    pub negate_unsigned: bool,
     /// spans of #![feature] attrs for stable language features. for error reporting
     pub declared_stable_lang_features: Vec<Span>,
     /// #![feature] attrs for non-language (library) features
@@ -535,10 +562,12 @@ pub struct Features {
     pub type_macros: bool,
     pub cfg_target_feature: bool,
     pub cfg_target_vendor: bool,
+    pub cfg_target_thread_local: bool,
     pub augmented_assignments: bool,
     pub braced_empty_structs: bool,
     pub staged_api: bool,
     pub stmt_expr_attributes: bool,
+    pub deprecated: bool,
 }
 
 impl Features {
@@ -546,7 +575,6 @@ impl Features {
         Features {
             unboxed_closures: false,
             rustc_diagnostic_macros: false,
-            visible_private_types: false,
             allow_quote: false,
             allow_asm: false,
             allow_log_syntax: false,
@@ -559,7 +587,6 @@ impl Features {
             allow_pushpop_unsafe: false,
             simd_ffi: false,
             unmarked_api: false,
-            negate_unsigned: false,
             declared_stable_lang_features: Vec::new(),
             declared_lib_features: Vec::new(),
             const_fn: false,
@@ -569,10 +596,12 @@ impl Features {
             type_macros: false,
             cfg_target_feature: false,
             cfg_target_vendor: false,
+            cfg_target_thread_local: false,
             augmented_assignments: false,
             braced_empty_structs: false,
             staged_api: false,
             stmt_expr_attributes: false,
+            deprecated: false,
         }
     }
 }
@@ -589,21 +618,21 @@ const EXPLAIN_PUSHPOP_UNSAFE: &'static str =
 const EXPLAIN_STMT_ATTR_SYNTAX: &'static str =
     "attributes on non-item statements and expressions are experimental.";
 
-pub fn check_for_box_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
+pub fn check_for_box_syntax(f: Option<&Features>, diag: &Handler, span: Span) {
     if let Some(&Features { allow_box: true, .. }) = f {
         return;
     }
     emit_feature_err(diag, "box_syntax", span, GateIssue::Language, EXPLAIN_BOX_SYNTAX);
 }
 
-pub fn check_for_placement_in(f: Option<&Features>, diag: &SpanHandler, span: Span) {
+pub fn check_for_placement_in(f: Option<&Features>, diag: &Handler, span: Span) {
     if let Some(&Features { allow_placement_in: true, .. }) = f {
         return;
     }
     emit_feature_err(diag, "placement_in_syntax", span, GateIssue::Language, EXPLAIN_PLACEMENT_IN);
 }
 
-pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
+pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &Handler, span: Span) {
     if let Some(&Features { allow_pushpop_unsafe: true, .. }) = f {
         return;
     }
@@ -612,7 +641,7 @@ pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &SpanHandler, span:
 
 struct Context<'a> {
     features: Vec<&'static str>,
-    span_handler: &'a SpanHandler,
+    span_handler: &'a Handler,
     cm: &'a CodeMap,
     plugin_attributes: &'a [(String, AttributeType)],
 }
@@ -698,24 +727,28 @@ pub enum GateIssue {
     Library(Option<u32>)
 }
 
-pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, issue: GateIssue,
+pub fn emit_feature_err(diag: &Handler, feature: &str, span: Span, issue: GateIssue,
                         explain: &str) {
     let issue = match issue {
         GateIssue::Language => find_lang_feature_issue(feature),
         GateIssue::Library(lib) => lib,
     };
 
-    if let Some(n) = issue {
-        diag.span_err(span, &format!("{} (see issue #{})", explain, n));
+    let mut err = if let Some(n) = issue {
+        diag.struct_span_err(span, &format!("{} (see issue #{})", explain, n))
     } else {
-        diag.span_err(span, explain);
-    }
+        diag.struct_span_err(span, explain)
+    };
 
     // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
-    if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() { return; }
-    diag.fileline_help(span, &format!("add #![feature({})] to the \
-                                   crate attributes to enable",
-                                  feature));
+    if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() {
+        err.emit();
+        return;
+    }
+    err.fileline_help(span, &format!("add #![feature({})] to the \
+                                      crate attributes to enable",
+                                     feature));
+    err.emit();
 }
 
 pub const EXPLAIN_ASM: &'static str =
@@ -842,6 +875,11 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
                     Abi::PlatformIntrinsic => {
                         Some(("platform_intrinsics",
                               "platform intrinsics are experimental and possibly buggy"))
+                    },
+                    Abi::Vectorcall => {
+                        Some(("abi_vectorcall",
+                            "vectorcall is experimental and subject to change"
+                        ))
                     }
                     _ => None
                 };
@@ -922,11 +960,13 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
                 self.gate_feature("braced_empty_structs", span,
                                   "empty structs and enum variants with braces are unstable");
             } else if s.is_tuple() {
-                self.context.span_handler.span_err(span, "empty tuple structs and enum variants \
-                                                          are not allowed, use unit structs and \
-                                                          enum variants instead");
-                self.context.span_handler.span_help(span, "remove trailing `()` to make a unit \
-                                                           struct or unit enum variant");
+                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)
@@ -954,6 +994,10 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
                                   "box expression syntax is experimental; \
                                    you can call `Box::new` instead.");
             }
+            ast::ExprType(..) => {
+                self.gate_feature("type_ascription", e.span,
+                                  "type ascription is experimental");
+            }
             _ => {}
         }
         visit::walk_expr(self, e);
@@ -1009,11 +1053,17 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
                                   "intrinsics are subject to change")
             }
             FnKind::ItemFn(_, _, _, _, abi, _) |
-            FnKind::Method(_, &ast::MethodSig { abi, .. }, _) if abi == Abi::RustCall => {
-                self.gate_feature("unboxed_closures",
-                                  span,
-                                  "rust-call ABI is subject to change")
-            }
+            FnKind::Method(_, &ast::MethodSig { abi, .. }, _) => match abi {
+                Abi::RustCall => {
+                    self.gate_feature("unboxed_closures", span,
+                        "rust-call ABI is subject to change");
+                },
+                Abi::Vectorcall => {
+                    self.gate_feature("abi_vectorcall", span,
+                        "vectorcall is experimental and subject to change");
+                },
+                _ => {}
+            },
             _ => {}
         }
         visit::walk_fn(self, fn_kind, fn_decl, block, span);
@@ -1058,7 +1108,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
     }
 }
 
-fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
+fn check_crate_inner<F>(cm: &CodeMap, span_handler: &Handler,
                         krate: &ast::Crate,
                         plugin_attributes: &[(String, AttributeType)],
                         check: F)
@@ -1124,7 +1174,6 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
     Features {
         unboxed_closures: cx.has_feature("unboxed_closures"),
         rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
-        visible_private_types: cx.has_feature("visible_private_types"),
         allow_quote: cx.has_feature("quote"),
         allow_asm: cx.has_feature("asm"),
         allow_log_syntax: cx.has_feature("log_syntax"),
@@ -1137,7 +1186,6 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
         allow_pushpop_unsafe: cx.has_feature("pushpop_unsafe"),
         simd_ffi: cx.has_feature("simd_ffi"),
         unmarked_api: cx.has_feature("unmarked_api"),
-        negate_unsigned: cx.has_feature("negate_unsigned"),
         declared_stable_lang_features: accepted_features,
         declared_lib_features: unknown_features,
         const_fn: cx.has_feature("const_fn"),
@@ -1147,20 +1195,22 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
         type_macros: cx.has_feature("type_macros"),
         cfg_target_feature: cx.has_feature("cfg_target_feature"),
         cfg_target_vendor: cx.has_feature("cfg_target_vendor"),
+        cfg_target_thread_local: cx.has_feature("cfg_target_thread_local"),
         augmented_assignments: cx.has_feature("augmented_assignments"),
         braced_empty_structs: cx.has_feature("braced_empty_structs"),
         staged_api: cx.has_feature("staged_api"),
         stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"),
+        deprecated: cx.has_feature("deprecated"),
     }
 }
 
-pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
+pub fn check_crate_macros(cm: &CodeMap, span_handler: &Handler, krate: &ast::Crate)
 -> Features {
     check_crate_inner(cm, span_handler, krate, &[] as &'static [_],
                       |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
 }
 
-pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
+pub fn check_crate(cm: &CodeMap, span_handler: &Handler, krate: &ast::Crate,
                    plugin_attributes: &[(String, AttributeType)],
                    unstable: UnstableFeatures) -> Features
 {
@@ -1185,7 +1235,7 @@ pub enum UnstableFeatures {
     Cheat
 }
 
-fn maybe_stage_features(span_handler: &SpanHandler, krate: &ast::Crate,
+fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate,
                         unstable: UnstableFeatures) {
     let allow_features = match unstable {
         UnstableFeatures::Allow => true,
index 73f2c51b246222a90bae743f392410e1a049ef33..1de6d6c002f065d60033eb3f0d9cc4228ff575bd 100644 (file)
@@ -23,7 +23,6 @@ use ast;
 use attr::{ThinAttributes, ThinAttributesExt};
 use ast_util;
 use codemap::{respan, Span, Spanned};
-use owned_slice::OwnedSlice;
 use parse::token;
 use ptr::P;
 use util::small_vector::SmallVector;
@@ -233,7 +232,7 @@ pub trait Folder : Sized {
         noop_fold_ty_param(tp, self)
     }
 
-    fn fold_ty_params(&mut self, tps: OwnedSlice<TyParam>) -> OwnedSlice<TyParam> {
+    fn fold_ty_params(&mut self, tps: P<[TyParam]>) -> P<[TyParam]> {
         noop_fold_ty_params(tps, self)
     }
 
@@ -257,13 +256,13 @@ pub trait Folder : Sized {
         noop_fold_opt_lifetime(o_lt, self)
     }
 
-    fn fold_opt_bounds(&mut self, b: Option<OwnedSlice<TyParamBound>>)
-                       -> Option<OwnedSlice<TyParamBound>> {
+    fn fold_opt_bounds(&mut self, b: Option<TyParamBounds>)
+                       -> Option<TyParamBounds> {
         noop_fold_opt_bounds(b, self)
     }
 
-    fn fold_bounds(&mut self, b: OwnedSlice<TyParamBound>)
-                       -> OwnedSlice<TyParamBound> {
+    fn fold_bounds(&mut self, b: TyParamBounds)
+                       -> TyParamBounds {
         noop_fold_bounds(b, self)
     }
 
@@ -470,10 +469,10 @@ pub fn noop_fold_path_parameters<T: Folder>(path_parameters: PathParameters, fld
                                             -> PathParameters
 {
     match path_parameters {
-        AngleBracketedParameters(data) =>
-            AngleBracketedParameters(fld.fold_angle_bracketed_parameter_data(data)),
-        ParenthesizedParameters(data) =>
-            ParenthesizedParameters(fld.fold_parenthesized_parameter_data(data)),
+        PathParameters::AngleBracketed(data) =>
+            PathParameters::AngleBracketed(fld.fold_angle_bracketed_parameter_data(data)),
+        PathParameters::Parenthesized(data) =>
+            PathParameters::Parenthesized(fld.fold_parenthesized_parameter_data(data)),
     }
 }
 
@@ -663,7 +662,8 @@ pub fn noop_fold_interpolated<T: Folder>(nt: token::Nonterminal, fld: &mut T)
         token::NtExpr(expr) => token::NtExpr(fld.fold_expr(expr)),
         token::NtTy(ty) => token::NtTy(fld.fold_ty(ty)),
         token::NtIdent(id, is_mod_name) =>
-            token::NtIdent(Box::new(fld.fold_ident(*id)), is_mod_name),
+            token::NtIdent(Box::new(Spanned::<Ident>{node: fld.fold_ident(id.node), .. *id}),
+                           is_mod_name),
         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))),
@@ -713,8 +713,8 @@ pub fn noop_fold_ty_param<T: Folder>(tp: TyParam, fld: &mut T) -> TyParam {
     }
 }
 
-pub fn noop_fold_ty_params<T: Folder>(tps: OwnedSlice<TyParam>, fld: &mut T)
-                                      -> OwnedSlice<TyParam> {
+pub fn noop_fold_ty_params<T: Folder>(tps: P<[TyParam]>, fld: &mut T)
+                                      -> P<[TyParam]> {
     tps.move_map(|tp| fld.fold_ty_param(tp))
 }
 
@@ -870,8 +870,8 @@ pub fn noop_fold_mt<T: Folder>(MutTy {ty, mutbl}: MutTy, folder: &mut T) -> MutT
     }
 }
 
-pub fn noop_fold_opt_bounds<T: Folder>(b: Option<OwnedSlice<TyParamBound>>, folder: &mut T)
-                                       -> Option<OwnedSlice<TyParamBound>> {
+pub fn noop_fold_opt_bounds<T: Folder>(b: Option<TyParamBounds>, folder: &mut T)
+                                       -> Option<TyParamBounds> {
     b.map(|bounds| folder.fold_bounds(bounds))
 }
 
@@ -1203,6 +1203,9 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
             ExprCast(expr, ty) => {
                 ExprCast(folder.fold_expr(expr), folder.fold_ty(ty))
             }
+            ExprType(expr, ty) => {
+                ExprType(folder.fold_expr(expr), folder.fold_ty(ty))
+            }
             ExprAddrOf(m, ohs) => ExprAddrOf(m, folder.fold_expr(ohs)),
             ExprIf(cond, tr, fl) => {
                 ExprIf(folder.fold_expr(cond),
@@ -1303,8 +1306,13 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
                 inputs: inputs.move_map(|(c, input)| {
                     (c, folder.fold_expr(input))
                 }),
-                outputs: outputs.move_map(|(c, out, is_rw)| {
-                    (c, folder.fold_expr(out), is_rw)
+                outputs: outputs.move_map(|out| {
+                    InlineAsmOutput {
+                        constraint: out.constraint,
+                        expr: folder.fold_expr(out.expr),
+                        is_rw: out.is_rw,
+                        is_indirect: out.is_indirect,
+                    }
                 }),
                 asm: asm,
                 asm_str_style: asm_str_style,
index 4498120a78f79f9e9a941ae2518a82b871f56aa5..795f4044f6eb1be35c1bb8b74a49093b813be9c9 100644 (file)
 //!
 //! This API is completely unstable and subject to change.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "syntax"]
 #![unstable(feature = "rustc_private", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -26,8 +23,6 @@
        html_root_url = "https://doc.rust-lang.org/nightly/",
        test(attr(deny(warnings))))]
 
-#![cfg_attr(stage0, feature(rustc_attrs))]
-#![cfg_attr(stage0, allow(unused_attributes))]
 #![feature(associated_consts)]
 #![feature(filling_drop)]
 #![feature(libc)]
@@ -37,7 +32,6 @@
 #![feature(str_escape)]
 #![feature(unicode)]
 
-extern crate fmt_macros;
 extern crate serialize;
 extern crate term;
 extern crate libc;
@@ -46,17 +40,22 @@ extern crate libc;
 
 extern crate serialize as rustc_serialize; // used by deriving
 
-// A variant of 'try!' that panics on Err(FatalError). 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 normal try
+// 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
+// normal try.
+// Exported for syntax_ext, not meant for general use.
+#[macro_export]
 macro_rules! panictry {
     ($e:expr) => ({
         use std::result::Result::{Ok, Err};
-        use diagnostic::FatalError;
+        use $crate::errors::FatalError;
         match $e {
             Ok(e) => e,
-            Err(FatalError) => panic!(FatalError)
+            Err(mut e) => {
+                e.emit();
+                panic!(FatalError);
+            }
         }
     })
 }
@@ -79,6 +78,8 @@ pub mod diagnostics {
     pub mod metadata;
 }
 
+pub mod errors;
+
 pub mod syntax {
     pub use ext;
     pub use parse;
@@ -91,7 +92,6 @@ pub mod ast_util;
 pub mod attr;
 pub mod codemap;
 pub mod config;
-pub mod diagnostic;
 pub mod entry;
 pub mod feature_gate;
 pub mod fold;
@@ -110,21 +110,12 @@ pub mod print {
 }
 
 pub mod ext {
-    pub mod asm;
     pub mod base;
     pub mod build;
-    pub mod cfg;
-    pub mod concat;
-    pub mod concat_idents;
-    pub mod deriving;
-    pub mod env;
     pub mod expand;
-    pub mod format;
-    pub mod log_syntax;
     pub mod mtwt;
     pub mod quote;
     pub mod source_util;
-    pub mod trace_macros;
 
     pub mod tt {
         pub mod transcribe;
index 83369689a94de077efbce2a8ce81ff6e32c4751e..33a3d5785981ae50f55b9708fecef54b0e9331f2 100644 (file)
@@ -8,100 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::default::Default;
-use std::fmt;
-use std::iter::{IntoIterator, FromIterator};
-use std::ops::Deref;
-use std::slice;
-use std::vec;
-use serialize::{Encodable, Decodable, Encoder, Decoder};
-
-/// A non-growable owned slice. This is a separate type to allow the
-/// representation to change.
-#[derive(Hash, PartialEq, Eq, PartialOrd, Ord)]
-pub struct OwnedSlice<T> {
-    data: Box<[T]>
-}
-
-impl<T:fmt::Debug> fmt::Debug for OwnedSlice<T> {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        self.data.fmt(fmt)
-    }
-}
-
-impl<T> OwnedSlice<T> {
-    pub fn empty() -> OwnedSlice<T> {
-        OwnedSlice  { data: Box::new([]) }
-    }
-
-    #[inline(never)]
-    pub fn from_vec(v: Vec<T>) -> OwnedSlice<T> {
-        OwnedSlice { data: v.into_boxed_slice() }
-    }
-
-    #[inline(never)]
-    pub fn into_vec(self) -> Vec<T> {
-        self.data.into_vec()
-    }
-
-    pub fn as_slice<'a>(&'a self) -> &'a [T] {
-        &*self.data
-    }
-
-    pub fn move_iter(self) -> vec::IntoIter<T> {
-        self.into_vec().into_iter()
-    }
-
-    pub fn map<U, F: FnMut(&T) -> U>(&self, f: F) -> OwnedSlice<U> {
-        self.iter().map(f).collect()
-    }
-}
-
-impl<T> Deref for OwnedSlice<T> {
-    type Target = [T];
-
-    fn deref(&self) -> &[T] {
-        self.as_slice()
-    }
-}
-
-impl<T> Default for OwnedSlice<T> {
-    fn default() -> OwnedSlice<T> {
-        OwnedSlice::empty()
-    }
-}
-
-impl<T: Clone> Clone for OwnedSlice<T> {
-    fn clone(&self) -> OwnedSlice<T> {
-        OwnedSlice::from_vec(self.to_vec())
-    }
-}
-
-impl<T> FromIterator<T> for OwnedSlice<T> {
-    fn from_iter<I: IntoIterator<Item=T>>(iter: I) -> OwnedSlice<T> {
-        OwnedSlice::from_vec(iter.into_iter().collect())
-    }
-}
-
-impl<'a, T> IntoIterator for &'a OwnedSlice<T> {
-    type Item = &'a T;
-    type IntoIter = slice::Iter<'a, T>;
-    fn into_iter(self) -> Self::IntoIter {
-        self.data.into_iter()
-    }
-}
-
-impl<T: Encodable> Encodable for OwnedSlice<T> {
-    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-        Encodable::encode(&**self, s)
-    }
-}
-
-impl<T: Decodable> Decodable for OwnedSlice<T> {
-    fn decode<D: Decoder>(d: &mut D) -> Result<OwnedSlice<T>, D::Error> {
-        Ok(OwnedSlice::from_vec(match Decodable::decode(d) {
-            Ok(t) => t,
-            Err(e) => return Err(e)
-        }))
-    }
-}
+/// A non-growable owned slice.
+#[unstable(feature = "rustc_private", issue = "0")]
+#[rustc_deprecated(since = "1.7.0", reason = "use `ptr::P<[T]>` instead")]
+pub type OwnedSlice<T> = ::ptr::P<[T]>;
index 5df2478d4875809c8fe5c6e381ba44658952913f..cad9b45694b28b5bc4d144682fa1e8fd35a63b3f 100644 (file)
@@ -19,7 +19,7 @@ use ptr::P;
 
 impl<'a> Parser<'a> {
     /// Parse attributes that appear before an item
-    pub fn parse_outer_attributes(&mut self) -> PResult<Vec<ast::Attribute>> {
+    pub fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
         let mut attrs: Vec<ast::Attribute> = Vec::new();
         loop {
             debug!("parse_outer_attributes: self.token={:?}",
@@ -39,7 +39,7 @@ impl<'a> Parser<'a> {
                   return Err(self.fatal("expected outer comment"));
                 }
                 attrs.push(attr);
-                try!(self.bump());
+                self.bump();
               }
               _ => break
             }
@@ -51,24 +51,26 @@ impl<'a> Parser<'a> {
     ///
     /// If permit_inner is true, then a leading `!` indicates an inner
     /// attribute
-    pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<ast::Attribute> {
+    pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
         debug!("parse_attributes: permit_inner={:?} self.token={:?}",
                permit_inner, self.token);
         let (span, value, mut style) = match self.token {
             token::Pound => {
                 let lo = self.span.lo;
-                try!(self.bump());
+                self.bump();
 
                 if permit_inner { self.expected_tokens.push(TokenType::Token(token::Not)); }
                 let style = if self.token == token::Not {
-                    try!(self.bump());
+                    self.bump();
                     if !permit_inner {
                         let span = self.span;
-                        self.span_err(span,
-                                      "an inner attribute is not permitted in \
-                                       this context");
-                        self.fileline_help(span,
-                                       "place inner attribute at the top of the module or block");
+                        self.diagnostic().struct_span_err(span,
+                                                          "an inner attribute is not permitted in \
+                                                           this context")
+                                         .fileline_help(span,
+                                                        "place inner attribute at the top of \
+                                                         the module or block")
+                                         .emit()
                     }
                     ast::AttrStyle::Inner
                 } else {
@@ -89,7 +91,7 @@ impl<'a> Parser<'a> {
         };
 
         if permit_inner && self.token == token::Semi {
-            try!(self.bump());
+            self.bump();
             self.span_warn(span, "this inner attribute syntax is deprecated. \
                            The new syntax is `#![foo]`, with a bang and no semicolon");
             style = ast::AttrStyle::Inner;
@@ -111,7 +113,7 @@ impl<'a> Parser<'a> {
     /// terminated by a semicolon.
 
     /// matches inner_attrs*
-    pub fn parse_inner_attributes(&mut self) -> PResult<Vec<ast::Attribute>> {
+    pub fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
         let mut attrs: Vec<ast::Attribute> = vec![];
         loop {
             match self.token {
@@ -132,7 +134,7 @@ impl<'a> Parser<'a> {
                     let attr = attr::mk_sugared_doc_attr(attr::mk_attr_id(), str, lo, hi);
                     if attr.node.style == ast::AttrStyle::Inner {
                         attrs.push(attr);
-                        try!(self.bump());
+                        self.bump();
                     } else {
                         break;
                     }
@@ -146,7 +148,7 @@ impl<'a> Parser<'a> {
     /// matches meta_item = IDENT
     /// | IDENT = lit
     /// | IDENT meta_seq
-    pub fn parse_meta_item(&mut self) -> PResult<P<ast::MetaItem>> {
+    pub fn parse_meta_item(&mut self) -> PResult<'a, P<ast::MetaItem>> {
         let nt_meta = match self.token {
             token::Interpolated(token::NtMeta(ref e)) => {
                 Some(e.clone())
@@ -156,7 +158,7 @@ impl<'a> Parser<'a> {
 
         match nt_meta {
             Some(meta) => {
-                try!(self.bump());
+                self.bump();
                 return Ok(meta);
             }
             None => {}
@@ -167,7 +169,7 @@ impl<'a> Parser<'a> {
         let name = self.id_to_interned_str(ident);
         match self.token {
             token::Eq => {
-                try!(self.bump());
+                self.bump();
                 let lit = try!(self.parse_lit());
                 // FIXME #623 Non-string meta items are not serialized correctly;
                 // just forbid them for now
@@ -195,10 +197,10 @@ impl<'a> Parser<'a> {
     }
 
     /// matches meta_seq = ( COMMASEP(meta_item) )
-    fn parse_meta_seq(&mut self) -> PResult<Vec<P<ast::MetaItem>>> {
+    fn parse_meta_seq(&mut self) -> PResult<'a, Vec<P<ast::MetaItem>>> {
         self.parse_unspanned_seq(&token::OpenDelim(token::Paren),
                                  &token::CloseDelim(token::Paren),
                                  seq_sep_trailing_allowed(token::Comma),
-                                 |p| p.parse_meta_item())
+                                 |p: &mut Parser<'a>| p.parse_meta_item())
     }
 }
index e5e2c3a986db374b76b03711b51daf16f9eabbee..e336c98f03ca0c7d2795f2407776c12cca94c8b1 100644 (file)
@@ -12,7 +12,7 @@ pub use self::CommentStyle::*;
 
 use ast;
 use codemap::{BytePos, CharPos, CodeMap, Pos};
-use diagnostic;
+use errors;
 use parse::lexer::is_block_doc_comment;
 use parse::lexer::{StringReader, TokenAndSpan};
 use parse::lexer::{is_whitespace, Reader};
@@ -43,10 +43,8 @@ pub struct Comment {
 }
 
 pub fn is_doc_comment(s: &str) -> bool {
-    (s.starts_with("///") && super::is_doc_comment(s)) ||
-    s.starts_with("//!") ||
-    (s.starts_with("/**") && is_block_doc_comment(s)) ||
-    s.starts_with("/*!")
+    (s.starts_with("///") && super::is_doc_comment(s)) || s.starts_with("//!") ||
+    (s.starts_with("/**") && is_block_doc_comment(s)) || s.starts_with("/*!")
 }
 
 pub fn doc_comment_style(comment: &str) -> ast::AttrStyle {
@@ -64,18 +62,18 @@ pub fn strip_doc_comment_decoration(comment: &str) -> String {
         let mut i = 0;
         let mut j = lines.len();
         // first line of all-stars should be omitted
-        if !lines.is_empty() &&
-                lines[0].chars().all(|c| c == '*') {
+        if !lines.is_empty() && lines[0].chars().all(|c| c == '*') {
             i += 1;
         }
         while i < j && lines[i].trim().is_empty() {
             i += 1;
         }
         // like the first, a last line of all stars should be omitted
-        if j > i && lines[j - 1]
-                         .chars()
-                         .skip(1)
-                         .all(|c| c == '*') {
+        if j > i &&
+           lines[j - 1]
+               .chars()
+               .skip(1)
+               .all(|c| c == '*') {
             j -= 1;
         }
         while j > i && lines[j - 1].trim().is_empty() {
@@ -85,7 +83,7 @@ pub fn strip_doc_comment_decoration(comment: &str) -> String {
     }
 
     /// remove a "[ \t]*\*" block from each line, if possible
-    fn horizontal_trim(lines: Vec<String> ) -> Vec<String> {
+    fn horizontal_trim(lines: Vec<String>) -> Vec<String> {
         let mut i = usize::MAX;
         let mut can_trim = true;
         let mut first = true;
@@ -114,9 +112,9 @@ pub fn strip_doc_comment_decoration(comment: &str) -> String {
         }
 
         if can_trim {
-            lines.iter().map(|line| {
-                (&line[i + 1..line.len()]).to_string()
-            }).collect()
+            lines.iter()
+                 .map(|line| (&line[i + 1..line.len()]).to_string())
+                 .collect()
         } else {
             lines
         }
@@ -132,9 +130,9 @@ pub fn strip_doc_comment_decoration(comment: &str) -> String {
 
     if comment.starts_with("/*") {
         let lines = comment[3..comment.len() - 2]
-            .lines()
-            .map(|s| s.to_string())
-            .collect::<Vec<String> >();
+                        .lines()
+                        .map(|s| s.to_string())
+                        .collect::<Vec<String>>();
 
         let lines = vertical_trim(lines);
         let lines = horizontal_trim(lines);
@@ -154,8 +152,7 @@ fn push_blank_line_comment(rdr: &StringReader, comments: &mut Vec<Comment>) {
     });
 }
 
-fn consume_whitespace_counting_blank_lines(rdr: &mut StringReader,
-                                           comments: &mut Vec<Comment>) {
+fn consume_whitespace_counting_blank_lines(rdr: &mut StringReader, comments: &mut Vec<Comment>) {
     while is_whitespace(rdr.curr) && !rdr.is_eof() {
         if rdr.col == CharPos(0) && rdr.curr_is('\n') {
             push_blank_line_comment(rdr, &mut *comments);
@@ -165,19 +162,21 @@ fn consume_whitespace_counting_blank_lines(rdr: &mut StringReader,
 }
 
 
-fn read_shebang_comment(rdr: &mut StringReader, code_to_the_left: bool,
+fn read_shebang_comment(rdr: &mut StringReader,
+                        code_to_the_left: bool,
                         comments: &mut Vec<Comment>) {
     debug!(">>> shebang comment");
     let p = rdr.last_pos;
     debug!("<<< shebang comment");
     comments.push(Comment {
         style: if code_to_the_left { Trailing } else { Isolated },
-        lines: vec!(rdr.read_one_line_comment()),
-        pos: p
+        lines: vec![rdr.read_one_line_comment()],
+        pos: p,
     });
 }
 
-fn read_line_comments(rdr: &mut StringReader, code_to_the_left: bool,
+fn read_line_comments(rdr: &mut StringReader,
+                      code_to_the_left: bool,
                       comments: &mut Vec<Comment>) {
     debug!(">>> line comments");
     let p = rdr.last_pos;
@@ -197,7 +196,7 @@ fn read_line_comments(rdr: &mut StringReader, code_to_the_left: bool,
         comments.push(Comment {
             style: if code_to_the_left { Trailing } else { Isolated },
             lines: lines,
-            pos: p
+            pos: p,
         });
     }
 }
@@ -220,8 +219,7 @@ fn all_whitespace(s: &str, col: CharPos) -> Option<usize> {
     return Some(cursor);
 }
 
-fn trim_whitespace_prefix_and_push_line(lines: &mut Vec<String> ,
-                                        s: String, col: CharPos) {
+fn trim_whitespace_prefix_and_push_line(lines: &mut Vec<String>, s: String, col: CharPos) {
     let len = s.len();
     let s1 = match all_whitespace(&s[..], col) {
         Some(col) => {
@@ -239,7 +237,7 @@ fn trim_whitespace_prefix_and_push_line(lines: &mut Vec<String> ,
 
 fn read_block_comment(rdr: &mut StringReader,
                       code_to_the_left: bool,
-                      comments: &mut Vec<Comment> ) {
+                      comments: &mut Vec<Comment>) {
     debug!(">>> block comment");
     let p = rdr.last_pos;
     let mut lines: Vec<String> = Vec::new();
@@ -261,7 +259,7 @@ fn read_block_comment(rdr: &mut StringReader,
             rdr.bump();
         }
         if is_block_doc_comment(&curr_line[..]) {
-            return
+            return;
         }
         assert!(!curr_line.contains('\n'));
         lines.push(curr_line);
@@ -273,9 +271,7 @@ fn read_block_comment(rdr: &mut StringReader,
                 panic!(rdr.fatal("unterminated block comment"));
             }
             if rdr.curr_is('\n') {
-                trim_whitespace_prefix_and_push_line(&mut lines,
-                                                     curr_line,
-                                                     col);
+                trim_whitespace_prefix_and_push_line(&mut lines, curr_line, col);
                 curr_line = String::new();
                 rdr.bump();
             } else {
@@ -291,30 +287,36 @@ fn read_block_comment(rdr: &mut StringReader,
                         rdr.bump();
                         curr_line.push('/');
                         level -= 1;
-                    } else { rdr.bump(); }
+                    } else {
+                        rdr.bump();
+                    }
                 }
             }
         }
         if !curr_line.is_empty() {
-            trim_whitespace_prefix_and_push_line(&mut lines,
-                                                 curr_line,
-                                                 col);
+            trim_whitespace_prefix_and_push_line(&mut lines, curr_line, col);
         }
     }
 
-    let mut style = if code_to_the_left { Trailing } else { Isolated };
+    let mut style = if code_to_the_left {
+        Trailing
+    } else {
+        Isolated
+    };
     rdr.consume_non_eol_whitespace();
     if !rdr.is_eof() && !rdr.curr_is('\n') && lines.len() == 1 {
         style = Mixed;
     }
     debug!("<<< block comment");
-    comments.push(Comment {style: style, lines: lines, pos: p});
+    comments.push(Comment {
+        style: style,
+        lines: lines,
+        pos: p,
+    });
 }
 
 
-fn consume_comment(rdr: &mut StringReader,
-                   code_to_the_left: bool,
-                   comments: &mut Vec<Comment> ) {
+fn consume_comment(rdr: &mut StringReader, code_to_the_left: bool, comments: &mut Vec<Comment>) {
     debug!(">>> consume comment");
     if rdr.curr_is('/') && rdr.nextch_is('/') {
         read_line_comments(rdr, code_to_the_left, comments);
@@ -322,7 +324,9 @@ fn consume_comment(rdr: &mut StringReader,
         read_block_comment(rdr, code_to_the_left, comments);
     } else if rdr.curr_is('#') && rdr.nextch_is('!') {
         read_shebang_comment(rdr, code_to_the_left, comments);
-    } else { panic!(); }
+    } else {
+        panic!();
+    }
     debug!("<<< consume comment");
 }
 
@@ -334,10 +338,10 @@ pub struct Literal {
 
 // it appears this function is called only from pprust... that's
 // probably not a good thing.
-pub fn gather_comments_and_literals(span_diagnostic: &diagnostic::SpanHandler,
+pub fn gather_comments_and_literals(span_diagnostic: &errors::Handler,
                                     path: String,
                                     srdr: &mut Read)
-                                 -> (Vec<Comment>, Vec<Literal>) {
+                                    -> (Vec<Comment>, Vec<Literal>) {
     let mut src = Vec::new();
     srdr.read_to_end(&mut src).unwrap();
     let src = String::from_utf8(src).unwrap();
@@ -366,12 +370,15 @@ pub fn gather_comments_and_literals(span_diagnostic: &diagnostic::SpanHandler,
 
         let bstart = rdr.last_pos;
         rdr.next_token();
-        //discard, and look ahead; we're working with internal state
+        // discard, and look ahead; we're working with internal state
         let TokenAndSpan { tok, sp } = rdr.peek();
         if tok.is_lit() {
             rdr.with_str_from(bstart, |s| {
                 debug!("tok lit: {}", s);
-                literals.push(Literal {lit: s.to_string(), pos: sp.lo});
+                literals.push(Literal {
+                    lit: s.to_string(),
+                    pos: sp.lo,
+                });
             })
         } else {
             debug!("tok: {}", pprust::token_to_string(&tok));
@@ -386,31 +393,36 @@ pub fn gather_comments_and_literals(span_diagnostic: &diagnostic::SpanHandler,
 mod tests {
     use super::*;
 
-    #[test] fn test_block_doc_comment_1() {
+    #[test]
+    fn test_block_doc_comment_1() {
         let comment = "/**\n * Test \n **  Test\n *   Test\n*/";
         let stripped = strip_doc_comment_decoration(comment);
         assert_eq!(stripped, " Test \n*  Test\n   Test");
     }
 
-    #[test] fn test_block_doc_comment_2() {
+    #[test]
+    fn test_block_doc_comment_2() {
         let comment = "/**\n * Test\n *  Test\n*/";
         let stripped = strip_doc_comment_decoration(comment);
         assert_eq!(stripped, " Test\n  Test");
     }
 
-    #[test] fn test_block_doc_comment_3() {
+    #[test]
+    fn test_block_doc_comment_3() {
         let comment = "/**\n let a: *i32;\n *a = 5;\n*/";
         let stripped = strip_doc_comment_decoration(comment);
         assert_eq!(stripped, " let a: *i32;\n *a = 5;");
     }
 
-    #[test] fn test_block_doc_comment_4() {
+    #[test]
+    fn test_block_doc_comment_4() {
         let comment = "/*******************\n test\n *********************/";
         let stripped = strip_doc_comment_decoration(comment);
         assert_eq!(stripped, " test");
     }
 
-    #[test] fn test_line_doc_comment() {
+    #[test]
+    fn test_line_doc_comment() {
         let stripped = strip_doc_comment_decoration("/// test");
         assert_eq!(stripped, " test");
         let stripped = strip_doc_comment_decoration("///! test");
index cb2181a0831773c2426a437b5ba6df00878dedb3..3183dfbd954f57dff4642fe8dceffb231e7c39f9 100644 (file)
@@ -11,8 +11,7 @@
 use ast;
 use codemap::{BytePos, CharPos, CodeMap, Pos, Span};
 use codemap;
-use diagnostic::FatalError;
-use diagnostic::SpanHandler;
+use errors::{FatalError, Handler, DiagnosticBuilder};
 use ext::tt::transcribe::tt_next_token;
 use parse::token::str_to_ident;
 use parse::token;
@@ -43,8 +42,8 @@ pub trait Reader {
             match t.tok {
                 token::Whitespace | token::Comment | token::Shebang(_) => {
                     t = self.next_token();
-                },
-                _ => break
+                }
+                _ => break,
             }
         }
         t
@@ -58,7 +57,7 @@ pub struct TokenAndSpan {
 }
 
 pub struct StringReader<'a> {
-    pub span_diagnostic: &'a SpanHandler,
+    pub span_diagnostic: &'a Handler,
     /// The absolute offset within the codemap of the next character to read
     pub pos: BytePos,
     /// The absolute offset within the codemap of the last character read(curr)
@@ -68,17 +67,19 @@ pub struct StringReader<'a> {
     /// The last character to be read
     pub curr: Option<char>,
     pub filemap: Rc<codemap::FileMap>,
-    /* cached: */
+    // cached:
     pub peek_tok: token::Token,
     pub peek_span: Span,
 
     // cache a direct reference to the source text, so that we don't have to
     // retrieve it via `self.filemap.src.as_ref().unwrap()` all the time.
-    source_text: Rc<String>
+    source_text: Rc<String>,
 }
 
 impl<'a> Reader for StringReader<'a> {
-    fn is_eof(&self) -> bool { self.curr.is_none() }
+    fn is_eof(&self) -> bool {
+        self.curr.is_none()
+    }
     /// Return the next token. EFFECT: advances the string_reader.
     fn next_token(&mut self) -> TokenAndSpan {
         let ret_val = TokenAndSpan {
@@ -128,11 +129,13 @@ 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 SpanHandler,
-                       filemap: Rc<codemap::FileMap>) -> StringReader<'b> {
+    pub fn new_raw<'b>(span_diagnostic: &'b Handler,
+                       filemap: Rc<codemap::FileMap>)
+                       -> StringReader<'b> {
         if filemap.src.is_none() {
-            span_diagnostic.handler.bug(&format!("Cannot lex filemap without source: {}",
-                                                 filemap.name)[..]);
+            span_diagnostic.bug(&format!("Cannot lex filemap \
+                                          without source: {}",
+                                         filemap.name)[..]);
         }
 
         let source_text = (*filemap.src.as_ref().unwrap()).clone();
@@ -144,17 +147,18 @@ impl<'a> StringReader<'a> {
             col: CharPos(0),
             curr: Some('\n'),
             filemap: filemap,
-            /* dummy values; not read */
+            // dummy values; not read
             peek_tok: token::Eof,
             peek_span: codemap::DUMMY_SP,
-            source_text: source_text
+            source_text: source_text,
         };
         sr.bump();
         sr
     }
 
-    pub fn new<'b>(span_diagnostic: &'b SpanHandler,
-                   filemap: Rc<codemap::FileMap>) -> StringReader<'b> {
+    pub fn new<'b>(span_diagnostic: &'b Handler,
+                   filemap: Rc<codemap::FileMap>)
+                   -> StringReader<'b> {
         let mut sr = StringReader::new_raw(span_diagnostic, filemap);
         sr.advance_token();
         sr
@@ -174,10 +178,6 @@ impl<'a> StringReader<'a> {
         self.span_diagnostic.span_err(sp, m)
     }
 
-    /// Suggest some help with a given span.
-    pub fn help_span(&self, sp: Span, m: &str) {
-        self.span_diagnostic.span_help(sp, m)
-    }
 
     /// Report a fatal error spanning [`from_pos`, `to_pos`).
     fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> FatalError {
@@ -189,28 +189,53 @@ impl<'a> StringReader<'a> {
         self.err_span(codemap::mk_sp(from_pos, to_pos), m)
     }
 
-    /// Suggest some help spanning [`from_pos`, `to_pos`).
-    fn help_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) {
-        self.help_span(codemap::mk_sp(from_pos, to_pos), m)
-    }
-
     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
     /// escaped character to the error message
     fn fatal_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) -> FatalError {
         let mut m = m.to_string();
         m.push_str(": ");
-        for c in c.escape_default() { m.push(c) }
+        for c in c.escape_default() {
+            m.push(c)
+        }
         self.fatal_span_(from_pos, to_pos, &m[..])
     }
+    fn struct_fatal_span_char(&self,
+                              from_pos: BytePos,
+                              to_pos: BytePos,
+                              m: &str,
+                              c: char)
+                              -> DiagnosticBuilder<'a> {
+        let mut m = m.to_string();
+        m.push_str(": ");
+        for c in c.escape_default() {
+            m.push(c)
+        }
+        self.span_diagnostic.struct_span_fatal(codemap::mk_sp(from_pos, to_pos), &m[..])
+    }
 
     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
     /// escaped character to the error message
     fn err_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) {
         let mut m = m.to_string();
         m.push_str(": ");
-        for c in c.escape_default() { m.push(c) }
+        for c in c.escape_default() {
+            m.push(c)
+        }
         self.err_span_(from_pos, to_pos, &m[..]);
     }
+    fn struct_err_span_char(&self,
+                            from_pos: BytePos,
+                            to_pos: BytePos,
+                            m: &str,
+                            c: char)
+                            -> DiagnosticBuilder<'a> {
+        let mut m = m.to_string();
+        m.push_str(": ");
+        for c in c.escape_default() {
+            m.push(c)
+        }
+        self.span_diagnostic.struct_span_err(codemap::mk_sp(from_pos, to_pos), &m[..])
+    }
 
     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending the
     /// offending string to the error message
@@ -229,7 +254,7 @@ impl<'a> StringReader<'a> {
             Some(comment) => {
                 self.peek_span = comment.sp;
                 self.peek_tok = comment.tok;
-            },
+            }
             None => {
                 if self.is_eof() {
                     self.peek_tok = token::Eof;
@@ -237,8 +262,7 @@ impl<'a> StringReader<'a> {
                 } 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 = codemap::mk_sp(start_bytepos, self.last_pos);
                 };
             }
         }
@@ -251,8 +275,8 @@ impl<'a> StringReader<'a> {
     /// Calls `f` with a string slice of the source text spanning from `start`
     /// up to but excluding `self.last_pos`, meaning the slice does not include
     /// the character `self.curr`.
-    pub fn with_str_from<T, F>(&self, start: BytePos, f: F) -> T where
-        F: FnOnce(&str) -> T,
+    pub fn with_str_from<T, F>(&self, start: BytePos, f: F) -> T
+        where F: FnOnce(&str) -> T
     {
         self.with_str_from_to(start, self.last_pos, f)
     }
@@ -273,16 +297,14 @@ impl<'a> StringReader<'a> {
 
     /// Calls `f` with a string slice of the source text spanning from `start`
     /// up to but excluding `end`.
-    fn with_str_from_to<T, F>(&self, start: BytePos, end: BytePos, f: F) -> T where
-        F: FnOnce(&str) -> T,
+    fn with_str_from_to<T, F>(&self, start: BytePos, end: BytePos, f: F) -> T
+        where F: FnOnce(&str) -> T
     {
-        f(&self.source_text[self.byte_offset(start).to_usize()..
-                            self.byte_offset(end).to_usize()])
+        f(&self.source_text[self.byte_offset(start).to_usize()..self.byte_offset(end).to_usize()])
     }
 
     /// Converts CRLF to LF in the given string, raising an error on bare CR.
-    fn translate_crlf<'b>(&self, start: BytePos,
-                          s: &'b str, errmsg: &'b str) -> Cow<'b, str> {
+    fn translate_crlf<'b>(&self, start: BytePos, s: &'b str, errmsg: &'b str) -> Cow<'b, str> {
         let mut i = 0;
         while i < s.len() {
             let ch = char_at(s, i);
@@ -299,15 +321,21 @@ impl<'a> StringReader<'a> {
         }
         return s.into();
 
-        fn translate_crlf_(rdr: &StringReader, start: BytePos,
-                        s: &str, errmsg: &str, mut i: usize) -> String {
+        fn translate_crlf_(rdr: &StringReader,
+                           start: BytePos,
+                           s: &str,
+                           errmsg: &str,
+                           mut i: usize)
+                           -> String {
             let mut buf = String::with_capacity(s.len());
             let mut j = 0;
             while i < s.len() {
                 let ch = char_at(s, i);
                 let next = i + ch.len_utf8();
                 if ch == '\r' {
-                    if j < i { buf.push_str(&s[j..i]); }
+                    if j < i {
+                        buf.push_str(&s[j..i]);
+                    }
                     j = next;
                     if next >= s.len() || char_at(s, next) != '\n' {
                         let pos = start + BytePos(i as u32);
@@ -317,7 +345,9 @@ impl<'a> StringReader<'a> {
                 }
                 i = next;
             }
-            if j < s.len() { buf.push_str(&s[j..]); }
+            if j < s.len() {
+                buf.push_str(&s[j..]);
+            }
             buf
         }
     }
@@ -366,7 +396,9 @@ impl<'a> StringReader<'a> {
     pub fn nextnextch(&self) -> Option<char> {
         let offset = self.byte_offset(self.pos).to_usize();
         let s = &self.source_text[..];
-        if offset >= s.len() { return None }
+        if offset >= s.len() {
+            return None;
+        }
         let next = offset + char_at(s, offset).len_utf8();
         if next < s.len() {
             Some(char_at(s, next))
@@ -382,7 +414,7 @@ impl<'a> StringReader<'a> {
     /// Eats <XID_start><XID_continue>*, if possible.
     fn scan_optional_raw_name(&mut self) -> Option<ast::Name> {
         if !ident_start(self.curr) {
-            return None
+            return None;
         }
         let start = self.last_pos;
         while ident_continue(self.curr) {
@@ -405,10 +437,11 @@ impl<'a> StringReader<'a> {
             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");
+                                                  "called consume_any_line_comment, but there \
+                                                   was whitespace");
                 }
-            },
-            None => { }
+            }
+            None => {}
         }
 
         if self.curr_is('/') {
@@ -431,13 +464,14 @@ impl<'a> StringReader<'a> {
                             '\r' => {
                                 if self.nextch_is('\n') {
                                     // CRLF
-                                    break
+                                    break;
                                 } else if doc_comment {
-                                    self.err_span_(self.last_pos, self.pos,
+                                    self.err_span_(self.last_pos,
+                                                   self.pos,
                                                    "bare CR not allowed in doc-comment");
                                 }
                             }
-                            _ => ()
+                            _ => (),
                         }
                         self.bump();
                     }
@@ -453,21 +487,22 @@ impl<'a> StringReader<'a> {
 
                             Some(TokenAndSpan {
                                 tok: tok,
-                                sp: codemap::mk_sp(start_bpos, self.last_pos)
+                                sp: codemap::mk_sp(start_bpos, self.last_pos),
                             })
                         })
                     } else {
                         Some(TokenAndSpan {
                             tok: token::Comment,
-                            sp: codemap::mk_sp(start_bpos, self.last_pos)
+                            sp: codemap::mk_sp(start_bpos, self.last_pos),
                         })
-                    }
+                    };
                 }
                 Some('*') => {
-                    self.bump(); self.bump();
+                    self.bump();
+                    self.bump();
                     self.scan_block_comment()
                 }
-                _ => None
+                _ => None,
             }
         } else if self.curr_is('#') {
             if self.nextch_is('!') {
@@ -486,10 +521,12 @@ impl<'a> StringReader<'a> {
                 if loc.line == 1 && loc.col == CharPos(0) {
                     // FIXME: Add shebang "token", return it
                     let start = self.last_pos;
-                    while !self.curr_is('\n') && !self.is_eof() { self.bump(); }
+                    while !self.curr_is('\n') && !self.is_eof() {
+                        self.bump();
+                    }
                     return Some(TokenAndSpan {
                         tok: token::Shebang(self.name_from(start)),
-                        sp: codemap::mk_sp(start, self.last_pos)
+                        sp: codemap::mk_sp(start, self.last_pos),
                     });
                 }
             }
@@ -509,18 +546,20 @@ impl<'a> StringReader<'a> {
                 let c = self.scan_comment();
                 debug!("scanning a comment {:?}", c);
                 c
-            },
+            }
             c if is_whitespace(Some(c)) => {
                 let start_bpos = self.last_pos;
-                while is_whitespace(self.curr) { self.bump(); }
+                while is_whitespace(self.curr) {
+                    self.bump();
+                }
                 let c = Some(TokenAndSpan {
                     tok: token::Whitespace,
-                    sp: codemap::mk_sp(start_bpos, self.last_pos)
+                    sp: codemap::mk_sp(start_bpos, self.last_pos),
                 });
                 debug!("scanning whitespace: {:?}", c);
                 c
-            },
-            _ => None
+            }
+            _ => None,
         }
     }
 
@@ -555,7 +594,7 @@ impl<'a> StringReader<'a> {
                 '\r' => {
                     has_cr = true;
                 }
-                _ => ()
+                _ => (),
             }
             self.bump();
         }
@@ -564,17 +603,20 @@ impl<'a> StringReader<'a> {
             // but comments with only "*"s between two "/"s are not
             let tok = if is_block_doc_comment(string) {
                 let string = if has_cr {
-                    self.translate_crlf(start_bpos, string,
+                    self.translate_crlf(start_bpos,
+                                        string,
                                         "bare CR not allowed in block doc-comment")
-                } else { string.into() };
+                } else {
+                    string.into()
+                };
                 token::DocComment(token::intern(&string[..]))
             } else {
                 token::Comment
             };
 
-            Some(TokenAndSpan{
+            Some(TokenAndSpan {
                 tok: tok,
-                sp: codemap::mk_sp(start_bpos, self.last_pos)
+                sp: codemap::mk_sp(start_bpos, self.last_pos),
             })
         })
     }
@@ -590,23 +632,27 @@ impl<'a> StringReader<'a> {
         let mut len = 0;
         loop {
             let c = self.curr;
-            if c == Some('_') { debug!("skipping a _"); self.bump(); continue; }
+            if c == Some('_') {
+                debug!("skipping a _");
+                self.bump();
+                continue;
+            }
             match c.and_then(|cc| cc.to_digit(scan_radix)) {
                 Some(_) => {
                     debug!("{:?} in scan_digits", c);
                     // check that the hypothetical digit is actually
                     // in range for the true radix
                     if c.unwrap().to_digit(real_radix).is_none() {
-                        self.err_span_(self.last_pos, self.pos,
-                                       &format!("invalid digit for a base {} literal",
-                                                real_radix));
+                        self.err_span_(self.last_pos,
+                                       self.pos,
+                                       &format!("invalid digit for a base {} literal", real_radix));
                     }
                     len += 1;
                     self.bump();
                 }
-                _ => return len
+                _ => return len,
             }
-        };
+        }
     }
 
     /// Lex a LIT_INTEGER or a LIT_FLOAT
@@ -619,9 +665,21 @@ impl<'a> StringReader<'a> {
 
         if c == '0' {
             match self.curr.unwrap_or('\0') {
-                'b' => { self.bump(); base = 2; num_digits = self.scan_digits(2, 10); }
-                'o' => { self.bump(); base = 8; num_digits = self.scan_digits(8, 10); }
-                'x' => { self.bump(); base = 16; num_digits = self.scan_digits(16, 16); }
+                'b' => {
+                    self.bump();
+                    base = 2;
+                    num_digits = self.scan_digits(2, 10);
+                }
+                'o' => {
+                    self.bump();
+                    base = 8;
+                    num_digits = self.scan_digits(8, 10);
+                }
+                'x' => {
+                    self.bump();
+                    base = 16;
+                    num_digits = self.scan_digits(16, 16);
+                }
                 '0'...'9' | '_' | '.' => {
                     num_digits = self.scan_digits(10, 10) + 1;
                 }
@@ -637,15 +695,19 @@ impl<'a> StringReader<'a> {
         }
 
         if num_digits == 0 {
-            self.err_span_(start_bpos, self.last_pos, "no valid digits found for number");
+            self.err_span_(start_bpos,
+                           self.last_pos,
+                           "no valid digits found for number");
             return token::Integer(token::intern("0"));
         }
 
         // might be a float, but don't be greedy if this is actually an
         // integer literal followed by field/method access or a range pattern
         // (`0..2` and `12.foo()`)
-        if self.curr_is('.') && !self.nextch_is('.') && !self.nextch().unwrap_or('\0')
-                                                             .is_xid_start() {
+        if self.curr_is('.') && !self.nextch_is('.') &&
+           !self.nextch()
+                .unwrap_or('\0')
+                .is_xid_start() {
             // might have stuff after the ., and if it does, it needs to start
             // with a number
             self.bump();
@@ -671,11 +733,7 @@ impl<'a> StringReader<'a> {
 
     /// Scan over `n_digits` hex digits, stopping at `delim`, reporting an
     /// error if too many or too few digits are encountered.
-    fn scan_hex_digits(&mut self,
-                       n_digits: usize,
-                       delim: char,
-                       below_0x7f_only: bool)
-                       -> bool {
+    fn scan_hex_digits(&mut self, n_digits: usize, delim: char, below_0x7f_only: bool) -> bool {
         debug!("scanning {} digits until {:?}", n_digits, delim);
         let start_bpos = self.last_pos;
         let mut accum_int = 0;
@@ -690,15 +748,19 @@ impl<'a> StringReader<'a> {
             }
             if self.curr_is(delim) {
                 let last_bpos = self.last_pos;
-                self.err_span_(start_bpos, last_bpos, "numeric character escape is too short");
+                self.err_span_(start_bpos,
+                               last_bpos,
+                               "numeric character escape is too short");
                 valid = false;
                 break;
             }
             let c = self.curr.unwrap_or('\x00');
             accum_int *= 16;
             accum_int += c.to_digit(16).unwrap_or_else(|| {
-                self.err_span_char(self.last_pos, self.pos,
-                              "invalid character in numeric character escape", c);
+                self.err_span_char(self.last_pos,
+                                   self.pos,
+                                   "invalid character in numeric character escape",
+                                   c);
 
                 valid = false;
                 0
@@ -709,8 +771,8 @@ impl<'a> StringReader<'a> {
         if below_0x7f_only && accum_int >= 0x80 {
             self.err_span_(start_bpos,
                            self.last_pos,
-                           "this form of character escape may only be used \
-                            with characters in the range [\\x00-\\x7f]");
+                           "this form of character escape may only be used with characters in \
+                            the range [\\x00-\\x7f]");
             valid = false;
         }
 
@@ -729,8 +791,12 @@ impl<'a> StringReader<'a> {
     /// `start` is the position of `first_source_char`, which is already consumed.
     ///
     /// Returns true if there was a valid char/byte, false otherwise.
-    fn scan_char_or_byte(&mut self, start: BytePos, first_source_char: char,
-                         ascii_only: bool, delim: char) -> bool {
+    fn scan_char_or_byte(&mut self,
+                         start: BytePos,
+                         first_source_char: char,
+                         ascii_only: bool,
+                         delim: char)
+                         -> bool {
         match first_source_char {
             '\\' => {
                 // '\X' for some X must be a character constant:
@@ -738,7 +804,7 @@ impl<'a> StringReader<'a> {
                 let escaped_pos = self.last_pos;
                 self.bump();
                 match escaped {
-                    None => {},  // EOF here is an error that will be checked later.
+                    None => {}  // EOF here is an error that will be checked later.
                     Some(e) => {
                         return match e {
                             'n' | 'r' | 't' | '\\' | '\'' | '"' | '0' => true,
@@ -747,17 +813,20 @@ impl<'a> StringReader<'a> {
                                 let valid = if self.curr_is('{') {
                                     self.scan_unicode_escape(delim) && !ascii_only
                                 } else {
-                                    self.err_span_(start, self.last_pos,
-                                        "incorrect unicode escape sequence");
-                                    self.help_span_(start, self.last_pos,
-                                        "format of unicode escape sequences is `\\u{…}`");
+                                    let span = codemap::mk_sp(start, self.last_pos);
+                                    self.span_diagnostic
+                                        .struct_span_err(span, "incorrect unicode escape sequence")
+                                        .span_help(span,
+                                                   "format of unicode escape sequences is \
+                                                    `\\u{…}`")
+                                        .emit();
                                     false
                                 };
                                 if ascii_only {
-                                    self.err_span_(start, self.last_pos,
-                                        "unicode escape sequences cannot be used as a byte or in \
-                                        a byte string"
-                                    );
+                                    self.err_span_(start,
+                                                   self.last_pos,
+                                                   "unicode escape sequences cannot be used as a \
+                                                    byte or in a byte string");
                                 }
                                 valid
 
@@ -765,28 +834,34 @@ impl<'a> StringReader<'a> {
                             '\n' if delim == '"' => {
                                 self.consume_whitespace();
                                 true
-                            },
+                            }
                             '\r' if delim == '"' && self.curr_is('\n') => {
                                 self.consume_whitespace();
                                 true
                             }
                             c => {
                                 let last_pos = self.last_pos;
-                                self.err_span_char(
-                                    escaped_pos, last_pos,
-                                    if ascii_only { "unknown byte escape" }
-                                    else { "unknown character escape" },
-                                    c);
+                                let mut err = self.struct_err_span_char(escaped_pos,
+                                                                        last_pos,
+                                                                        if ascii_only {
+                                                                            "unknown byte escape"
+                                                                        } else {
+                                                                            "unknown character \
+                                                                             escape"
+                                                                        },
+                                                                        c);
                                 if e == '\r' {
-                                    self.help_span_(escaped_pos, last_pos,
-                                        "this is an isolated carriage return; consider checking \
-                                         your editor and version control settings")
+                                    err.span_help(codemap::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 {
-                                    self.help_span_(escaped_pos, last_pos,
-                                        "if used in a formatting string, \
-                                        curly braces are escaped with `{{` and `}}`")
+                                    err.span_help(codemap::mk_sp(escaped_pos, last_pos),
+                                                  "if used in a formatting string, curly braces \
+                                                   are escaped with `{{` and `}}`");
                                 }
+                                err.emit();
                                 false
                             }
                         }
@@ -795,11 +870,14 @@ impl<'a> StringReader<'a> {
             }
             '\t' | '\n' | '\r' | '\'' if delim == '\'' => {
                 let last_pos = self.last_pos;
-                self.err_span_char(
-                    start, last_pos,
-                    if ascii_only { "byte constant must be escaped" }
-                    else { "character constant must be escaped" },
-                    first_source_char);
+                self.err_span_char(start,
+                                   last_pos,
+                                   if ascii_only {
+                                       "byte constant must be escaped"
+                                   } else {
+                                       "character constant must be escaped"
+                                   },
+                                   first_source_char);
                 return false;
             }
             '\r' => {
@@ -807,18 +885,22 @@ impl<'a> StringReader<'a> {
                     self.bump();
                     return true;
                 } else {
-                    self.err_span_(start, self.last_pos,
+                    self.err_span_(start,
+                                   self.last_pos,
                                    "bare CR not allowed in string, use \\r instead");
                     return false;
                 }
             }
-            _ => if ascii_only && first_source_char > '\x7F' {
-                let last_pos = self.last_pos;
-                self.err_span_char(
-                    start, last_pos,
-                    "byte constant must be ASCII. \
-                     Use a \\xHH escape for a non-ASCII byte", first_source_char);
-                return false;
+            _ => {
+                if ascii_only && first_source_char > '\x7F' {
+                    let last_pos = self.last_pos;
+                    self.err_span_char(start,
+                                       last_pos,
+                                       "byte constant must be ASCII. Use a \\xHH escape for a \
+                                        non-ASCII byte",
+                                       first_source_char);
+                    return false;
+                }
             }
         }
         true
@@ -839,18 +921,22 @@ impl<'a> StringReader<'a> {
             let c = match self.curr {
                 Some(c) => c,
                 None => {
-                    panic!(self.fatal_span_(start_bpos, self.last_pos,
+                    panic!(self.fatal_span_(start_bpos,
+                                            self.last_pos,
                                             "unterminated unicode escape (found EOF)"));
                 }
             };
             accum_int *= 16;
             accum_int += c.to_digit(16).unwrap_or_else(|| {
                 if c == delim {
-                    panic!(self.fatal_span_(self.last_pos, self.pos,
+                    panic!(self.fatal_span_(self.last_pos,
+                                            self.pos,
                                             "unterminated unicode escape (needed a `}`)"));
                 } else {
-                    self.err_span_char(self.last_pos, self.pos,
-                                   "invalid character in unicode escape", c);
+                    self.err_span_char(self.last_pos,
+                                       self.pos,
+                                       "invalid character in unicode escape",
+                                       c);
                 }
                 valid = false;
                 0
@@ -860,13 +946,16 @@ impl<'a> StringReader<'a> {
         }
 
         if count > 6 {
-            self.err_span_(start_bpos, self.last_pos,
-                          "overlong unicode escape (can have at most 6 hex digits)");
+            self.err_span_(start_bpos,
+                           self.last_pos,
+                           "overlong unicode escape (can have at most 6 hex digits)");
             valid = false;
         }
 
         if valid && (char::from_u32(accum_int).is_none() || count == 0) {
-            self.err_span_(start_bpos, self.last_pos, "invalid unicode character escape");
+            self.err_span_(start_bpos,
+                           self.last_pos,
+                           "invalid unicode character escape");
             valid = false;
         }
 
@@ -882,7 +971,9 @@ impl<'a> StringReader<'a> {
                 self.bump();
             }
             if self.scan_digits(10, 10) == 0 {
-                self.err_span_(self.last_pos, self.pos, "expected at least one digit in exponent")
+                self.err_span_(self.last_pos,
+                               self.pos,
+                               "expected at least one digit in exponent")
             }
         }
     }
@@ -891,11 +982,22 @@ impl<'a> StringReader<'a> {
     /// error if it isn't.
     fn check_float_base(&mut self, start_bpos: BytePos, last_bpos: BytePos, base: usize) {
         match base {
-            16 => self.err_span_(start_bpos, last_bpos, "hexadecimal float literal is not \
-                                   supported"),
-            8 => self.err_span_(start_bpos, last_bpos, "octal float literal is not supported"),
-            2 => self.err_span_(start_bpos, last_bpos, "binary float literal is not supported"),
-            _   => ()
+            16 => {
+                self.err_span_(start_bpos,
+                               last_bpos,
+                               "hexadecimal float literal is not supported")
+            }
+            8 => {
+                self.err_span_(start_bpos,
+                               last_bpos,
+                               "octal float literal is not supported")
+            }
+            2 => {
+                self.err_span_(start_bpos,
+                               last_bpos,
+                               "binary float literal is not supported")
+            }
+            _ => (),
         }
     }
 
@@ -913,14 +1015,18 @@ impl<'a> StringReader<'a> {
     /// token, and updates the interner
     fn next_token_inner(&mut self) -> token::Token {
         let c = self.curr;
-        if ident_start(c) && match (c.unwrap(), self.nextch(), self.nextnextch()) {
+        if ident_start(c) &&
+           match (c.unwrap(), self.nextch(), self.nextnextch()) {
             // Note: r as in r" or r#" is part of a raw string literal,
             // b as in b' is part of a byte literal.
             // They are not identifiers, and are handled further down.
-           ('r', Some('"'), _) | ('r', Some('#'), _) |
-           ('b', Some('"'), _) | ('b', Some('\''), _) |
-           ('b', Some('r'), Some('"')) | ('b', Some('r'), Some('#')) => false,
-           _ => true
+            ('r', Some('"'), _) |
+            ('r', Some('#'), _) |
+            ('b', Some('"'), _) |
+            ('b', Some('\''), _) |
+            ('b', Some('r'), Some('"')) |
+            ('b', Some('r'), Some('#')) => false,
+            _ => true,
         } {
             let start = self.last_pos;
             while ident_continue(self.curr) {
@@ -945,294 +1051,398 @@ impl<'a> StringReader<'a> {
             let num = self.scan_number(c.unwrap());
             let suffix = self.scan_optional_raw_name();
             debug!("next_token_inner: scanned number {:?}, {:?}", num, suffix);
-            return token::Literal(num, suffix)
+            return token::Literal(num, suffix);
         }
 
         match c.expect("next_token_inner called at EOF") {
-          // One-byte tokens.
-          ';' => { self.bump(); return token::Semi; }
-          ',' => { self.bump(); return token::Comma; }
-          '.' => {
-              self.bump();
-              return if self.curr_is('.') {
-                  self.bump();
-                  if self.curr_is('.') {
-                      self.bump();
-                      token::DotDotDot
-                  } else {
-                      token::DotDot
-                  }
-              } else {
-                  token::Dot
-              };
-          }
-          '(' => { self.bump(); return token::OpenDelim(token::Paren); }
-          ')' => { self.bump(); return token::CloseDelim(token::Paren); }
-          '{' => { self.bump(); return token::OpenDelim(token::Brace); }
-          '}' => { self.bump(); return token::CloseDelim(token::Brace); }
-          '[' => { self.bump(); return token::OpenDelim(token::Bracket); }
-          ']' => { self.bump(); return token::CloseDelim(token::Bracket); }
-          '@' => { self.bump(); return token::At; }
-          '#' => { self.bump(); return token::Pound; }
-          '~' => { self.bump(); return token::Tilde; }
-          '?' => { self.bump(); return token::Question; }
-          ':' => {
-            self.bump();
-            if self.curr_is(':') {
+            // One-byte tokens.
+            ';' => {
                 self.bump();
-                return token::ModSep;
-            } else {
-                return token::Colon;
+                return token::Semi;
+            }
+            ',' => {
+                self.bump();
+                return token::Comma;
+            }
+            '.' => {
+                self.bump();
+                return if self.curr_is('.') {
+                    self.bump();
+                    if self.curr_is('.') {
+                        self.bump();
+                        token::DotDotDot
+                    } else {
+                        token::DotDot
+                    }
+                } else {
+                    token::Dot
+                };
+            }
+            '(' => {
+                self.bump();
+                return token::OpenDelim(token::Paren);
+            }
+            ')' => {
+                self.bump();
+                return token::CloseDelim(token::Paren);
+            }
+            '{' => {
+                self.bump();
+                return token::OpenDelim(token::Brace);
+            }
+            '}' => {
+                self.bump();
+                return token::CloseDelim(token::Brace);
+            }
+            '[' => {
+                self.bump();
+                return token::OpenDelim(token::Bracket);
+            }
+            ']' => {
+                self.bump();
+                return token::CloseDelim(token::Bracket);
+            }
+            '@' => {
+                self.bump();
+                return token::At;
+            }
+            '#' => {
+                self.bump();
+                return token::Pound;
+            }
+            '~' => {
+                self.bump();
+                return token::Tilde;
+            }
+            '?' => {
+                self.bump();
+                return token::Question;
+            }
+            ':' => {
+                self.bump();
+                if self.curr_is(':') {
+                    self.bump();
+                    return token::ModSep;
+                } else {
+                    return token::Colon;
+                }
             }
-          }
 
-          '$' => { self.bump(); return token::Dollar; }
+            '$' => {
+                self.bump();
+                return token::Dollar;
+            }
 
-          // Multi-byte tokens.
-          '=' => {
-            self.bump();
-            if self.curr_is('=') {
+            // Multi-byte tokens.
+            '=' => {
                 self.bump();
-                return token::EqEq;
-            } else if self.curr_is('>') {
+                if self.curr_is('=') {
+                    self.bump();
+                    return token::EqEq;
+                } else if self.curr_is('>') {
+                    self.bump();
+                    return token::FatArrow;
+                } else {
+                    return token::Eq;
+                }
+            }
+            '!' => {
                 self.bump();
-                return token::FatArrow;
-            } else {
-                return token::Eq;
+                if self.curr_is('=') {
+                    self.bump();
+                    return token::Ne;
+                } else {
+                    return token::Not;
+                }
             }
-          }
-          '!' => {
-            self.bump();
-            if self.curr_is('=') {
+            '<' => {
                 self.bump();
-                return token::Ne;
-            } else { return token::Not; }
-          }
-          '<' => {
-            self.bump();
-            match self.curr.unwrap_or('\x00') {
-              '=' => { self.bump(); return token::Le; }
-              '<' => { return self.binop(token::Shl); }
-              '-' => {
+                match self.curr.unwrap_or('\x00') {
+                    '=' => {
+                        self.bump();
+                        return token::Le;
+                    }
+                    '<' => {
+                        return self.binop(token::Shl);
+                    }
+                    '-' => {
+                        self.bump();
+                        match self.curr.unwrap_or('\x00') {
+                            _ => {
+                                return token::LArrow;
+                            }
+                        }
+                    }
+                    _ => {
+                        return token::Lt;
+                    }
+                }
+            }
+            '>' => {
                 self.bump();
                 match self.curr.unwrap_or('\x00') {
-                  _ => { return token::LArrow; }
+                    '=' => {
+                        self.bump();
+                        return token::Ge;
+                    }
+                    '>' => {
+                        return self.binop(token::Shr);
+                    }
+                    _ => {
+                        return token::Gt;
+                    }
                 }
-              }
-              _ => { return token::Lt; }
             }
-          }
-          '>' => {
-            self.bump();
-            match self.curr.unwrap_or('\x00') {
-              '=' => { self.bump(); return token::Ge; }
-              '>' => { return self.binop(token::Shr); }
-              _ => { return token::Gt; }
-            }
-          }
-          '\'' => {
-            // Either a character constant 'a' OR a lifetime name 'abc
-            self.bump();
-            let start = self.last_pos;
+            '\'' => {
+                // Either a character constant 'a' OR a lifetime name 'abc
+                let start_with_quote = self.last_pos;
+                self.bump();
+                let start = self.last_pos;
 
-            // the eof will be picked up by the final `'` check below
-            let c2 = self.curr.unwrap_or('\x00');
-            self.bump();
+                // the eof will be picked up by the final `'` check below
+                let c2 = self.curr.unwrap_or('\x00');
+                self.bump();
 
-            // If the character is an ident start not followed by another single
-            // quote, then this is a lifetime name:
-            if ident_start(Some(c2)) && !self.curr_is('\'') {
-                while ident_continue(self.curr) {
-                    self.bump();
-                }
+                // If the character is an ident start not followed by another single
+                // quote, then this is a lifetime name:
+                if ident_start(Some(c2)) && !self.curr_is('\'') {
+                    while ident_continue(self.curr) {
+                        self.bump();
+                    }
+                    // lifetimes shouldn't end with a single quote
+                    // if we find one, then this is an invalid character literal
+                    if self.curr_is('\'') {
+                        panic!(self.fatal_span_verbose(
+                               start_with_quote, self.pos,
+                               String::from("character literal may only contain one codepoint")));
 
-                // Include the leading `'` in the real identifier, for macro
-                // expansion purposes. See #12512 for the gory details of why
-                // this is necessary.
-                let ident = self.with_str_from(start, |lifetime_name| {
-                    str_to_ident(&format!("'{}", lifetime_name))
-                });
+                    }
 
-                // Conjure up a "keyword checking ident" to make sure that
-                // the lifetime name is not a keyword.
-                let keyword_checking_ident =
-                    self.with_str_from(start, |lifetime_name| {
+                    // Include the leading `'` in the real identifier, for macro
+                    // expansion purposes. See #12512 for the gory details of why
+                    // this is necessary.
+                    let ident = self.with_str_from(start, |lifetime_name| {
+                        str_to_ident(&format!("'{}", lifetime_name))
+                    });
+
+                    // Conjure up a "keyword checking ident" to make sure that
+                    // the lifetime name is not a keyword.
+                    let keyword_checking_ident = self.with_str_from(start, |lifetime_name| {
                         str_to_ident(lifetime_name)
                     });
-                let keyword_checking_token =
-                    &token::Ident(keyword_checking_ident, token::Plain);
-                let last_bpos = self.last_pos;
-                if keyword_checking_token.is_keyword(token::keywords::SelfValue) {
-                    self.err_span_(start,
-                                   last_bpos,
-                                   "invalid lifetime name: 'self \
-                                    is no longer a special lifetime");
-                } else if keyword_checking_token.is_any_keyword() &&
-                    !keyword_checking_token.is_keyword(token::keywords::Static)
-                {
-                    self.err_span_(start,
-                                   last_bpos,
-                                   "invalid lifetime name");
+                    let keyword_checking_token = &token::Ident(keyword_checking_ident,
+                                                               token::Plain);
+                    let last_bpos = self.last_pos;
+                    if keyword_checking_token.is_keyword(token::keywords::SelfValue) {
+                        self.err_span_(start,
+                                       last_bpos,
+                                       "invalid lifetime name: 'self is no longer a special \
+                                        lifetime");
+                    } else if keyword_checking_token.is_any_keyword() &&
+                       !keyword_checking_token.is_keyword(token::keywords::Static) {
+                        self.err_span_(start, last_bpos, "invalid lifetime name");
+                    }
+
+                    return token::Lifetime(ident);
                 }
-                return token::Lifetime(ident);
-            }
 
-            // Otherwise it is a character constant:
-            let valid = self.scan_char_or_byte(start, c2, /* ascii_only = */ false, '\'');
-            if !self.curr_is('\'') {
-                let last_bpos = self.last_pos;
-                panic!(self.fatal_span_verbose(
-                        // Byte offsetting here is okay because the
-                        // character before position `start` is an
-                        // ascii single quote.
-                        start - BytePos(1), last_bpos,
+                let valid = self.scan_char_or_byte(start,
+                                                   c2,
+                                                   // ascii_only =
+                                                   false,
+                                                   '\'');
 
-                        String::from("character literal may only contain one codepoint")));
-            }
-            let id = if valid { self.name_from(start) } else { token::intern("0") };
-            self.bump(); // advance curr past token
-            let suffix = self.scan_optional_raw_name();
-            return token::Literal(token::Char(id), suffix);
-          }
-          'b' => {
-            self.bump();
-            let lit = match self.curr {
-                Some('\'') => self.scan_byte(),
-                Some('"') => self.scan_byte_string(),
-                Some('r') => self.scan_raw_byte_string(),
-                _ => unreachable!()  // Should have been a token::Ident above.
-            };
-            let suffix = self.scan_optional_raw_name();
-            return token::Literal(lit, suffix);
-          }
-          '"' => {
-            let start_bpos = self.last_pos;
-            let mut valid = true;
-            self.bump();
-            while !self.curr_is('"') {
-                if self.is_eof() {
-                    let last_bpos = self.last_pos;
-                    panic!(self.fatal_span_(start_bpos,
-                                            last_bpos,
-                                            "unterminated double quote string"));
+                if !self.curr_is('\'') {
+                    panic!(self.fatal_span_verbose(
+                           start_with_quote, self.last_pos,
+                           String::from("character literal may only contain one codepoint")));
                 }
 
-                let ch_start = self.last_pos;
-                let ch = self.curr.unwrap();
-                self.bump();
-                valid &= self.scan_char_or_byte(ch_start, ch, /* ascii_only = */ false, '"');
+                let id = if valid {
+                    self.name_from(start)
+                } else {
+                    token::intern("0")
+                };
+                self.bump(); // advance curr past token
+                let suffix = self.scan_optional_raw_name();
+                return token::Literal(token::Char(id), suffix);
             }
-            // adjust for the ASCII " at the start of the literal
-            let id = if valid { self.name_from(start_bpos + BytePos(1)) }
-                     else { token::intern("??") };
-            self.bump();
-            let suffix = self.scan_optional_raw_name();
-            return token::Literal(token::Str_(id), suffix);
-          }
-          'r' => {
-            let start_bpos = self.last_pos;
-            self.bump();
-            let mut hash_count = 0;
-            while self.curr_is('#') {
+            'b' => {
                 self.bump();
-                hash_count += 1;
+                let lit = match self.curr {
+                    Some('\'') => self.scan_byte(),
+                    Some('"') => self.scan_byte_string(),
+                    Some('r') => self.scan_raw_byte_string(),
+                    _ => unreachable!(),  // Should have been a token::Ident above.
+                };
+                let suffix = self.scan_optional_raw_name();
+                return token::Literal(lit, suffix);
             }
+            '"' => {
+                let start_bpos = self.last_pos;
+                let mut valid = true;
+                self.bump();
+                while !self.curr_is('"') {
+                    if self.is_eof() {
+                        let last_bpos = self.last_pos;
+                        panic!(self.fatal_span_(start_bpos,
+                                                last_bpos,
+                                                "unterminated double quote string"));
+                    }
 
-            if self.is_eof() {
-                let last_bpos = self.last_pos;
-                panic!(self.fatal_span_(start_bpos, last_bpos, "unterminated raw string"));
-            } else if !self.curr_is('"') {
-                let last_bpos = self.last_pos;
-                let curr_char = self.curr.unwrap();
-                panic!(self.fatal_span_char(start_bpos, last_bpos,
-                                "found invalid character; \
-                                 only `#` is allowed in raw string delimitation",
-                                curr_char));
+                    let ch_start = self.last_pos;
+                    let ch = self.curr.unwrap();
+                    self.bump();
+                    valid &= self.scan_char_or_byte(ch_start,
+                                                    ch,
+                                                    // ascii_only =
+                                                    false,
+                                                    '"');
+                }
+                // adjust for the ASCII " at the start of the literal
+                let id = if valid {
+                    self.name_from(start_bpos + BytePos(1))
+                } else {
+                    token::intern("??")
+                };
+                self.bump();
+                let suffix = self.scan_optional_raw_name();
+                return token::Literal(token::Str_(id), suffix);
             }
-            self.bump();
-            let content_start_bpos = self.last_pos;
-            let mut content_end_bpos;
-            let mut valid = true;
-            'outer: loop {
+            'r' => {
+                let start_bpos = self.last_pos;
+                self.bump();
+                let mut hash_count = 0;
+                while self.curr_is('#') {
+                    self.bump();
+                    hash_count += 1;
+                }
+
                 if self.is_eof() {
                     let last_bpos = self.last_pos;
                     panic!(self.fatal_span_(start_bpos, last_bpos, "unterminated raw string"));
+                } else if !self.curr_is('"') {
+                    let last_bpos = self.last_pos;
+                    let curr_char = self.curr.unwrap();
+                    panic!(self.fatal_span_char(start_bpos,
+                                                last_bpos,
+                                                "found invalid character; only `#` is allowed \
+                                                 in raw string delimitation",
+                                                curr_char));
                 }
-                //if self.curr_is('"') {
-                    //content_end_bpos = self.last_pos;
-                    //for _ in 0..hash_count {
-                        //self.bump();
-                        //if !self.curr_is('#') {
-                            //continue 'outer;
-                let c = self.curr.unwrap();
-                match c {
-                    '"' => {
-                        content_end_bpos = self.last_pos;
-                        for _ in 0..hash_count {
-                            self.bump();
-                            if !self.curr_is('#') {
-                                continue 'outer;
+                self.bump();
+                let content_start_bpos = self.last_pos;
+                let mut content_end_bpos;
+                let mut valid = true;
+                'outer: loop {
+                    if self.is_eof() {
+                        let last_bpos = self.last_pos;
+                        panic!(self.fatal_span_(start_bpos, last_bpos, "unterminated raw string"));
+                    }
+                    // if self.curr_is('"') {
+                    // content_end_bpos = self.last_pos;
+                    // for _ in 0..hash_count {
+                    // self.bump();
+                    // if !self.curr_is('#') {
+                    // continue 'outer;
+                    let c = self.curr.unwrap();
+                    match c {
+                        '"' => {
+                            content_end_bpos = self.last_pos;
+                            for _ in 0..hash_count {
+                                self.bump();
+                                if !self.curr_is('#') {
+                                    continue 'outer;
+                                }
                             }
+                            break;
                         }
-                        break;
-                    },
-                    '\r' => {
-                        if !self.nextch_is('\n') {
-                            let last_bpos = self.last_pos;
-                            self.err_span_(start_bpos, last_bpos, "bare CR not allowed in raw \
-                                           string, use \\r instead");
-                            valid = false;
+                        '\r' => {
+                            if !self.nextch_is('\n') {
+                                let last_bpos = self.last_pos;
+                                self.err_span_(start_bpos,
+                                               last_bpos,
+                                               "bare CR not allowed in raw string, use \\r \
+                                                instead");
+                                valid = false;
+                            }
                         }
+                        _ => (),
                     }
-                    _ => ()
+                    self.bump();
                 }
                 self.bump();
+                let id = if valid {
+                    self.name_from_to(content_start_bpos, content_end_bpos)
+                } else {
+                    token::intern("??")
+                };
+                let suffix = self.scan_optional_raw_name();
+                return token::Literal(token::StrRaw(id, hash_count), suffix);
+            }
+            '-' => {
+                if self.nextch_is('>') {
+                    self.bump();
+                    self.bump();
+                    return token::RArrow;
+                } else {
+                    return self.binop(token::Minus);
+                }
+            }
+            '&' => {
+                if self.nextch_is('&') {
+                    self.bump();
+                    self.bump();
+                    return token::AndAnd;
+                } else {
+                    return self.binop(token::And);
+                }
+            }
+            '|' => {
+                match self.nextch() {
+                    Some('|') => {
+                        self.bump();
+                        self.bump();
+                        return token::OrOr;
+                    }
+                    _ => {
+                        return self.binop(token::Or);
+                    }
+                }
+            }
+            '+' => {
+                return self.binop(token::Plus);
+            }
+            '*' => {
+                return self.binop(token::Star);
+            }
+            '/' => {
+                return self.binop(token::Slash);
+            }
+            '^' => {
+                return self.binop(token::Caret);
+            }
+            '%' => {
+                return self.binop(token::Percent);
+            }
+            c => {
+                let last_bpos = self.last_pos;
+                let bpos = self.pos;
+                let mut err = self.struct_fatal_span_char(last_bpos,
+                                                          bpos,
+                                                          "unknown start of token",
+                                                          c);
+                unicode_chars::check_for_substitution(&self, c, &mut err);
+                err.emit();
+                panic!(FatalError);
             }
-            self.bump();
-            let id = if valid {
-                self.name_from_to(content_start_bpos, content_end_bpos)
-            } else {
-                token::intern("??")
-            };
-            let suffix = self.scan_optional_raw_name();
-            return token::Literal(token::StrRaw(id, hash_count), suffix);
-          }
-          '-' => {
-            if self.nextch_is('>') {
-                self.bump();
-                self.bump();
-                return token::RArrow;
-            } else { return self.binop(token::Minus); }
-          }
-          '&' => {
-            if self.nextch_is('&') {
-                self.bump();
-                self.bump();
-                return token::AndAnd;
-            } else { return self.binop(token::And); }
-          }
-          '|' => {
-            match self.nextch() {
-              Some('|') => { self.bump(); self.bump(); return token::OrOr; }
-              _ => { return self.binop(token::Or); }
-            }
-          }
-          '+' => { return self.binop(token::Plus); }
-          '*' => { return self.binop(token::Star); }
-          '/' => { return self.binop(token::Slash); }
-          '^' => { return self.binop(token::Caret); }
-          '%' => { return self.binop(token::Percent); }
-          c => {
-              let last_bpos = self.last_pos;
-              let bpos = self.pos;
-              unicode_chars::check_for_substitution(&self, c);
-              panic!(self.fatal_span_char(last_bpos, bpos, "unknown start of token", c))
-          }
         }
     }
 
     fn consume_whitespace(&mut self) {
-        while is_whitespace(self.curr) && !self.is_eof() { self.bump(); }
+        while is_whitespace(self.curr) && !self.is_eof() {
+            self.bump();
+        }
     }
 
     fn read_to_eol(&mut self) -> String {
@@ -1241,14 +1451,16 @@ impl<'a> StringReader<'a> {
             val.push(self.curr.unwrap());
             self.bump();
         }
-        if self.curr_is('\n') { self.bump(); }
-        return val
+        if self.curr_is('\n') {
+            self.bump();
+        }
+        return val;
     }
 
     fn read_one_line_comment(&mut self) -> String {
         let val = self.read_to_eol();
-        assert!((val.as_bytes()[0] == b'/' && val.as_bytes()[1] == b'/')
-             || (val.as_bytes()[0] == b'#' && val.as_bytes()[1] == b'!'));
+        assert!((val.as_bytes()[0] == b'/' && val.as_bytes()[1] == b'/') ||
+                (val.as_bytes()[0] == b'#' && val.as_bytes()[1] == b'!'));
         return val;
     }
 
@@ -1259,10 +1471,9 @@ impl<'a> StringReader<'a> {
     }
 
     fn peeking_at_comment(&self) -> bool {
-        (self.curr_is('/') && self.nextch_is('/'))
-     || (self.curr_is('/') && self.nextch_is('*'))
-     // consider shebangs comments, but not inner attributes
-     || (self.curr_is('#') && self.nextch_is('!') && !self.nextnextch_is('['))
+        (self.curr_is('/') && self.nextch_is('/')) || (self.curr_is('/') && self.nextch_is('*')) ||
+        // consider shebangs comments, but not inner attributes
+        (self.curr_is('#') && self.nextch_is('!') && !self.nextnextch_is('['))
     }
 
     fn scan_byte(&mut self) -> token::Lit {
@@ -1273,18 +1484,26 @@ impl<'a> StringReader<'a> {
         let c2 = self.curr.unwrap_or('\x00');
         self.bump();
 
-        let valid = self.scan_char_or_byte(start, c2, /* ascii_only = */ true, '\'');
+        let valid = self.scan_char_or_byte(start,
+                                           c2,
+                                           // ascii_only =
+                                           true,
+                                           '\'');
         if !self.curr_is('\'') {
             // Byte offsetting here is okay because the
             // character before position `start` are an
             // ascii single quote and ascii 'b'.
             let last_pos = self.last_pos;
-            panic!(self.fatal_span_verbose(
-                start - BytePos(2), last_pos,
-                "unterminated byte constant".to_string()));
+            panic!(self.fatal_span_verbose(start - BytePos(2),
+                                           last_pos,
+                                           "unterminated byte constant".to_string()));
         }
 
-        let id = if valid { self.name_from(start) } else { token::intern("?") };
+        let id = if valid {
+            self.name_from(start)
+        } else {
+            token::intern("?")
+        };
         self.bump(); // advance curr past token
         return token::Byte(id);
     }
@@ -1307,9 +1526,17 @@ impl<'a> StringReader<'a> {
             let ch_start = self.last_pos;
             let ch = self.curr.unwrap();
             self.bump();
-            valid &= self.scan_char_or_byte(ch_start, ch, /* ascii_only = */ true, '"');
+            valid &= self.scan_char_or_byte(ch_start,
+                                            ch,
+                                            // ascii_only =
+                                            true,
+                                            '"');
         }
-        let id = if valid { self.name_from(start) } else { token::intern("??") };
+        let id = if valid {
+            self.name_from(start)
+        } else {
+            token::intern("??")
+        };
         self.bump();
         return token::ByteStr(id);
     }
@@ -1329,10 +1556,11 @@ impl<'a> StringReader<'a> {
         } else if !self.curr_is('"') {
             let last_pos = self.last_pos;
             let ch = self.curr.unwrap();
-            panic!(self.fatal_span_char(start_bpos, last_pos,
-                            "found invalid character; \
-                             only `#` is allowed in raw string delimitation",
-                            ch));
+            panic!(self.fatal_span_char(start_bpos,
+                                        last_pos,
+                                        "found invalid character; only `#` is allowed in raw \
+                                         string delimitation",
+                                        ch));
         }
         self.bump();
         let content_start_bpos = self.last_pos;
@@ -1342,7 +1570,7 @@ impl<'a> StringReader<'a> {
                 None => {
                     let last_pos = self.last_pos;
                     panic!(self.fatal_span_(start_bpos, last_pos, "unterminated raw string"))
-                },
+                }
                 Some('"') => {
                     content_end_bpos = self.last_pos;
                     for _ in 0..hash_count {
@@ -1352,70 +1580,72 @@ impl<'a> StringReader<'a> {
                         }
                     }
                     break;
-                },
-                Some(c) => if c > '\x7F' {
-                    let last_pos = self.last_pos;
-                    self.err_span_char(
-                        last_pos, last_pos, "raw byte string must be ASCII", c);
+                }
+                Some(c) => {
+                    if c > '\x7F' {
+                        let last_pos = self.last_pos;
+                        self.err_span_char(last_pos, last_pos, "raw byte string must be ASCII", c);
+                    }
                 }
             }
             self.bump();
         }
         self.bump();
-        return token::ByteStrRaw(self.name_from_to(content_start_bpos,
-                                                  content_end_bpos),
-                                hash_count);
+        return token::ByteStrRaw(self.name_from_to(content_start_bpos, content_end_bpos),
+                                 hash_count);
     }
 }
 
 pub fn is_whitespace(c: Option<char>) -> bool {
     match c.unwrap_or('\x00') { // None can be null for now... it's not whitespace
         ' ' | '\n' | '\t' | '\r' => true,
-        _ => false
+        _ => false,
     }
 }
 
 fn in_range(c: Option<char>, lo: char, hi: char) -> bool {
     match c {
         Some(c) => lo <= c && c <= hi,
-        _ => false
+        _ => false,
     }
 }
 
-fn is_dec_digit(c: Option<char>) -> bool { return in_range(c, '0', '9'); }
+fn is_dec_digit(c: Option<char>) -> bool {
+    return in_range(c, '0', '9');
+}
 
 pub fn is_doc_comment(s: &str) -> bool {
-    let res = (s.starts_with("///") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'/')
-              || s.starts_with("//!");
+    let res = (s.starts_with("///") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'/') ||
+              s.starts_with("//!");
     debug!("is {:?} a doc comment? {}", s, res);
     res
 }
 
 pub fn is_block_doc_comment(s: &str) -> bool {
-    let res = ((s.starts_with("/**") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'*')
-               || s.starts_with("/*!"))
-              && s.len() >= 5; // Prevent `/**/` from being parsed as a doc comment
+    // Prevent `/**/` from being parsed as a doc comment
+    let res = ((s.starts_with("/**") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'*') ||
+               s.starts_with("/*!")) && s.len() >= 5;
     debug!("is {:?} a doc comment? {}", s, res);
     res
 }
 
 fn ident_start(c: Option<char>) -> bool {
-    let c = match c { Some(c) => c, None => return false };
+    let c = match c {
+        Some(c) => c,
+        None => return false,
+    };
 
-    (c >= 'a' && c <= 'z')
-        || (c >= 'A' && c <= 'Z')
-        || c == '_'
-        || (c > '\x7f' && c.is_xid_start())
+    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c > '\x7f' && c.is_xid_start())
 }
 
 fn ident_continue(c: Option<char>) -> bool {
-    let c = match c { Some(c) => c, None => return false };
+    let c = match c {
+        Some(c) => c,
+        None => return false,
+    };
 
-    (c >= 'a' && c <= 'z')
-        || (c >= 'A' && c <= 'Z')
-        || (c >= '0' && c <= '9')
-        || c == '_'
-        || (c > '\x7f' && c.is_xid_continue())
+    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' ||
+    (c > '\x7f' && c.is_xid_continue())
 }
 
 #[cfg(test)]
@@ -1423,54 +1653,69 @@ mod tests {
     use super::*;
 
     use codemap::{BytePos, CodeMap, Span, NO_EXPANSION};
-    use diagnostic;
+    use errors;
     use parse::token;
-    use parse::token::{str_to_ident};
+    use parse::token::str_to_ident;
     use std::io;
+    use std::rc::Rc;
 
-    fn mk_sh() -> diagnostic::SpanHandler {
+    fn mk_sh(cm: Rc<CodeMap>) -> errors::Handler {
         // FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
-        let emitter = diagnostic::EmitterWriter::new(Box::new(io::sink()), None);
-        let handler = diagnostic::Handler::with_emitter(true, Box::new(emitter));
-        diagnostic::SpanHandler::new(handler, CodeMap::new())
+        let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()), None, cm);
+        errors::Handler::with_emitter(true, false, Box::new(emitter))
     }
 
     // open a string reader for the given string
-    fn setup<'a>(span_handler: &'a diagnostic::SpanHandler,
-                 teststr: String) -> StringReader<'a> {
-        let fm = span_handler.cm.new_filemap("zebra.rs".to_string(), teststr);
+    fn setup<'a>(cm: &CodeMap,
+                 span_handler: &'a errors::Handler,
+                 teststr: String)
+                 -> StringReader<'a> {
+        let fm = cm.new_filemap("zebra.rs".to_string(), teststr);
         StringReader::new(span_handler, fm)
     }
 
-    #[test] fn t1 () {
-        let span_handler = mk_sh();
-        let mut string_reader = setup(&span_handler,
-            "/* my source file */ \
-             fn main() { println!(\"zebra\"); }\n".to_string());
+    #[test]
+    fn t1() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        let mut string_reader = setup(&cm,
+                                      &sh,
+                                      "/* my source file */ fn main() { println!(\"zebra\"); }\n"
+                                          .to_string());
         let id = str_to_ident("fn");
         assert_eq!(string_reader.next_token().tok, token::Comment);
         assert_eq!(string_reader.next_token().tok, token::Whitespace);
         let tok1 = string_reader.next_token();
-        let tok2 = TokenAndSpan{
-            tok:token::Ident(id, token::Plain),
-            sp:Span {lo:BytePos(21),hi:BytePos(23),expn_id: NO_EXPANSION}};
-        assert_eq!(tok1,tok2);
+        let tok2 = TokenAndSpan {
+            tok: token::Ident(id, token::Plain),
+            sp: Span {
+                lo: BytePos(21),
+                hi: BytePos(23),
+                expn_id: NO_EXPANSION,
+            },
+        };
+        assert_eq!(tok1, tok2);
         assert_eq!(string_reader.next_token().tok, token::Whitespace);
         // the 'main' id is already read:
         assert_eq!(string_reader.last_pos.clone(), BytePos(28));
         // read another token:
         let tok3 = string_reader.next_token();
-        let tok4 = TokenAndSpan{
-            tok:token::Ident(str_to_ident("main"), token::Plain),
-            sp:Span {lo:BytePos(24),hi:BytePos(28),expn_id: NO_EXPANSION}};
-        assert_eq!(tok3,tok4);
+        let tok4 = TokenAndSpan {
+            tok: token::Ident(str_to_ident("main"), token::Plain),
+            sp: Span {
+                lo: BytePos(24),
+                hi: BytePos(28),
+                expn_id: NO_EXPANSION,
+            },
+        };
+        assert_eq!(tok3, tok4);
         // the lparen is already read:
         assert_eq!(string_reader.last_pos.clone(), BytePos(29))
     }
 
     // check that the given reader produces the desired stream
     // of tokens (stop checking after exhausting the expected vec)
-    fn check_tokenization (mut string_reader: StringReader, expected: Vec<token::Token> ) {
+    fn check_tokenization(mut string_reader: StringReader, expected: Vec<token::Token>) {
         for expected_tok in &expected {
             assert_eq!(&string_reader.next_token().tok, expected_tok);
         }
@@ -1481,71 +1726,101 @@ mod tests {
         token::Ident(str_to_ident(id), style)
     }
 
-    #[test] fn doublecolonparsing () {
-        check_tokenization(setup(&mk_sh(), "a b".to_string()),
+    #[test]
+    fn doublecolonparsing() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        check_tokenization(setup(&cm, &sh, "a b".to_string()),
                            vec![mk_ident("a", token::Plain),
                                 token::Whitespace,
                                 mk_ident("b", token::Plain)]);
     }
 
-    #[test] fn dcparsing_2 () {
-        check_tokenization(setup(&mk_sh(), "a::b".to_string()),
-                           vec![mk_ident("a",token::ModName),
+    #[test]
+    fn dcparsing_2() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        check_tokenization(setup(&cm, &sh, "a::b".to_string()),
+                           vec![mk_ident("a", token::ModName),
                                 token::ModSep,
                                 mk_ident("b", token::Plain)]);
     }
 
-    #[test] fn dcparsing_3 () {
-        check_tokenization(setup(&mk_sh(), "a ::b".to_string()),
+    #[test]
+    fn dcparsing_3() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        check_tokenization(setup(&cm, &sh, "a ::b".to_string()),
                            vec![mk_ident("a", token::Plain),
                                 token::Whitespace,
                                 token::ModSep,
                                 mk_ident("b", token::Plain)]);
     }
 
-    #[test] fn dcparsing_4 () {
-        check_tokenization(setup(&mk_sh(), "a:: b".to_string()),
-                           vec![mk_ident("a",token::ModName),
+    #[test]
+    fn dcparsing_4() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        check_tokenization(setup(&cm, &sh, "a:: b".to_string()),
+                           vec![mk_ident("a", token::ModName),
                                 token::ModSep,
                                 token::Whitespace,
                                 mk_ident("b", token::Plain)]);
     }
 
-    #[test] fn character_a() {
-        assert_eq!(setup(&mk_sh(), "'a'".to_string()).next_token().tok,
+    #[test]
+    fn character_a() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        assert_eq!(setup(&cm, &sh, "'a'".to_string()).next_token().tok,
                    token::Literal(token::Char(token::intern("a")), None));
     }
 
-    #[test] fn character_space() {
-        assert_eq!(setup(&mk_sh(), "' '".to_string()).next_token().tok,
+    #[test]
+    fn character_space() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        assert_eq!(setup(&cm, &sh, "' '".to_string()).next_token().tok,
                    token::Literal(token::Char(token::intern(" ")), None));
     }
 
-    #[test] fn character_escaped() {
-        assert_eq!(setup(&mk_sh(), "'\\n'".to_string()).next_token().tok,
+    #[test]
+    fn character_escaped() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        assert_eq!(setup(&cm, &sh, "'\\n'".to_string()).next_token().tok,
                    token::Literal(token::Char(token::intern("\\n")), None));
     }
 
-    #[test] fn lifetime_name() {
-        assert_eq!(setup(&mk_sh(), "'abc".to_string()).next_token().tok,
+    #[test]
+    fn lifetime_name() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        assert_eq!(setup(&cm, &sh, "'abc".to_string()).next_token().tok,
                    token::Lifetime(token::str_to_ident("'abc")));
     }
 
-    #[test] fn raw_string() {
-        assert_eq!(setup(&mk_sh(),
-                         "r###\"\"#a\\b\x00c\"\"###".to_string()).next_token()
-                                                                 .tok,
+    #[test]
+    fn raw_string() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        assert_eq!(setup(&cm, &sh, "r###\"\"#a\\b\x00c\"\"###".to_string())
+                       .next_token()
+                       .tok,
                    token::Literal(token::StrRaw(token::intern("\"#a\\b\x00c\""), 3), None));
     }
 
-    #[test] fn literal_suffixes() {
+    #[test]
+    fn literal_suffixes() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
         macro_rules! test {
             ($input: expr, $tok_type: ident, $tok_contents: expr) => {{
-                assert_eq!(setup(&mk_sh(), format!("{}suffix", $input)).next_token().tok,
+                assert_eq!(setup(&cm, &sh, format!("{}suffix", $input)).next_token().tok,
                            token::Literal(token::$tok_type(token::intern($tok_contents)),
                                           Some(token::intern("suffix"))));
                 // with a whitespace separator:
-                assert_eq!(setup(&mk_sh(), format!("{} suffix", $input)).next_token().tok,
+                assert_eq!(setup(&cm, &sh, format!("{} suffix", $input)).next_token().tok,
                            token::Literal(token::$tok_type(token::intern($tok_contents)),
                                           None));
             }}
@@ -1561,40 +1836,47 @@ mod tests {
         test!("1.0", Float, "1.0");
         test!("1.0e10", Float, "1.0e10");
 
-        assert_eq!(setup(&mk_sh(), "2us".to_string()).next_token().tok,
+        assert_eq!(setup(&cm, &sh, "2us".to_string()).next_token().tok,
                    token::Literal(token::Integer(token::intern("2")),
                                   Some(token::intern("us"))));
-        assert_eq!(setup(&mk_sh(), "r###\"raw\"###suffix".to_string()).next_token().tok,
+        assert_eq!(setup(&cm, &sh, "r###\"raw\"###suffix".to_string()).next_token().tok,
                    token::Literal(token::StrRaw(token::intern("raw"), 3),
                                   Some(token::intern("suffix"))));
-        assert_eq!(setup(&mk_sh(), "br###\"raw\"###suffix".to_string()).next_token().tok,
+        assert_eq!(setup(&cm, &sh, "br###\"raw\"###suffix".to_string()).next_token().tok,
                    token::Literal(token::ByteStrRaw(token::intern("raw"), 3),
                                   Some(token::intern("suffix"))));
     }
 
-    #[test] fn line_doc_comments() {
+    #[test]
+    fn line_doc_comments() {
         assert!(is_doc_comment("///"));
         assert!(is_doc_comment("/// blah"));
         assert!(!is_doc_comment("////"));
     }
 
-    #[test] fn nested_block_comments() {
-        let sh = mk_sh();
-        let mut lexer = setup(&sh, "/* /* */ */'a'".to_string());
+    #[test]
+    fn nested_block_comments() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        let mut lexer = setup(&cm, &sh, "/* /* */ */'a'".to_string());
         match lexer.next_token().tok {
-            token::Comment => { },
-            _ => panic!("expected a comment!")
+            token::Comment => {}
+            _ => panic!("expected a comment!"),
         }
-        assert_eq!(lexer.next_token().tok, token::Literal(token::Char(token::intern("a")), None));
+        assert_eq!(lexer.next_token().tok,
+                   token::Literal(token::Char(token::intern("a")), None));
     }
 
-    #[test] fn crlf_comments() {
-        let sh = mk_sh();
-        let mut lexer = setup(&sh, "// test\r\n/// test\r\n".to_string());
+    #[test]
+    fn crlf_comments() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        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!(lexer.next_token().tok, token::Whitespace);
-        assert_eq!(lexer.next_token().tok, token::DocComment(token::intern("/// test")));
+        assert_eq!(lexer.next_token().tok,
+                   token::DocComment(token::intern("/// test")));
     }
 }
index a2f37426210e92fafe5bc1ad38ff52748b339b22..1d32dd4973127432562f15c14cadeb2f9ab49696 100644 (file)
@@ -12,6 +12,7 @@
 // http://www.unicode.org/Public/security/revision-06/confusables.txt
 
 use codemap::mk_sp as make_span;
+use errors::DiagnosticBuilder;
 use super::StringReader;
 
 const UNICODE_ARRAY: &'static [(char, &'static str, char)] = &[
@@ -179,7 +180,9 @@ const ASCII_ARRAY: &'static [(char, &'static str)] = &[
     ('=', "Equals Sign"),
     ('>', "Greater-Than Sign"), ];
 
-pub fn check_for_substitution(reader: &StringReader, ch: char) {
+pub fn check_for_substitution<'a>(reader: &StringReader<'a>,
+                                  ch: char,
+                                  err: &mut DiagnosticBuilder<'a>) {
     UNICODE_ARRAY
     .iter()
     .find(|&&(c, _, _)| c == ch)
@@ -190,7 +193,7 @@ pub fn check_for_substitution(reader: &StringReader, ch: char) {
                 let msg =
                     format!("unicode character '{}' ({}) looks much like '{}' ({}), but it's not",
                             ch, u_name, ascii_char, ascii_name);
-                reader.help_span(span, &msg);
+                err.span_help(span, &msg);
             },
             None => {
                 reader
index e9c8173a4d9802e4bc6b0f5d7a36f0e6a06aa8e9..090b070433f46dc9fef15255b90dd99044df2c3b 100644 (file)
@@ -12,7 +12,7 @@
 
 use ast;
 use codemap::{self, Span, CodeMap, FileMap};
-use diagnostic::{SpanHandler, Handler, Auto, FatalError};
+use errors::{Handler, ColorConfig, DiagnosticBuilder};
 use parse::parser::Parser;
 use parse::token::InternedString;
 use ptr::P;
@@ -25,7 +25,7 @@ use std::path::{Path, PathBuf};
 use std::rc::Rc;
 use std::str;
 
-pub type PResult<T> = Result<T, FatalError>;
+pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
 
 #[macro_use]
 pub mod parser;
@@ -40,26 +40,29 @@ pub mod obsolete;
 
 /// Info about a parsing session.
 pub struct ParseSess {
-    pub span_diagnostic: SpanHandler, // better be the same as the one in the reader!
+    pub span_diagnostic: Handler, // better be the same as the one in the reader!
     /// Used to determine and report recursive mod inclusions
     included_mod_stack: RefCell<Vec<PathBuf>>,
+    code_map: Rc<CodeMap>,
 }
 
 impl ParseSess {
     pub fn new() -> ParseSess {
-        let handler = SpanHandler::new(Handler::new(Auto, None, true), CodeMap::new());
-        ParseSess::with_span_handler(handler)
+        let cm = Rc::new(CodeMap::new());
+        let handler = Handler::with_tty_emitter(ColorConfig::Auto, None, true, false, cm.clone());
+        ParseSess::with_span_handler(handler, cm)
     }
 
-    pub fn with_span_handler(sh: SpanHandler) -> ParseSess {
+    pub fn with_span_handler(handler: Handler, code_map: Rc<CodeMap>) -> ParseSess {
         ParseSess {
-            span_diagnostic: sh,
-            included_mod_stack: RefCell::new(vec![])
+            span_diagnostic: handler,
+            included_mod_stack: RefCell::new(vec![]),
+            code_map: code_map
         }
     }
 
     pub fn codemap(&self) -> &CodeMap {
-        &self.span_diagnostic.cm
+        &self.code_map
     }
 }
 
@@ -73,8 +76,8 @@ pub fn parse_crate_from_file(
     cfg: ast::CrateConfig,
     sess: &ParseSess
 ) -> ast::Crate {
-    panictry!(new_parser_from_file(sess, cfg, input).parse_crate_mod())
-    // why is there no p.abort_if_errors here?
+    let mut parser = new_parser_from_file(sess, cfg, input);
+    abort_if_errors(parser.parse_crate_mod(), &parser)
 }
 
 pub fn parse_crate_attrs_from_file(
@@ -82,8 +85,8 @@ pub fn parse_crate_attrs_from_file(
     cfg: ast::CrateConfig,
     sess: &ParseSess
 ) -> Vec<ast::Attribute> {
-    // FIXME: maybe_aborted?
-    panictry!(new_parser_from_file(sess, cfg, input).parse_inner_attributes())
+    let mut parser = new_parser_from_file(sess, cfg, input);
+    abort_if_errors(parser.parse_inner_attributes(), &parser)
 }
 
 pub fn parse_crate_from_source_str(name: String,
@@ -235,7 +238,7 @@ fn file_to_filemap(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
             let msg = format!("couldn't read {:?}: {}", path.display(), e);
             match spanopt {
                 Some(sp) => panic!(sess.span_diagnostic.span_fatal(sp, &msg)),
-                None => panic!(sess.span_diagnostic.handler().fatal(&msg))
+                None => panic!(sess.span_diagnostic.fatal(&msg))
             }
         }
     }
@@ -258,7 +261,7 @@ pub fn tts_to_parser<'a>(sess: &'a ParseSess,
                          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));
-    panictry!(p.check_unknown_macro_variable());
+    p.check_unknown_macro_variable();
     p
 }
 
@@ -268,6 +271,20 @@ pub fn maybe_aborted<T>(result: T, p: Parser) -> T {
     result
 }
 
+fn abort_if_errors<'a, T>(result: PResult<'a, T>, p: &Parser) -> T {
+    match result {
+        Ok(c) => {
+            p.abort_if_errors();
+            c
+        }
+        Err(mut e) => {
+            e.emit();
+            p.abort_if_errors();
+            unreachable!();
+        }
+    }
+}
+
 /// Parse a string representing a character literal into its final form.
 /// Rather than just accepting/rejecting a given literal, unescapes it as
 /// well. Can take any slice prefixed by a character escape. Returns the
@@ -438,7 +455,7 @@ fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool {
 }
 
 fn filtered_float_lit(data: token::InternedString, suffix: Option<&str>,
-                      sd: &SpanHandler, sp: Span) -> ast::Lit_ {
+                      sd: &Handler, sp: Span) -> ast::Lit_ {
     debug!("filtered_float_lit: {}, {:?}", data, suffix);
     match suffix.as_ref().map(|s| &**s) {
         Some("f32") => ast::LitFloat(data, ast::TyF32),
@@ -446,11 +463,13 @@ fn filtered_float_lit(data: token::InternedString, suffix: Option<&str>,
         Some(suf) => {
             if suf.len() >= 2 && looks_like_width_suffix(&['f'], suf) {
                 // if it looks like a width, lets try to be helpful.
-                sd.span_err(sp, &format!("invalid width `{}` for float literal", &suf[1..]));
-                sd.fileline_help(sp, "valid widths are 32 and 64");
+                sd.struct_span_err(sp, &format!("invalid width `{}` for float literal", &suf[1..]))
+                 .fileline_help(sp, "valid widths are 32 and 64")
+                 .emit();
             } else {
-                sd.span_err(sp, &format!("invalid suffix `{}` for float literal", suf));
-                sd.fileline_help(sp, "valid suffixes are `f32` and `f64`");
+                sd.struct_span_err(sp, &format!("invalid suffix `{}` for float literal", suf))
+                  .fileline_help(sp, "valid suffixes are `f32` and `f64`")
+                  .emit();
             }
 
             ast::LitFloatUnsuffixed(data)
@@ -459,7 +478,7 @@ fn filtered_float_lit(data: token::InternedString, suffix: Option<&str>,
     }
 }
 pub fn float_lit(s: &str, suffix: Option<InternedString>,
-                 sd: &SpanHandler, sp: Span) -> ast::Lit_ {
+                 sd: &Handler, sp: Span) -> ast::Lit_ {
     debug!("float_lit: {:?}, {:?}", s, suffix);
     // FIXME #2252: bounds checking float literals is deferred until trans
     let s = s.chars().filter(|&c| c != '_').collect::<String>();
@@ -561,7 +580,7 @@ pub fn byte_str_lit(lit: &str) -> Rc<Vec<u8>> {
 
 pub fn integer_lit(s: &str,
                    suffix: Option<InternedString>,
-                   sd: &SpanHandler,
+                   sd: &Handler,
                    sp: Span)
                    -> ast::Lit_ {
     // s can only be ascii, byte indexing is fine
@@ -619,13 +638,15 @@ pub fn integer_lit(s: &str,
                 // i<digits> and u<digits> look like widths, so lets
                 // give an error message along those lines
                 if looks_like_width_suffix(&['i', 'u'], suf) {
-                    sd.span_err(sp, &format!("invalid width `{}` for integer literal",
-                                             &suf[1..]));
-                    sd.fileline_help(sp, "valid widths are 8, 16, 32 and 64");
+                    sd.struct_span_err(sp, &format!("invalid width `{}` for integer literal",
+                                             &suf[1..]))
+                      .fileline_help(sp, "valid widths are 8, 16, 32 and 64")
+                      .emit();
                 } else {
-                    sd.span_err(sp, &format!("invalid suffix `{}` for numeric literal", suf));
-                    sd.fileline_help(sp, "the suffix must be one of the integral types \
-                                      (`u32`, `isize`, etc)");
+                    sd.struct_span_err(sp, &format!("invalid suffix `{}` for numeric literal", suf))
+                      .fileline_help(sp, "the suffix must be one of the integral types \
+                                      (`u32`, `isize`, etc)")
+                      .emit();
                 }
 
                 ty
@@ -668,7 +689,6 @@ mod tests {
     use super::*;
     use std::rc::Rc;
     use codemap::{Span, BytePos, Pos, Spanned, NO_EXPANSION};
-    use owned_slice::OwnedSlice;
     use ast::{self, TokenTree};
     use abi;
     use attr::{first_attr_value_str_by_name, AttrMetaMethods};
@@ -890,7 +910,7 @@ mod tests {
         assert!(panictry!(parser.parse_pat())
                 == P(ast::Pat{
                 id: ast::DUMMY_NODE_ID,
-                node: ast::PatIdent(ast::BindByValue(ast::MutImmutable),
+                node: ast::PatIdent(ast::BindingMode::ByValue(ast::MutImmutable),
                                     Spanned{ span:sp(0, 1),
                                              node: str_to_ident("b")
                     },
@@ -926,7 +946,7 @@ mod tests {
                                     pat: P(ast::Pat {
                                         id: ast::DUMMY_NODE_ID,
                                         node: ast::PatIdent(
-                                            ast::BindByValue(ast::MutImmutable),
+                                            ast::BindingMode::ByValue(ast::MutImmutable),
                                                 Spanned{
                                                     span: sp(6,7),
                                                     node: str_to_ident("b")},
@@ -944,7 +964,7 @@ mod tests {
                                     abi::Rust,
                                     ast::Generics{ // no idea on either of these:
                                         lifetimes: Vec::new(),
-                                        ty_params: OwnedSlice::empty(),
+                                        ty_params: P::empty(),
                                         where_clause: ast::WhereClause {
                                             id: ast::DUMMY_NODE_ID,
                                             predicates: Vec::new(),
index bc355f70fb31e92637a3e3a712ecd4581570cde5..75f1ac49c9acc92956446f5db7d56edab98e57b7 100644 (file)
@@ -59,19 +59,17 @@ impl<'a> ParserObsoleteMethods for parser::Parser<'a> {
               kind_str: &str,
               desc: &str,
               error: bool) {
-        if error {
-            self.span_err(sp, &format!("obsolete syntax: {}", kind_str));
+        let mut err = if error {
+            self.diagnostic().struct_span_err(sp, &format!("obsolete syntax: {}", kind_str))
         } else {
-            self.span_warn(sp, &format!("obsolete syntax: {}", kind_str));
-        }
+            self.diagnostic().struct_span_warn(sp, &format!("obsolete syntax: {}", kind_str))
+        };
 
         if !self.obsolete_set.contains(&kind) &&
-            (error || self.sess.span_diagnostic.handler().can_emit_warnings) {
-            self.sess
-                .span_diagnostic
-                .handler()
-                .note(&format!("{}", desc));
+            (error || self.sess.span_diagnostic.can_emit_warnings) {
+            err.note(&format!("{}", desc));
             self.obsolete_set.insert(kind);
         }
+        err.emit();
     }
 }
index 7502a8cbc35466156a00ce3238eeb1d2e9519133..bfa42e761294b7f9f69c3edcf5d9b3470450a49b 100644 (file)
@@ -14,7 +14,7 @@ use abi;
 use ast::BareFnTy;
 use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
 use ast::{Public, Unsafety};
-use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue};
+use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindingMode};
 use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, Block};
 use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause};
 use ast::{Constness, ConstTraitItem, Crate, CrateConfig};
@@ -26,14 +26,14 @@ use ast::{ExprBreak, ExprCall, ExprCast, ExprInPlace};
 use ast::{ExprField, ExprTupField, ExprClosure, ExprIf, ExprIfLet, ExprIndex};
 use ast::{ExprLit, ExprLoop, ExprMac, ExprRange};
 use ast::{ExprMethodCall, ExprParen, ExprPath};
-use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary};
+use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprType, ExprUnary};
 use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl};
-use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod, FunctionRetTy};
+use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, FunctionRetTy};
 use ast::{Ident, Inherited, ImplItem, Item, Item_, ItemStatic};
 use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl, ItemConst};
 use ast::{ItemMac, ItemMod, ItemStruct, ItemTrait, ItemTy, ItemDefaultImpl};
 use ast::{ItemExternCrate, ItemUse};
-use ast::{LifetimeDef, Lit, Lit_};
+use ast::{Lit, Lit_};
 use ast::{LitBool, LitChar, LitByte, LitByteStr};
 use ast::{LitStr, LitInt, Local};
 use ast::{MacStmtWithBraces, MacStmtWithSemicolon, MacStmtWithoutBraces};
@@ -50,7 +50,7 @@ use ast::{SelfExplicit, SelfRegion, SelfStatic, SelfValue};
 use ast::{Delimited, SequenceRepetition, TokenTree, TraitItem, TraitRef};
 use ast::{Ty, Ty_, TypeBinding, TyMac};
 use ast::{TyFixedLengthVec, TyBareFn, TyTypeof, TyInfer};
-use ast::{TyParam, TyParamBound, TyParen, TyPath, TyPolyTraitRef, TyPtr};
+use ast::{TyParam, TyParamBounds, TyParen, TyPath, TyPtr};
 use ast::{TyRptr, TyTup, TyU32, TyVec};
 use ast::TypeTraitItem;
 use ast::{UnnamedField, UnsafeBlock};
@@ -60,22 +60,20 @@ use attr::{ThinAttributes, ThinAttributesExt, AttributesExt};
 use ast;
 use ast_util::{self, ident_to_path};
 use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp, CodeMap};
-use diagnostic;
+use errors::{self, DiagnosticBuilder};
 use ext::tt::macro_parser;
 use parse;
 use parse::classify;
 use parse::common::{SeqSep, seq_sep_none, seq_sep_trailing_allowed};
 use parse::lexer::{Reader, TokenAndSpan};
 use parse::obsolete::{ParserObsoleteMethods, ObsoleteSyntax};
-use parse::token::{self, MatchNt, SubstNt, SpecialVarNt, InternedString};
+use parse::token::{self, intern, MatchNt, SubstNt, SpecialVarNt, InternedString};
 use parse::token::{keywords, special_idents, SpecialMacroVar};
 use parse::{new_sub_parser_from_file, ParseSess};
 use util::parser::{AssocOp, Fixity};
 use print::pprust;
 use ptr::P;
-use owned_slice::OwnedSlice;
 use parse::PResult;
-use diagnostic::FatalError;
 
 use std::collections::HashSet;
 use std::io::prelude::*;
@@ -157,7 +155,7 @@ macro_rules! maybe_whole_expr {
             };
             match found {
                 Some(e) => {
-                    try!($p.bump());
+                    $p.bump();
                     return Ok(e);
                 }
                 None => ()
@@ -172,7 +170,7 @@ macro_rules! maybe_whole {
         {
             let found = match ($p).token {
                 token::Interpolated(token::$constructor(_)) => {
-                    Some(try!(($p).bump_and_get()))
+                    Some(($p).bump_and_get())
                 }
                 _ => None
             };
@@ -185,7 +183,7 @@ macro_rules! maybe_whole {
         {
             let found = match ($p).token {
                 token::Interpolated(token::$constructor(_)) => {
-                    Some(try!(($p).bump_and_get()))
+                    Some(($p).bump_and_get())
                 }
                 _ => None
             };
@@ -198,7 +196,7 @@ macro_rules! maybe_whole {
         {
             let found = match ($p).token {
                 token::Interpolated(token::$constructor(_)) => {
-                    Some(try!(($p).bump_and_get()))
+                    Some(($p).bump_and_get())
                 }
                 _ => None
             };
@@ -211,7 +209,7 @@ macro_rules! maybe_whole {
         {
             let found = match ($p).token {
                 token::Interpolated(token::$constructor(_)) => {
-                    Some(try!(($p).bump_and_get()))
+                    Some(($p).bump_and_get())
                 }
                 _ => None
             };
@@ -224,7 +222,7 @@ macro_rules! maybe_whole {
         {
             let found = match ($p).token {
                 token::Interpolated(token::$constructor(_)) => {
-                    Some(try!(($p).bump_and_get()))
+                    Some(($p).bump_and_get())
                 }
                 _ => None
             };
@@ -394,26 +392,26 @@ impl<'a> Parser<'a> {
         Parser::token_to_string(&self.token)
     }
 
-    pub fn unexpected_last(&self, t: &token::Token) -> FatalError {
+    pub fn unexpected_last<T>(&self, t: &token::Token) -> PResult<'a, T> {
         let token_str = Parser::token_to_string(t);
         let last_span = self.last_span;
-        self.span_fatal(last_span, &format!("unexpected token: `{}`",
-                                                token_str))
+        Err(self.span_fatal(last_span, &format!("unexpected token: `{}`", token_str)))
     }
 
-    pub fn unexpected(&mut self) -> FatalError {
+    pub fn unexpected<T>(&mut self) -> PResult<'a, T> {
         match self.expect_one_of(&[], &[]) {
-            Err(e) => e,
-            Ok(_) => unreachable!()
+            Err(e) => Err(e),
+            Ok(_) => unreachable!(),
         }
     }
 
     /// Expect and consume the token t. Signal an error if
     /// the next token is not t.
-    pub fn expect(&mut self, t: &token::Token) -> PResult<()> {
+    pub fn expect(&mut self, t: &token::Token) -> PResult<'a,  ()> {
         if self.expected_tokens.is_empty() {
             if self.token == *t {
-                self.bump()
+                self.bump();
+                Ok(())
             } else {
                 let token_str = Parser::token_to_string(t);
                 let this_token_str = self.this_token_to_string();
@@ -431,7 +429,7 @@ impl<'a> Parser<'a> {
     /// anything.  Signal a fatal error if next token is unexpected.
     pub fn expect_one_of(&mut self,
                          edible: &[token::Token],
-                         inedible: &[token::Token]) -> PResult<()>{
+                         inedible: &[token::Token]) -> PResult<'a,  ()>{
         fn tokens_to_string(tokens: &[TokenType]) -> String {
             let mut i = tokens.iter();
             // This might be a sign we need a connect method on Iterator.
@@ -450,7 +448,8 @@ impl<'a> Parser<'a> {
             })
         }
         if edible.contains(&self.token) {
-            self.bump()
+            self.bump();
+            Ok(())
         } else if inedible.contains(&self.token) {
             // leave it in the input
             Ok(())
@@ -486,19 +485,18 @@ impl<'a> Parser<'a> {
     /// true if and only if input was consumed for recovery.
     pub fn check_for_erroneous_unit_struct_expecting(&mut self,
                                                      expected: &[token::Token])
-                                                     -> PResult<bool> {
+                                                     -> bool {
         if self.token == token::OpenDelim(token::Brace)
             && expected.iter().all(|t| *t != token::OpenDelim(token::Brace))
             && self.look_ahead(1, |t| *t == token::CloseDelim(token::Brace)) {
             // matched; signal non-fatal error and recover.
             let span = self.span;
-            self.span_err(span,
-                          "unit-like struct construction is written with no trailing `{ }`");
-            try!(self.eat(&token::OpenDelim(token::Brace)));
-            try!(self.eat(&token::CloseDelim(token::Brace)));
-            Ok(true)
+            self.span_err(span, "unit-like struct construction is written with no trailing `{ }`");
+            self.eat(&token::OpenDelim(token::Brace));
+            self.eat(&token::CloseDelim(token::Brace));
+            true
         } else {
-            Ok(false)
+            false
         }
     }
 
@@ -506,7 +504,7 @@ impl<'a> Parser<'a> {
     /// followed by some token from the set edible + inedible.  Recover
     /// from anticipated input errors, discarding erroneous characters.
     pub fn commit_expr(&mut self, e: &Expr, edible: &[token::Token],
-                       inedible: &[token::Token]) -> PResult<()> {
+                       inedible: &[token::Token]) -> PResult<'a, ()> {
         debug!("commit_expr {:?}", e);
         if let ExprPath(..) = e.node {
             // might be unit-struct construction; check for recoverableinput error.
@@ -514,12 +512,12 @@ impl<'a> Parser<'a> {
                 .cloned()
                 .chain(inedible.iter().cloned())
                 .collect::<Vec<_>>();
-            try!(self.check_for_erroneous_unit_struct_expecting(&expected[..]));
+            self.check_for_erroneous_unit_struct_expecting(&expected[..]);
         }
         self.expect_one_of(edible, inedible)
     }
 
-    pub fn commit_expr_expecting(&mut self, e: &Expr, edible: token::Token) -> PResult<()> {
+    pub fn commit_expr_expecting(&mut self, e: &Expr, edible: token::Token) -> PResult<'a, ()> {
         self.commit_expr(e, &[edible], &[])
     }
 
@@ -527,7 +525,7 @@ impl<'a> Parser<'a> {
     /// followed by some token from the set edible + inedible.  Check
     /// for recoverable input errors, discarding erroneous characters.
     pub fn commit_stmt(&mut self, edible: &[token::Token],
-                       inedible: &[token::Token]) -> PResult<()> {
+                       inedible: &[token::Token]) -> PResult<'a, ()> {
         if self.last_token
                .as_ref()
                .map_or(false, |t| t.is_ident() || t.is_path()) {
@@ -535,21 +533,21 @@ impl<'a> Parser<'a> {
                 .cloned()
                 .chain(inedible.iter().cloned())
                 .collect::<Vec<_>>();
-            try!(self.check_for_erroneous_unit_struct_expecting(&expected));
+            self.check_for_erroneous_unit_struct_expecting(&expected);
         }
         self.expect_one_of(edible, inedible)
     }
 
-    pub fn commit_stmt_expecting(&mut self, edible: token::Token) -> PResult<()> {
+    pub fn commit_stmt_expecting(&mut self, edible: token::Token) -> PResult<'a, ()> {
         self.commit_stmt(&[edible], &[])
     }
 
-    pub fn parse_ident(&mut self) -> PResult<ast::Ident> {
+    pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> {
         self.check_strict_keywords();
-        try!(self.check_reserved_keywords());
+        self.check_reserved_keywords();
         match self.token {
             token::Ident(i, _) => {
-                try!(self.bump());
+                self.bump();
                 Ok(i)
             }
             token::Interpolated(token::NtIdent(..)) => {
@@ -563,7 +561,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn parse_ident_or_self_type(&mut self) -> PResult<ast::Ident> {
+    pub fn parse_ident_or_self_type(&mut self) -> PResult<'a, ast::Ident> {
         if self.is_self_type_ident() {
             self.expect_self_type_ident()
         } else {
@@ -571,9 +569,9 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn parse_path_list_item(&mut self) -> PResult<ast::PathListItem> {
+    pub fn parse_path_list_item(&mut self) -> PResult<'a, ast::PathListItem> {
         let lo = self.span.lo;
-        let node = if try!(self.eat_keyword(keywords::SelfValue)) {
+        let node = if self.eat_keyword(keywords::SelfValue) {
             let rename = try!(self.parse_rename());
             ast::PathListMod { id: ast::DUMMY_NODE_ID, rename: rename }
         } else {
@@ -597,10 +595,10 @@ impl<'a> Parser<'a> {
 
     /// Consume token 'tok' if it exists. Returns true if the given
     /// token was present, false otherwise.
-    pub fn eat(&mut self, tok: &token::Token) -> PResult<bool> {
+    pub fn eat(&mut self, tok: &token::Token) -> bool {
         let is_present = self.check(tok);
-        if is_present { try!(self.bump())}
-        Ok(is_present)
+        if is_present { self.bump() }
+        is_present
     }
 
     pub fn check_keyword(&mut self, kw: keywords::Keyword) -> bool {
@@ -610,30 +608,30 @@ impl<'a> Parser<'a> {
 
     /// If the next token is the given keyword, eat it and return
     /// true. Otherwise, return false.
-    pub fn eat_keyword(&mut self, kw: keywords::Keyword) -> PResult<bool> {
+    pub fn eat_keyword(&mut self, kw: keywords::Keyword) -> bool {
         if self.check_keyword(kw) {
-            try!(self.bump());
-            Ok(true)
+            self.bump();
+            true
         } else {
-            Ok(false)
+            false
         }
     }
 
-    pub fn eat_keyword_noexpect(&mut self, kw: keywords::Keyword) -> PResult<bool> {
+    pub fn eat_keyword_noexpect(&mut self, kw: keywords::Keyword) -> bool {
         if self.token.is_keyword(kw) {
-            try!(self.bump());
-            Ok(true)
+            self.bump();
+            true
         } else {
-            Ok(false)
+            false
         }
     }
 
     /// If the given word is not a keyword, signal an error.
     /// If the next token is not the given word, signal an error.
     /// Otherwise, eat it.
-    pub fn expect_keyword(&mut self, kw: keywords::Keyword) -> PResult<()> {
-        if !try!(self.eat_keyword(kw) ){
-            self.expect_one_of(&[], &[])
+    pub fn expect_keyword(&mut self, kw: keywords::Keyword) -> PResult<'a, ()> {
+        if !self.eat_keyword(kw) {
+            self.unexpected()
         } else {
             Ok(())
         }
@@ -651,28 +649,28 @@ impl<'a> Parser<'a> {
     }
 
     /// Signal an error if the current token is a reserved keyword
-    pub fn check_reserved_keywords(&mut self) -> PResult<()>{
+    pub fn check_reserved_keywords(&mut self) {
         if self.token.is_reserved_keyword() {
             let token_str = self.this_token_to_string();
-            Err(self.fatal(&format!("`{}` is a reserved keyword",
-                               token_str)))
-        } else {
-            Ok(())
+            self.fatal(&format!("`{}` is a reserved keyword", token_str)).emit()
         }
     }
 
     /// Expect and consume an `&`. If `&&` is seen, replace it with a single
     /// `&` and continue. If an `&` is not seen, signal an error.
-    fn expect_and(&mut self) -> PResult<()> {
+    fn expect_and(&mut self) -> PResult<'a, ()> {
         self.expected_tokens.push(TokenType::Token(token::BinOp(token::And)));
         match self.token {
-            token::BinOp(token::And) => self.bump(),
+            token::BinOp(token::And) => {
+                self.bump();
+                Ok(())
+            }
             token::AndAnd => {
                 let span = self.span;
                 let lo = span.lo + BytePos(1);
                 Ok(self.replace_token(token::BinOp(token::And), lo, span.hi))
             }
-            _ => self.expect_one_of(&[], &[])
+            _ => self.unexpected()
         }
     }
 
@@ -695,23 +693,26 @@ impl<'a> Parser<'a> {
     ///
     /// This is meant to be used when parsing generics on a path to get the
     /// starting token.
-    fn eat_lt(&mut self) -> PResult<bool> {
+    fn eat_lt(&mut self) -> bool {
         self.expected_tokens.push(TokenType::Token(token::Lt));
         match self.token {
-            token::Lt => { try!(self.bump()); Ok(true)}
+            token::Lt => {
+                self.bump();
+                true
+            }
             token::BinOp(token::Shl) => {
                 let span = self.span;
                 let lo = span.lo + BytePos(1);
                 self.replace_token(token::Lt, lo, span.hi);
-                Ok(true)
+                true
             }
-            _ => Ok(false),
+            _ => false,
         }
     }
 
-    fn expect_lt(&mut self) -> PResult<()> {
-        if !try!(self.eat_lt()) {
-            self.expect_one_of(&[], &[])
+    fn expect_lt(&mut self) -> PResult<'a, ()> {
+        if !self.eat_lt() {
+            self.unexpected()
         } else {
             Ok(())
         }
@@ -720,10 +721,13 @@ impl<'a> Parser<'a> {
     /// Expect and consume a GT. if a >> is seen, replace it
     /// with a single > and continue. If a GT is not seen,
     /// signal an error.
-    pub fn expect_gt(&mut self) -> PResult<()> {
+    pub fn expect_gt(&mut self) -> PResult<'a, ()> {
         self.expected_tokens.push(TokenType::Token(token::Gt));
         match self.token {
-            token::Gt => self.bump(),
+            token::Gt => {
+                self.bump();
+                Ok(())
+            }
             token::BinOp(token::Shr) => {
                 let span = self.span;
                 let lo = span.lo + BytePos(1);
@@ -752,8 +756,8 @@ impl<'a> Parser<'a> {
     pub fn parse_seq_to_before_gt_or_return<T, F>(&mut self,
                                                   sep: Option<token::Token>,
                                                   mut f: F)
-                                                  -> PResult<(OwnedSlice<T>, bool)> where
-        F: FnMut(&mut Parser) -> PResult<Option<T>>,
+                                                  -> PResult<'a, (P<[T]>, bool)>
+        where F: FnMut(&mut Parser<'a>) -> PResult<'a, Option<T>>,
     {
         let mut v = Vec::new();
         // This loop works by alternating back and forth between parsing types
@@ -773,7 +777,7 @@ impl<'a> Parser<'a> {
             if i % 2 == 0 {
                 match try!(f(self)) {
                     Some(result) => v.push(result),
-                    None => return Ok((OwnedSlice::from_vec(v), true))
+                    None => return Ok((P::from_vec(v), true))
                 }
             } else {
                 if let Some(t) = sep.as_ref() {
@@ -782,7 +786,7 @@ impl<'a> Parser<'a> {
 
             }
         }
-        return Ok((OwnedSlice::from_vec(v), false));
+        return Ok((P::from_vec(v), false));
     }
 
     /// Parse a sequence bracketed by '<' and '>', stopping
@@ -790,8 +794,8 @@ impl<'a> Parser<'a> {
     pub fn parse_seq_to_before_gt<T, F>(&mut self,
                                         sep: Option<token::Token>,
                                         mut f: F)
-                                        -> PResult<OwnedSlice<T>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                                        -> PResult<'a, P<[T]>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
     {
         let (result, returned) = try!(self.parse_seq_to_before_gt_or_return(sep,
                                                     |p| Ok(Some(try!(f(p))))));
@@ -802,8 +806,8 @@ impl<'a> Parser<'a> {
     pub fn parse_seq_to_gt<T, F>(&mut self,
                                  sep: Option<token::Token>,
                                  f: F)
-                                 -> PResult<OwnedSlice<T>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                                 -> PResult<'a, P<[T]>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
     {
         let v = try!(self.parse_seq_to_before_gt(sep, f));
         try!(self.expect_gt());
@@ -813,8 +817,8 @@ impl<'a> Parser<'a> {
     pub fn parse_seq_to_gt_or_return<T, F>(&mut self,
                                            sep: Option<token::Token>,
                                            f: F)
-                                           -> PResult<(OwnedSlice<T>, bool)> where
-        F: FnMut(&mut Parser) -> PResult<Option<T>>,
+                                           -> PResult<'a, (P<[T]>, bool)> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a, Option<T>>,
     {
         let (v, returned) = try!(self.parse_seq_to_before_gt_or_return(sep, f));
         if !returned {
@@ -830,11 +834,11 @@ impl<'a> Parser<'a> {
                                   ket: &token::Token,
                                   sep: SeqSep,
                                   f: F)
-                                  -> PResult<Vec<T>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                                  -> PResult<'a, Vec<T>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
     {
         let val = try!(self.parse_seq_to_before_end(ket, sep, f));
-        try!(self.bump());
+        self.bump();
         Ok(val)
     }
 
@@ -845,8 +849,8 @@ impl<'a> Parser<'a> {
                                          ket: &token::Token,
                                          sep: SeqSep,
                                          mut f: F)
-                                         -> PResult<Vec<T>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                                         -> PResult<'a, Vec<T>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
     {
         let mut first: bool = true;
         let mut v = vec!();
@@ -872,12 +876,12 @@ impl<'a> Parser<'a> {
                                      ket: &token::Token,
                                      sep: SeqSep,
                                      f: F)
-                                     -> PResult<Vec<T>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                                     -> PResult<'a, Vec<T>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
     {
         try!(self.expect(bra));
         let result = try!(self.parse_seq_to_before_end(ket, sep, f));
-        try!(self.bump());
+        self.bump();
         Ok(result)
     }
 
@@ -888,8 +892,8 @@ impl<'a> Parser<'a> {
                                         ket: &token::Token,
                                         sep: SeqSep,
                                         f: F)
-                                        -> PResult<Vec<T>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                                        -> PResult<'a, Vec<T>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
     {
         let result = try!(self.parse_unspanned_seq(bra, ket, sep, f));
         if result.is_empty() {
@@ -907,19 +911,19 @@ impl<'a> Parser<'a> {
                            ket: &token::Token,
                            sep: SeqSep,
                            f: F)
-                           -> PResult<Spanned<Vec<T>>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                           -> PResult<'a, Spanned<Vec<T>>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
     {
         let lo = self.span.lo;
         try!(self.expect(bra));
         let result = try!(self.parse_seq_to_before_end(ket, sep, f));
         let hi = self.span.hi;
-        try!(self.bump());
+        self.bump();
         Ok(spanned(lo, hi, result))
     }
 
     /// Advance the parser by one token
-    pub fn bump(&mut self) -> PResult<()> {
+    pub fn bump(&mut self) {
         self.last_span = self.span;
         // Stash token for error recovery (sometimes; clone is not necessarily cheap).
         self.last_token = if self.token.is_ident() ||
@@ -948,14 +952,14 @@ impl<'a> Parser<'a> {
         self.tokens_consumed += 1;
         self.expected_tokens.clear();
         // check after each token
-        self.check_unknown_macro_variable()
+        self.check_unknown_macro_variable();
     }
 
     /// Advance the parser by one token and return the bumped token.
-    pub fn bump_and_get(&mut self) -> PResult<token::Token> {
+    pub fn bump_and_get(&mut self) -> token::Token {
         let old_token = mem::replace(&mut self.token, token::Underscore);
-        try!(self.bump());
-        Ok(old_token)
+        self.bump();
+        old_token
     }
 
     /// EFFECT: replace the current token and span with the given one
@@ -983,28 +987,16 @@ impl<'a> Parser<'a> {
         }
         f(&self.buffer[((self.buffer_start + dist - 1) & 3) as usize].tok)
     }
-    pub fn fatal(&self, m: &str) -> diagnostic::FatalError {
-        self.sess.span_diagnostic.span_fatal(self.span, m)
-    }
-    pub fn span_fatal(&self, sp: Span, m: &str) -> diagnostic::FatalError {
-        self.sess.span_diagnostic.span_fatal(sp, m)
+    pub fn fatal(&self, m: &str) -> DiagnosticBuilder<'a> {
+        self.sess.span_diagnostic.struct_span_fatal(self.span, m)
     }
-    pub fn span_fatal_help(&self, sp: Span, m: &str, help: &str) -> diagnostic::FatalError {
-        self.span_err(sp, m);
-        self.fileline_help(sp, help);
-        diagnostic::FatalError
+    pub fn span_fatal(&self, sp: Span, m: &str) -> DiagnosticBuilder<'a> {
+        self.sess.span_diagnostic.struct_span_fatal(sp, m)
     }
-    pub fn span_note(&self, sp: Span, m: &str) {
-        self.sess.span_diagnostic.span_note(sp, m)
-    }
-    pub fn span_help(&self, sp: Span, m: &str) {
-        self.sess.span_diagnostic.span_help(sp, m)
-    }
-    pub fn span_suggestion(&self, sp: Span, m: &str, n: String) {
-        self.sess.span_diagnostic.span_suggestion(sp, m, n)
-    }
-    pub fn fileline_help(&self, sp: Span, m: &str) {
-        self.sess.span_diagnostic.fileline_help(sp, m)
+    pub fn span_fatal_help(&self, sp: Span, m: &str, help: &str) -> DiagnosticBuilder<'a> {
+        let mut err = self.sess.span_diagnostic.struct_span_fatal(sp, m);
+        err.fileline_help(sp, help);
+        err
     }
     pub fn bug(&self, m: &str) -> ! {
         self.sess.span_diagnostic.span_bug(self.span, m)
@@ -1022,7 +1014,11 @@ impl<'a> Parser<'a> {
         self.sess.span_diagnostic.span_bug(sp, m)
     }
     pub fn abort_if_errors(&self) {
-        self.sess.span_diagnostic.handler().abort_if_errors();
+        self.sess.span_diagnostic.abort_if_errors();
+    }
+
+    pub fn diagnostic(&self) -> &'a errors::Handler {
+        &self.sess.span_diagnostic
     }
 
     pub fn id_to_interned_str(&mut self, id: Ident) -> InternedString {
@@ -1044,7 +1040,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn parse_for_in_type(&mut self) -> PResult<Ty_> {
+    pub fn parse_for_in_type(&mut self) -> PResult<'a, Ty_> {
         /*
         Parses whatever can come after a `for` keyword in a type.
         The `for` has already been consumed.
@@ -1074,10 +1070,10 @@ impl<'a> Parser<'a> {
             let poly_trait_ref = ast::PolyTraitRef { bound_lifetimes: lifetime_defs,
                                                      trait_ref: trait_ref,
                                                      span: mk_sp(lo, hi)};
-            let other_bounds = if try!(self.eat(&token::BinOp(token::Plus)) ){
+            let other_bounds = if self.eat(&token::BinOp(token::Plus)) {
                 try!(self.parse_ty_param_bounds(BoundParsingMode::Bare))
             } else {
-                OwnedSlice::empty()
+                P::empty()
             };
             let all_bounds =
                 Some(TraitTyParamBound(poly_trait_ref, TraitBoundModifier::None)).into_iter()
@@ -1087,12 +1083,12 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn parse_ty_path(&mut self) -> PResult<Ty_> {
+    pub fn parse_ty_path(&mut self) -> PResult<'a, Ty_> {
         Ok(TyPath(None, try!(self.parse_path(LifetimeAndTypesWithoutColons))))
     }
 
     /// parse a TyBareFn type:
-    pub fn parse_ty_bare_fn(&mut self, lifetime_defs: Vec<ast::LifetimeDef>) -> PResult<Ty_> {
+    pub fn parse_ty_bare_fn(&mut self, lifetime_defs: Vec<ast::LifetimeDef>) -> PResult<'a, Ty_> {
         /*
 
         [unsafe] [extern "ABI"] fn <'lt> (S) -> T
@@ -1106,7 +1102,7 @@ impl<'a> Parser<'a> {
         */
 
         let unsafety = try!(self.parse_unsafety());
-        let abi = if try!(self.eat_keyword(keywords::Extern) ){
+        let abi = if self.eat_keyword(keywords::Extern) {
             try!(self.parse_opt_abi()).unwrap_or(abi::C)
         } else {
             abi::Rust
@@ -1129,24 +1125,24 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses an obsolete closure kind (`&:`, `&mut:`, or `:`).
-    pub fn parse_obsolete_closure_kind(&mut self) -> PResult<()> {
+    pub fn parse_obsolete_closure_kind(&mut self) -> PResult<'a, ()> {
          let lo = self.span.lo;
         if
             self.check(&token::BinOp(token::And)) &&
             self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) &&
             self.look_ahead(2, |t| *t == token::Colon)
         {
-            try!(self.bump());
-            try!(self.bump());
-            try!(self.bump());
+            self.bump();
+            self.bump();
+            self.bump();
         } else if
             self.token == token::BinOp(token::And) &&
             self.look_ahead(1, |t| *t == token::Colon)
         {
-            try!(self.bump());
-            try!(self.bump());
+            self.bump();
+            self.bump();
         } else if
-            try!(self.eat(&token::Colon))
+            self.eat(&token::Colon)
         {
             /* nothing */
         } else {
@@ -1158,8 +1154,8 @@ impl<'a> Parser<'a> {
         Ok(())
     }
 
-    pub fn parse_unsafety(&mut self) -> PResult<Unsafety> {
-        if try!(self.eat_keyword(keywords::Unsafe)) {
+    pub fn parse_unsafety(&mut self) -> PResult<'a, Unsafety> {
+        if self.eat_keyword(keywords::Unsafe) {
             return Ok(Unsafety::Unsafe);
         } else {
             return Ok(Unsafety::Normal);
@@ -1167,17 +1163,17 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse the items in a trait declaration
-    pub fn parse_trait_items(&mut self) -> PResult<Vec<P<TraitItem>>> {
+    pub fn parse_trait_items(&mut self) -> PResult<'a,  Vec<P<TraitItem>>> {
         self.parse_unspanned_seq(
             &token::OpenDelim(token::Brace),
             &token::CloseDelim(token::Brace),
             seq_sep_none(),
-            |p| -> PResult<P<TraitItem>> {
+            |p| -> PResult<'a, P<TraitItem>> {
             maybe_whole!(no_clone p, NtTraitItem);
             let mut attrs = try!(p.parse_outer_attributes());
             let lo = p.span.lo;
 
-            let (name, node) = if try!(p.eat_keyword(keywords::Type)) {
+            let (name, node) = if p.eat_keyword(keywords::Type) {
                 let TyParam {ident, bounds, default, ..} = try!(p.parse_ty_param());
                 try!(p.expect(&token::Semi));
                 (ident, TypeTraitItem(bounds, default))
@@ -1187,7 +1183,7 @@ impl<'a> Parser<'a> {
                 try!(p.expect(&token::Colon));
                 let ty = try!(p.parse_ty_sum());
                 let default = if p.check(&token::Eq) {
-                    try!(p.bump());
+                    p.bump();
                     let expr = try!(p.parse_expr());
                     try!(p.commit_expr_expecting(&expr, token::Semi));
                     Some(expr)
@@ -1202,7 +1198,7 @@ impl<'a> Parser<'a> {
                 let ident = try!(p.parse_ident());
                 let mut generics = try!(p.parse_generics());
 
-                let (explicit_self, d) = try!(p.parse_fn_decl_with_self(|p|{
+                let (explicit_self, d) = try!(p.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...
@@ -1221,7 +1217,7 @@ impl<'a> Parser<'a> {
 
                 let body = match p.token {
                   token::Semi => {
-                    try!(p.bump());
+                    p.bump();
                     debug!("parse_trait_methods(): parsing required method");
                     None
                   }
@@ -1253,16 +1249,16 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a possibly mutable type
-    pub fn parse_mt(&mut self) -> PResult<MutTy> {
+    pub fn parse_mt(&mut self) -> PResult<'a, MutTy> {
         let mutbl = try!(self.parse_mutability());
         let t = try!(self.parse_ty());
         Ok(MutTy { ty: t, mutbl: mutbl })
     }
 
     /// Parse optional return type [ -> TY ] in function decl
-    pub fn parse_ret_ty(&mut self) -> PResult<FunctionRetTy> {
-        if try!(self.eat(&token::RArrow) ){
-            if try!(self.eat(&token::Not) ){
+    pub fn parse_ret_ty(&mut self) -> PResult<'a, FunctionRetTy> {
+        if self.eat(&token::RArrow) {
+            if self.eat(&token::Not) {
                 Ok(NoReturn(self.last_span))
             } else {
                 Ok(Return(try!(self.parse_ty())))
@@ -1274,11 +1270,11 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a type in a context where `T1+T2` is allowed.
-    pub fn parse_ty_sum(&mut self) -> PResult<P<Ty>> {
+    pub fn parse_ty_sum(&mut self) -> PResult<'a, P<Ty>> {
         let lo = self.span.lo;
         let lhs = try!(self.parse_ty());
 
-        if !try!(self.eat(&token::BinOp(token::Plus)) ){
+        if !self.eat(&token::BinOp(token::Plus)) {
             return Ok(lhs);
         }
 
@@ -1299,13 +1295,13 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a type.
-    pub fn parse_ty(&mut self) -> PResult<P<Ty>> {
+    pub fn parse_ty(&mut self) -> PResult<'a, P<Ty>> {
         maybe_whole!(no_clone self, NtTy);
 
         let lo = self.span.lo;
 
         let t = if self.check(&token::OpenDelim(token::Paren)) {
-            try!(self.bump());
+            self.bump();
 
             // (t) is a parenthesized ty
             // (t,) is the type of a tuple with only one field,
@@ -1316,7 +1312,7 @@ impl<'a> Parser<'a> {
                 ts.push(try!(self.parse_ty_sum()));
                 if self.check(&token::Comma) {
                     last_comma = true;
-                    try!(self.bump());
+                    self.bump();
                 } else {
                     last_comma = false;
                     break;
@@ -1331,7 +1327,7 @@ impl<'a> Parser<'a> {
             }
         } else if self.check(&token::BinOp(token::Star)) {
             // STAR POINTER (bare pointer?)
-            try!(self.bump());
+            self.bump();
             TyPtr(try!(self.parse_ptr()))
         } else if self.check(&token::OpenDelim(token::Bracket)) {
             // VECTOR
@@ -1356,14 +1352,14 @@ impl<'a> Parser<'a> {
         } else if self.token_is_bare_fn_keyword() {
             // BARE FUNCTION
             try!(self.parse_ty_bare_fn(Vec::new()))
-        } else if try!(self.eat_keyword_noexpect(keywords::Typeof)) {
+        } else if self.eat_keyword_noexpect(keywords::Typeof) {
             // TYPEOF
             // In order to not be ambiguous, the type must be surrounded by parens.
             try!(self.expect(&token::OpenDelim(token::Paren)));
             let e = try!(self.parse_expr());
             try!(self.expect(&token::CloseDelim(token::Paren)));
             TyTypeof(e)
-        } else if try!(self.eat_lt()) {
+        } else if self.eat_lt() {
 
             let (qself, path) =
                  try!(self.parse_qualified_path(NoTypesAllowed));
@@ -1375,7 +1371,7 @@ impl<'a> Parser<'a> {
             let path = try!(self.parse_path(LifetimeAndTypesWithoutColons));
             if self.check(&token::Not) {
                 // MACRO INVOCATION
-                try!(self.bump());
+                self.bump();
                 let delim = try!(self.expect_open_delim());
                 let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim),
                                                      seq_sep_none(),
@@ -1386,7 +1382,7 @@ impl<'a> Parser<'a> {
                 // NAMED TYPE
                 TyPath(None, path)
             }
-        } else if try!(self.eat(&token::Underscore) ){
+        } else if self.eat(&token::Underscore) {
             // TYPE TO BE INFERRED
             TyInfer
         } else {
@@ -1399,7 +1395,7 @@ impl<'a> Parser<'a> {
         Ok(P(Ty {id: ast::DUMMY_NODE_ID, node: t, span: sp}))
     }
 
-    pub fn parse_borrowed_pointee(&mut self) -> PResult<Ty_> {
+    pub fn parse_borrowed_pointee(&mut self) -> PResult<'a, Ty_> {
         // look for `&'lt` or `&'foo ` and interpret `foo` as the region name:
         let opt_lifetime = try!(self.parse_opt_lifetime());
 
@@ -1407,10 +1403,10 @@ impl<'a> Parser<'a> {
         return Ok(TyRptr(opt_lifetime, mt));
     }
 
-    pub fn parse_ptr(&mut self) -> PResult<MutTy> {
-        let mutbl = if try!(self.eat_keyword(keywords::Mut) ){
+    pub fn parse_ptr(&mut self) -> PResult<'a, MutTy> {
+        let mutbl = if self.eat_keyword(keywords::Mut) {
             MutMutable
-        } else if try!(self.eat_keyword(keywords::Const) ){
+        } else if self.eat_keyword(keywords::Const) {
             MutImmutable
         } else {
             let span = self.last_span;
@@ -1445,7 +1441,7 @@ impl<'a> Parser<'a> {
 
     /// This version of parse arg doesn't necessarily require
     /// identifier names.
-    pub fn parse_arg_general(&mut self, require_name: bool) -> PResult<Arg> {
+    pub fn parse_arg_general(&mut self, require_name: bool) -> PResult<'a, Arg> {
         maybe_whole!(no_clone self, NtArg);
 
         let pat = if require_name || self.is_named_argument() {
@@ -1472,14 +1468,14 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a single function argument
-    pub fn parse_arg(&mut self) -> PResult<Arg> {
+    pub fn parse_arg(&mut self) -> PResult<'a, Arg> {
         self.parse_arg_general(true)
     }
 
     /// Parse an argument in a lambda header e.g. |arg, arg|
-    pub fn parse_fn_block_arg(&mut self) -> PResult<Arg> {
+    pub fn parse_fn_block_arg(&mut self) -> PResult<'a, Arg> {
         let pat = try!(self.parse_pat());
-        let t = if try!(self.eat(&token::Colon) ){
+        let t = if self.eat(&token::Colon) {
             try!(self.parse_ty_sum())
         } else {
             P(Ty {
@@ -1495,9 +1491,9 @@ impl<'a> Parser<'a> {
         })
     }
 
-    pub fn maybe_parse_fixed_length_of_vec(&mut self) -> PResult<Option<P<ast::Expr>>> {
+    pub fn maybe_parse_fixed_length_of_vec(&mut self) -> PResult<'a, Option<P<ast::Expr>>> {
         if self.check(&token::Semi) {
-            try!(self.bump());
+            self.bump();
             Ok(Some(try!(self.parse_expr())))
         } else {
             Ok(None)
@@ -1505,12 +1501,12 @@ impl<'a> Parser<'a> {
     }
 
     /// Matches token_lit = LIT_INTEGER | ...
-    pub fn lit_from_token(&self, tok: &token::Token) -> PResult<Lit_> {
+    pub fn lit_from_token(&self, tok: &token::Token) -> PResult<'a, Lit_> {
         match *tok {
             token::Interpolated(token::NtExpr(ref v)) => {
                 match v.node {
                     ExprLit(ref lit) => { Ok(lit.node.clone()) }
-                    _ => { return Err(self.unexpected_last(tok)); }
+                    _ => { return self.unexpected_last(tok); }
                 }
             }
             token::Literal(lit, suf) => {
@@ -1559,19 +1555,19 @@ impl<'a> Parser<'a> {
 
                 Ok(out)
             }
-            _ => { return Err(self.unexpected_last(tok)); }
+            _ => { return self.unexpected_last(tok); }
         }
     }
 
     /// Matches lit = true | false | token_lit
-    pub fn parse_lit(&mut self) -> PResult<Lit> {
+    pub fn parse_lit(&mut self) -> PResult<'a, Lit> {
         let lo = self.span.lo;
-        let lit = if try!(self.eat_keyword(keywords::True) ){
+        let lit = if self.eat_keyword(keywords::True) {
             LitBool(true)
-        } else if try!(self.eat_keyword(keywords::False) ){
+        } else if self.eat_keyword(keywords::False) {
             LitBool(false)
         } else {
-            let token = try!(self.bump_and_get());
+            let token = self.bump_and_get();
             let lit = try!(self.lit_from_token(&token));
             lit
         };
@@ -1579,9 +1575,9 @@ impl<'a> Parser<'a> {
     }
 
     /// matches '-' lit | lit
-    pub fn parse_pat_literal_maybe_minus(&mut self) -> PResult<P<Expr>> {
+    pub fn parse_pat_literal_maybe_minus(&mut self) -> PResult<'a, P<Expr>> {
         let minus_lo = self.span.lo;
-        let minus_present = try!(self.eat(&token::BinOp(token::Minus)));
+        let minus_present = self.eat(&token::BinOp(token::Minus));
         let lo = self.span.lo;
         let literal = P(try!(self.parse_lit()));
         let hi = self.last_span.hi;
@@ -1612,10 +1608,10 @@ impl<'a> Parser<'a> {
     /// `<T as U>::a`
     /// `<T as U>::F::a::<S>`
     pub fn parse_qualified_path(&mut self, mode: PathParsingMode)
-                                -> PResult<(QSelf, ast::Path)> {
+                                -> PResult<'a, (QSelf, ast::Path)> {
         let span = self.last_span;
         let self_type = try!(self.parse_ty_sum());
-        let mut path = if try!(self.eat_keyword(keywords::As)) {
+        let mut path = if self.eat_keyword(keywords::As) {
             try!(self.parse_path(LifetimeAndTypesWithoutColons))
         } else {
             ast::Path {
@@ -1655,10 +1651,10 @@ impl<'a> Parser<'a> {
     /// mode. The `mode` parameter determines whether lifetimes, types, and/or
     /// bounds are permitted and whether `::` must precede type parameter
     /// groups.
-    pub fn parse_path(&mut self, mode: PathParsingMode) -> PResult<ast::Path> {
+    pub fn parse_path(&mut self, mode: PathParsingMode) -> PResult<'a, ast::Path> {
         // Check for a whole path...
         let found = match self.token {
-            token::Interpolated(token::NtPath(_)) => Some(try!(self.bump_and_get())),
+            token::Interpolated(token::NtPath(_)) => Some(self.bump_and_get()),
             _ => None,
         };
         if let Some(token::Interpolated(token::NtPath(path))) = found {
@@ -1666,7 +1662,7 @@ impl<'a> Parser<'a> {
         }
 
         let lo = self.span.lo;
-        let is_global = try!(self.eat(&token::ModSep));
+        let is_global = self.eat(&token::ModSep);
 
         // Parse any number of segments and bound sets. A segment is an
         // identifier followed by an optional lifetime and a set of types.
@@ -1698,22 +1694,22 @@ impl<'a> Parser<'a> {
     /// - `a::b<T,U>::c<V,W>`
     /// - `a::b<T,U>::c(V) -> W`
     /// - `a::b<T,U>::c(V)`
-    pub fn parse_path_segments_without_colons(&mut self) -> PResult<Vec<ast::PathSegment>> {
+    pub fn parse_path_segments_without_colons(&mut self) -> PResult<'a, Vec<ast::PathSegment>> {
         let mut segments = Vec::new();
         loop {
             // First, parse an identifier.
             let identifier = try!(self.parse_ident_or_self_type());
 
             // Parse types, optionally.
-            let parameters = if try!(self.eat_lt() ){
+            let parameters = if self.eat_lt() {
                 let (lifetimes, types, bindings) = try!(self.parse_generic_values_after_lt());
 
-                ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
+                ast::PathParameters::AngleBracketed(ast::AngleBracketedParameterData {
                     lifetimes: lifetimes,
-                    types: OwnedSlice::from_vec(types),
-                    bindings: OwnedSlice::from_vec(bindings),
+                    types: P::from_vec(types),
+                    bindings: P::from_vec(bindings),
                 })
-            } else if try!(self.eat(&token::OpenDelim(token::Paren)) ){
+            } else if self.eat(&token::OpenDelim(token::Paren)) {
                 let lo = self.last_span.lo;
 
                 let inputs = try!(self.parse_seq_to_end(
@@ -1721,7 +1717,7 @@ impl<'a> Parser<'a> {
                     seq_sep_trailing_allowed(token::Comma),
                     |p| p.parse_ty_sum()));
 
-                let output_ty = if try!(self.eat(&token::RArrow) ){
+                let output_ty = if self.eat(&token::RArrow) {
                     Some(try!(self.parse_ty()))
                 } else {
                     None
@@ -1729,7 +1725,7 @@ impl<'a> Parser<'a> {
 
                 let hi = self.last_span.hi;
 
-                ast::ParenthesizedParameters(ast::ParenthesizedParameterData {
+                ast::PathParameters::Parenthesized(ast::ParenthesizedParameterData {
                     span: mk_sp(lo, hi),
                     inputs: inputs,
                     output: output_ty,
@@ -1743,7 +1739,7 @@ impl<'a> Parser<'a> {
                                              parameters: parameters });
 
             // Continue only if we see a `::`
-            if !try!(self.eat(&token::ModSep) ){
+            if !self.eat(&token::ModSep) {
                 return Ok(segments);
             }
         }
@@ -1751,14 +1747,14 @@ impl<'a> Parser<'a> {
 
     /// Examples:
     /// - `a::b::<T,U>::c`
-    pub fn parse_path_segments_with_colons(&mut self) -> PResult<Vec<ast::PathSegment>> {
+    pub fn parse_path_segments_with_colons(&mut self) -> PResult<'a, Vec<ast::PathSegment>> {
         let mut segments = Vec::new();
         loop {
             // First, parse an identifier.
             let identifier = try!(self.parse_ident_or_self_type());
 
             // If we do not see a `::`, stop.
-            if !try!(self.eat(&token::ModSep) ){
+            if !self.eat(&token::ModSep) {
                 segments.push(ast::PathSegment {
                     identifier: identifier,
                     parameters: ast::PathParameters::none()
@@ -1767,20 +1763,21 @@ impl<'a> Parser<'a> {
             }
 
             // Check for a type segment.
-            if try!(self.eat_lt() ){
+            if self.eat_lt() {
                 // Consumed `a::b::<`, go look for types
                 let (lifetimes, types, bindings) = try!(self.parse_generic_values_after_lt());
+                let parameters = ast::AngleBracketedParameterData {
+                    lifetimes: lifetimes,
+                    types: P::from_vec(types),
+                    bindings: P::from_vec(bindings),
+                };
                 segments.push(ast::PathSegment {
                     identifier: identifier,
-                    parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
-                        lifetimes: lifetimes,
-                        types: OwnedSlice::from_vec(types),
-                        bindings: OwnedSlice::from_vec(bindings),
-                    }),
+                    parameters: ast::PathParameters::AngleBracketed(parameters),
                 });
 
                 // Consumed `a::b::<T,U>`, check for `::` before proceeding
-                if !try!(self.eat(&token::ModSep) ){
+                if !self.eat(&token::ModSep) {
                     return Ok(segments);
                 }
             } else {
@@ -1796,7 +1793,7 @@ impl<'a> Parser<'a> {
 
     /// Examples:
     /// - `a::b::c`
-    pub fn parse_path_segments_without_types(&mut self) -> PResult<Vec<ast::PathSegment>> {
+    pub fn parse_path_segments_without_types(&mut self) -> PResult<'a, Vec<ast::PathSegment>> {
         let mut segments = Vec::new();
         loop {
             // First, parse an identifier.
@@ -1809,14 +1806,14 @@ impl<'a> Parser<'a> {
             });
 
             // If we do not see a `::`, stop.
-            if !try!(self.eat(&token::ModSep) ){
+            if !self.eat(&token::ModSep) {
                 return Ok(segments);
             }
         }
     }
 
     /// parses 0 or 1 lifetime
-    pub fn parse_opt_lifetime(&mut self) -> PResult<Option<ast::Lifetime>> {
+    pub fn parse_opt_lifetime(&mut self) -> PResult<'a, Option<ast::Lifetime>> {
         match self.token {
             token::Lifetime(..) => {
                 Ok(Some(try!(self.parse_lifetime())))
@@ -1829,11 +1826,11 @@ impl<'a> Parser<'a> {
 
     /// Parses a single lifetime
     /// Matches lifetime = LIFETIME
-    pub fn parse_lifetime(&mut self) -> PResult<ast::Lifetime> {
+    pub fn parse_lifetime(&mut self) -> PResult<'a, ast::Lifetime> {
         match self.token {
             token::Lifetime(i) => {
                 let span = self.span;
-                try!(self.bump());
+                self.bump();
                 return Ok(ast::Lifetime {
                     id: ast::DUMMY_NODE_ID,
                     span: span,
@@ -1848,7 +1845,7 @@ impl<'a> Parser<'a> {
 
     /// Parses `lifetime_defs = [ lifetime_defs { ',' lifetime_defs } ]` where `lifetime_def  =
     /// lifetime [':' lifetimes]`
-    pub fn parse_lifetime_defs(&mut self) -> PResult<Vec<ast::LifetimeDef>> {
+    pub fn parse_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> {
 
         let mut res = Vec::new();
         loop {
@@ -1856,7 +1853,7 @@ impl<'a> Parser<'a> {
                 token::Lifetime(_) => {
                     let lifetime = try!(self.parse_lifetime());
                     let bounds =
-                        if try!(self.eat(&token::Colon) ){
+                        if self.eat(&token::Colon) {
                             try!(self.parse_lifetimes(token::BinOp(token::Plus)))
                         } else {
                             Vec::new()
@@ -1871,7 +1868,7 @@ impl<'a> Parser<'a> {
             }
 
             match self.token {
-                token::Comma => { try!(self.bump());}
+                token::Comma => { self.bump();}
                 token::Gt => { return Ok(res); }
                 token::BinOp(token::Shr) => { return Ok(res); }
                 _ => {
@@ -1891,7 +1888,7 @@ impl<'a> Parser<'a> {
     /// Parses zero or more comma separated lifetimes. Expects each lifetime to be followed by
     /// either a comma or `>`.  Used when parsing type parameter lists, where we expect something
     /// like `<'a, 'b, T>`.
-    pub fn parse_lifetimes(&mut self, sep: token::Token) -> PResult<Vec<ast::Lifetime>> {
+    pub fn parse_lifetimes(&mut self, sep: token::Token) -> PResult<'a, Vec<ast::Lifetime>> {
 
         let mut res = Vec::new();
         loop {
@@ -1908,13 +1905,13 @@ impl<'a> Parser<'a> {
                 return Ok(res);
             }
 
-            try!(self.bump());
+            self.bump();
         }
     }
 
     /// Parse mutability declaration (mut/const/imm)
-    pub fn parse_mutability(&mut self) -> PResult<Mutability> {
-        if try!(self.eat_keyword(keywords::Mut) ){
+    pub fn parse_mutability(&mut self) -> PResult<'a, Mutability> {
+        if self.eat_keyword(keywords::Mut) {
             Ok(MutMutable)
         } else {
             Ok(MutImmutable)
@@ -1922,7 +1919,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse ident COLON expr
-    pub fn parse_field(&mut self) -> PResult<Field> {
+    pub fn parse_field(&mut self) -> PResult<'a, Field> {
         let lo = self.span.lo;
         let i = try!(self.parse_ident());
         let hi = self.last_span.hi;
@@ -2014,11 +2011,11 @@ impl<'a> Parser<'a> {
         })
     }
 
-    fn expect_open_delim(&mut self) -> PResult<token::DelimToken> {
+    fn expect_open_delim(&mut self) -> PResult<'a, token::DelimToken> {
         self.expected_tokens.push(TokenType::Token(token::Gt));
         match self.token {
             token::OpenDelim(delim) => {
-                try!(self.bump());
+                self.bump();
                 Ok(delim)
             },
             _ => Err(self.fatal("expected open delimiter")),
@@ -2032,7 +2029,7 @@ impl<'a> Parser<'a> {
     /// NB: This does not parse outer attributes,
     ///     and is private because it only works
     ///     correctly if called from parse_dot_or_call_expr().
-    fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
+    fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
         maybe_whole_expr!(self);
 
         // Outer attributes are already parsed and will be
@@ -2050,7 +2047,7 @@ impl<'a> Parser<'a> {
         // Note: when adding new syntax here, don't forget to adjust Token::can_begin_expr().
         match self.token {
             token::OpenDelim(token::Paren) => {
-                try!(self.bump());
+                self.bump();
 
                 let attrs = try!(self.parse_inner_attributes())
                     .into_thin_attrs()
@@ -2067,13 +2064,13 @@ impl<'a> Parser<'a> {
                     if self.check(&token::Comma) {
                         trailing_comma = true;
 
-                        try!(self.bump());
+                        self.bump();
                     } else {
                         trailing_comma = false;
                         break;
                     }
                 }
-                try!(self.bump());
+                self.bump();
 
                 hi = self.last_span.hi;
                 return if es.len() == 1 && !trailing_comma {
@@ -2093,13 +2090,13 @@ impl<'a> Parser<'a> {
                             name: token::SELF_KEYWORD_NAME,
                             ctxt: _
                          }, token::Plain) => {
-                try!(self.bump());
+                self.bump();
                 let path = ast_util::ident_to_path(mk_sp(lo, hi), id);
                 ex = ExprPath(None, path);
                 hi = self.last_span.hi;
             }
             token::OpenDelim(token::Bracket) => {
-                try!(self.bump());
+                self.bump();
 
                 let inner_attrs = try!(self.parse_inner_attributes())
                     .into_thin_attrs();
@@ -2107,20 +2104,20 @@ impl<'a> Parser<'a> {
 
                 if self.check(&token::CloseDelim(token::Bracket)) {
                     // Empty vector.
-                    try!(self.bump());
+                    self.bump();
                     ex = ExprVec(Vec::new());
                 } else {
                     // Nonempty vector.
                     let first_expr = try!(self.parse_expr());
                     if self.check(&token::Semi) {
                         // Repeating array syntax: [ 0; 512 ]
-                        try!(self.bump());
+                        self.bump();
                         let count = try!(self.parse_expr());
                         try!(self.expect(&token::CloseDelim(token::Bracket)));
                         ex = ExprRepeat(first_expr, count);
                     } else if self.check(&token::Comma) {
                         // Vector with two or more elements.
-                        try!(self.bump());
+                        self.bump();
                         let remaining_exprs = try!(self.parse_seq_to_end(
                             &token::CloseDelim(token::Bracket),
                             seq_sep_trailing_allowed(token::Comma),
@@ -2138,54 +2135,54 @@ impl<'a> Parser<'a> {
                 hi = self.last_span.hi;
             }
             _ => {
-                if try!(self.eat_lt()){
+                if self.eat_lt() {
                     let (qself, path) =
                         try!(self.parse_qualified_path(LifetimeAndTypesWithColons));
                     hi = path.span.hi;
                     return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path), attrs));
                 }
-                if try!(self.eat_keyword(keywords::Move) ){
+                if self.eat_keyword(keywords::Move) {
                     let lo = self.last_span.lo;
                     return self.parse_lambda_expr(lo, CaptureByValue, attrs);
                 }
-                if try!(self.eat_keyword(keywords::If)) {
+                if self.eat_keyword(keywords::If) {
                     return self.parse_if_expr(attrs);
                 }
-                if try!(self.eat_keyword(keywords::For) ){
+                if self.eat_keyword(keywords::For) {
                     let lo = self.last_span.lo;
                     return self.parse_for_expr(None, lo, attrs);
                 }
-                if try!(self.eat_keyword(keywords::While) ){
+                if self.eat_keyword(keywords::While) {
                     let lo = self.last_span.lo;
                     return self.parse_while_expr(None, lo, attrs);
                 }
                 if self.token.is_lifetime() {
                     let lifetime = self.get_lifetime();
                     let lo = self.span.lo;
-                    try!(self.bump());
+                    self.bump();
                     try!(self.expect(&token::Colon));
-                    if try!(self.eat_keyword(keywords::While) ){
+                    if self.eat_keyword(keywords::While) {
                         return self.parse_while_expr(Some(lifetime), lo, attrs)
                     }
-                    if try!(self.eat_keyword(keywords::For) ){
+                    if self.eat_keyword(keywords::For) {
                         return self.parse_for_expr(Some(lifetime), lo, attrs)
                     }
-                    if try!(self.eat_keyword(keywords::Loop) ){
+                    if self.eat_keyword(keywords::Loop) {
                         return self.parse_loop_expr(Some(lifetime), lo, attrs)
                     }
                     return Err(self.fatal("expected `while`, `for`, or `loop` after a label"))
                 }
-                if try!(self.eat_keyword(keywords::Loop) ){
+                if self.eat_keyword(keywords::Loop) {
                     let lo = self.last_span.lo;
                     return self.parse_loop_expr(None, lo, attrs);
                 }
-                if try!(self.eat_keyword(keywords::Continue) ){
+                if self.eat_keyword(keywords::Continue) {
                     let ex = if self.token.is_lifetime() {
                         let ex = ExprAgain(Some(Spanned{
                             node: self.get_lifetime(),
                             span: self.span
                         }));
-                        try!(self.bump());
+                        self.bump();
                         ex
                     } else {
                         ExprAgain(None)
@@ -2193,16 +2190,16 @@ impl<'a> Parser<'a> {
                     let hi = self.last_span.hi;
                     return Ok(self.mk_expr(lo, hi, ex, attrs));
                 }
-                if try!(self.eat_keyword(keywords::Match) ){
+                if self.eat_keyword(keywords::Match) {
                     return self.parse_match_expr(attrs);
                 }
-                if try!(self.eat_keyword(keywords::Unsafe) ){
+                if self.eat_keyword(keywords::Unsafe) {
                     return self.parse_block_expr(
                         lo,
                         UnsafeBlock(ast::UserProvided),
                         attrs);
                 }
-                if try!(self.eat_keyword(keywords::Return) ){
+                if self.eat_keyword(keywords::Return) {
                     if self.token.can_begin_expr() {
                         let e = try!(self.parse_expr());
                         hi = e.span.hi;
@@ -2210,13 +2207,13 @@ impl<'a> Parser<'a> {
                     } else {
                         ex = ExprRet(None);
                     }
-                } else if try!(self.eat_keyword(keywords::Break) ){
+                } else if self.eat_keyword(keywords::Break) {
                     if self.token.is_lifetime() {
                         ex = ExprBreak(Some(Spanned {
                             node: self.get_lifetime(),
                             span: self.span
                         }));
-                        try!(self.bump());
+                        self.bump();
                     } else {
                         ex = ExprBreak(None);
                     }
@@ -2231,7 +2228,7 @@ impl<'a> Parser<'a> {
                     // `!`, as an operator, is prefix, so we know this isn't that
                     if self.check(&token::Not) {
                         // MACRO INVOCATION expression
-                        try!(self.bump());
+                        self.bump();
 
                         let delim = try!(self.expect_open_delim());
                         let tts = try!(self.parse_seq_to_end(
@@ -2253,7 +2250,7 @@ impl<'a> Parser<'a> {
                         );
                         if !prohibited {
                             // It's a struct literal.
-                            try!(self.bump());
+                            self.bump();
                             let mut fields = Vec::new();
                             let mut base = None;
 
@@ -2262,7 +2259,7 @@ impl<'a> Parser<'a> {
                                     .into_thin_attrs());
 
                             while self.token != token::CloseDelim(token::Brace) {
-                                if try!(self.eat(&token::DotDot) ){
+                                if self.eat(&token::DotDot) {
                                     base = Some(try!(self.parse_expr()));
                                     break;
                                 }
@@ -2296,7 +2293,7 @@ impl<'a> Parser<'a> {
 
     fn parse_or_use_outer_attributes(&mut self,
                                      already_parsed_attrs: Option<ThinAttributes>)
-                                     -> PResult<ThinAttributes> {
+                                     -> PResult<'a, ThinAttributes> {
         if let Some(attrs) = already_parsed_attrs {
             Ok(attrs)
         } else {
@@ -2307,7 +2304,7 @@ impl<'a> Parser<'a> {
     /// Parse a block or unsafe block
     pub fn parse_block_expr(&mut self, lo: BytePos, blk_mode: BlockCheckMode,
                             attrs: ThinAttributes)
-                            -> PResult<P<Expr>> {
+                            -> PResult<'a, P<Expr>> {
 
         let outer_attrs = attrs;
         try!(self.expect(&token::OpenDelim(token::Brace)));
@@ -2322,7 +2319,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>)
-                                  -> PResult<P<Expr>> {
+                                  -> PResult<'a, P<Expr>> {
         let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs));
 
         let b = try!(self.parse_bottom_expr());
@@ -2332,7 +2329,7 @@ impl<'a> Parser<'a> {
     pub fn parse_dot_or_call_expr_with(&mut self,
                                        e0: P<Expr>,
                                        attrs: ThinAttributes)
-                                       -> PResult<P<Expr>> {
+                                       -> 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
         // structure
@@ -2358,19 +2355,19 @@ impl<'a> Parser<'a> {
         )
     }
 
-    fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>) -> PResult<P<Expr>> {
+    fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>) -> PResult<'a, P<Expr>> {
         let mut e = e0;
         let lo = e.span.lo;
         let mut hi;
         loop {
             // expr.f
-            if try!(self.eat(&token::Dot) ){
+            if self.eat(&token::Dot) {
                 match self.token {
                   token::Ident(i, _) => {
                     let dot = self.last_span.hi;
                     hi = self.span.hi;
-                    try!(self.bump());
-                    let (_, tys, bindings) = if try!(self.eat(&token::ModSep) ){
+                    self.bump();
+                    let (_, tys, bindings) = if self.eat(&token::ModSep) {
                         try!(self.expect_lt());
                         try!(self.parse_generic_values_after_lt())
                     } else {
@@ -2420,7 +2417,7 @@ impl<'a> Parser<'a> {
 
                     let dot = self.last_span.hi;
                     hi = self.span.hi;
-                    try!(self.bump());
+                    self.bump();
 
                     let index = n.as_str().parse::<usize>().ok();
                     match index {
@@ -2436,25 +2433,26 @@ impl<'a> Parser<'a> {
                     }
                   }
                   token::Literal(token::Float(n), _suf) => {
-                    try!(self.bump());
+                    self.bump();
                     let last_span = self.last_span;
                     let fstr = n.as_str();
-                    self.span_err(last_span,
-                                  &format!("unexpected token: `{}`", n.as_str()));
+                    let mut err = self.diagnostic().struct_span_err(last_span,
+                        &format!("unexpected token: `{}`", n.as_str()));
                     if fstr.chars().all(|x| "0123456789.".contains(x)) {
                         let float = match fstr.parse::<f64>().ok() {
                             Some(f) => f,
                             None => continue,
                         };
-                        self.fileline_help(last_span,
+                        err.fileline_help(last_span,
                             &format!("try parenthesizing the first index; e.g., `(foo.{}){}`",
                                     float.trunc() as usize,
                                     format!(".{}", fstr.splitn(2, ".").last().unwrap())));
                     }
+                    err.emit();
                     self.abort_if_errors();
 
                   }
-                  _ => return Err(self.unexpected())
+                  _ => return self.unexpected()
                 }
                 continue;
             }
@@ -2477,7 +2475,7 @@ impl<'a> Parser<'a> {
               // expr[...]
               // Could be either an index expression or a slicing expression.
               token::OpenDelim(token::Bracket) => {
-                try!(self.bump());
+                self.bump();
                 let ix = try!(self.parse_expr());
                 hi = self.span.hi;
                 try!(self.commit_expr_expecting(&*ix, token::CloseDelim(token::Bracket)));
@@ -2491,11 +2489,11 @@ impl<'a> Parser<'a> {
     }
 
     // Parse unquoted tokens after a `$` in a token tree
-    fn parse_unquoted(&mut self) -> PResult<TokenTree> {
+    fn parse_unquoted(&mut self) -> PResult<'a, TokenTree> {
         let mut sp = self.span;
         let (name, namep) = match self.token {
             token::Dollar => {
-                try!(self.bump());
+                self.bump();
 
                 if self.token == token::OpenDelim(token::Paren) {
                     let Spanned { node: seq, span: seq_span } = try!(self.parse_seq(
@@ -2514,7 +2512,7 @@ impl<'a> Parser<'a> {
                                           num_captures: name_num
                                       })));
                 } else if self.token.is_keyword_allow_following_colon(keywords::Crate) {
-                    try!(self.bump());
+                    self.bump();
                     return Ok(TokenTree::Token(sp, SpecialVarNt(SpecialMacroVar::CrateMacroVar)));
                 } else {
                     sp = mk_sp(sp.lo, self.span.hi);
@@ -2524,7 +2522,7 @@ impl<'a> Parser<'a> {
                 }
             }
             token::SubstNt(name, namep) => {
-                try!(self.bump());
+                self.bump();
                 (name, namep)
             }
             _ => unreachable!()
@@ -2533,7 +2531,7 @@ impl<'a> Parser<'a> {
         if self.token == token::Colon && self.look_ahead(1, |t| t.is_ident() &&
                                                                 !t.is_strict_keyword() &&
                                                                 !t.is_reserved_keyword()) {
-            try!(self.bump());
+            self.bump();
             sp = mk_sp(sp.lo, self.span.hi);
             let kindp = match self.token { token::Ident(_, p) => p, _ => token::Plain };
             let nt_kind = try!(self.parse_ident());
@@ -2543,29 +2541,28 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn check_unknown_macro_variable(&mut self) -> PResult<()> {
+    pub fn check_unknown_macro_variable(&mut self) {
         if self.quote_depth == 0 {
             match self.token {
                 token::SubstNt(name, _) =>
-                    return Err(self.fatal(&format!("unknown macro variable `{}`",
-                                       name))),
+                    self.fatal(&format!("unknown macro variable `{}`", name)).emit(),
                 _ => {}
             }
         }
-        Ok(())
     }
 
     /// Parse an optional separator followed by a Kleene-style
     /// repetition token (+ or *).
-    pub fn parse_sep_and_kleene_op(&mut self) -> PResult<(Option<token::Token>, ast::KleeneOp)> {
-        fn parse_kleene_op(parser: &mut Parser) -> PResult<Option<ast::KleeneOp>> {
+    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>> {
             match parser.token {
                 token::BinOp(token::Star) => {
-                    try!(parser.bump());
+                    parser.bump();
                     Ok(Some(ast::ZeroOrMore))
                 },
                 token::BinOp(token::Plus) => {
-                    try!(parser.bump());
+                    parser.bump();
                     Ok(Some(ast::OneOrMore))
                 },
                 _ => Ok(None)
@@ -2577,7 +2574,7 @@ impl<'a> Parser<'a> {
             None => {}
         }
 
-        let separator = try!(self.bump_and_get());
+        let separator = self.bump_and_get();
         match try!(parse_kleene_op(self)) {
             Some(zerok) => Ok((Some(separator), zerok)),
             None => return Err(self.fatal("expected `*` or `+`"))
@@ -2585,7 +2582,7 @@ impl<'a> Parser<'a> {
     }
 
     /// parse a single token tree from the input.
-    pub fn parse_token_tree(&mut self) -> PResult<TokenTree> {
+    pub fn parse_token_tree(&mut self) -> PResult<'a, TokenTree> {
         // FIXME #6994: currently, this is too eager. It
         // parses token trees but also identifies TokenType::Sequence's
         // and token::SubstNt's; it's too early to know yet
@@ -2598,27 +2595,27 @@ impl<'a> Parser<'a> {
         // not an EOF, and not the desired right-delimiter (if
         // it were, parse_seq_to_before_end would have prevented
         // reaching this point.
-        fn parse_non_delim_tt_tok(p: &mut Parser) -> PResult<TokenTree> {
+        fn parse_non_delim_tt_tok<'b>(p: &mut Parser<'b>) -> PResult<'b,  TokenTree> {
             maybe_whole!(deref p, NtTT);
             match p.token {
                 token::CloseDelim(_) => {
+                    let token_str = p.this_token_to_string();
+                    let mut err = p.fatal(
+                        &format!("incorrect close delimiter: `{}`", token_str));
                     // This is a conservative error: only report the last unclosed delimiter. The
                     // previous unclosed delimiters could actually be closed! The parser just hasn't
                     // gotten to them yet.
-                    match p.open_braces.last() {
-                        None => {}
-                        Some(&sp) => p.span_note(sp, "unclosed delimiter"),
+                    if let Some(&sp) = p.open_braces.last() {
+                        err.span_note(sp, "unclosed delimiter");
                     };
-                    let token_str = p.this_token_to_string();
-                    Err(p.fatal(&format!("incorrect close delimiter: `{}`",
-                                    token_str)))
+                    Err(err)
                 },
                 /* we ought to allow different depths of unquotation */
                 token::Dollar | token::SubstNt(..) if p.quote_depth > 0 => {
                     p.parse_unquoted()
                 }
                 _ => {
-                    Ok(TokenTree::Token(p.span, try!(p.bump_and_get())))
+                    Ok(TokenTree::Token(p.span, p.bump_and_get()))
                 }
             }
         }
@@ -2626,12 +2623,12 @@ impl<'a> Parser<'a> {
         match self.token {
             token::Eof => {
                 let open_braces = self.open_braces.clone();
+                let mut err: DiagnosticBuilder<'a> =
+                    self.fatal("this file contains an un-closed delimiter");
                 for sp in &open_braces {
-                    self.span_help(*sp, "did you mean to close this delimiter?");
+                    err.span_help(*sp, "did you mean to close this delimiter?");
                 }
-                // There shouldn't really be a span, but it's easier for the test runner
-                // if we give it one
-                return Err(self.fatal("this file contains an un-closed delimiter "));
+                return Err(err);
             },
             token::OpenDelim(delim) => {
                 // The span for beginning of the delimited section
@@ -2640,7 +2637,7 @@ impl<'a> Parser<'a> {
                 // Parse the open delimiter.
                 self.open_braces.push(self.span);
                 let open_span = self.span;
-                try!(self.bump());
+                self.bump();
 
                 // Parse the token trees within the delimiters
                 let tts = try!(self.parse_seq_to_before_end(
@@ -2651,7 +2648,7 @@ impl<'a> Parser<'a> {
 
                 // Parse the close delimiter.
                 let close_span = self.span;
-                try!(self.bump());
+                self.bump();
                 self.open_braces.pop().unwrap();
 
                 // Expand to cover the entire delimited token tree
@@ -2670,7 +2667,7 @@ impl<'a> Parser<'a> {
 
     // parse a stream of tokens into a list of TokenTree's,
     // up to EOF.
-    pub fn parse_all_token_trees(&mut self) -> PResult<Vec<TokenTree>> {
+    pub fn parse_all_token_trees(&mut self) -> PResult<'a, Vec<TokenTree>> {
         let mut tts = Vec::new();
         while self.token != token::Eof {
             tts.push(try!(self.parse_token_tree()));
@@ -2681,26 +2678,26 @@ impl<'a> Parser<'a> {
     /// Parse a prefix-unary-operator expr
     pub fn parse_prefix_expr(&mut self,
                              already_parsed_attrs: Option<ThinAttributes>)
-                             -> PResult<P<Expr>> {
+                             -> PResult<'a, P<Expr>> {
         let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs));
         let lo = self.span.lo;
         let hi;
         // Note: when adding new unary operators, don't forget to adjust Token::can_begin_expr()
         let ex = match self.token {
             token::Not => {
-                try!(self.bump());
+                self.bump();
                 let e = try!(self.parse_prefix_expr(None));
                 hi = e.span.hi;
                 self.mk_unary(UnNot, e)
             }
             token::BinOp(token::Minus) => {
-                try!(self.bump());
+                self.bump();
                 let e = try!(self.parse_prefix_expr(None));
                 hi = e.span.hi;
                 self.mk_unary(UnNeg, e)
             }
             token::BinOp(token::Star) => {
-                try!(self.bump());
+                self.bump();
                 let e = try!(self.parse_prefix_expr(None));
                 hi = e.span.hi;
                 self.mk_unary(UnDeref, e)
@@ -2713,7 +2710,7 @@ impl<'a> Parser<'a> {
                 ExprAddrOf(m, e)
             }
             token::Ident(..) if self.token.is_keyword(keywords::In) => {
-                try!(self.bump());
+                self.bump();
                 let place = try!(self.parse_expr_res(
                     Restrictions::RESTRICTION_NO_STRUCT_LITERAL,
                     None,
@@ -2726,7 +2723,7 @@ impl<'a> Parser<'a> {
                 ExprInPlace(place, blk_expr)
             }
             token::Ident(..) if self.token.is_keyword(keywords::Box) => {
-                try!(self.bump());
+                self.bump();
                 let subexpression = try!(self.parse_prefix_expr(None));
                 hi = subexpression.span.hi;
                 ExprBox(subexpression)
@@ -2742,7 +2739,7 @@ impl<'a> Parser<'a> {
     /// the expression.
     pub fn parse_assoc_expr(&mut self,
                             already_parsed_attrs: Option<ThinAttributes>)
-                            -> PResult<P<Expr>> {
+                            -> PResult<'a, P<Expr>> {
         self.parse_assoc_expr_with(0, already_parsed_attrs.into())
     }
 
@@ -2750,7 +2747,7 @@ impl<'a> Parser<'a> {
     pub fn parse_assoc_expr_with(&mut self,
                                  min_prec: usize,
                                  lhs: LhsExpr)
-                                 -> PResult<P<Expr>> {
+                                 -> PResult<'a, P<Expr>> {
         let mut lhs = if let LhsExpr::AlreadyParsed(expr) = lhs {
             expr
         } else {
@@ -2779,7 +2776,7 @@ impl<'a> Parser<'a> {
             if op.precedence() < min_prec {
                 break;
             }
-            try!(self.bump());
+            self.bump();
             if op.is_comparison() {
                 self.check_no_chained_comparison(&*lhs, &op);
             }
@@ -2789,6 +2786,11 @@ impl<'a> Parser<'a> {
                 lhs = self.mk_expr(lhs.span.lo, rhs.span.hi,
                                    ExprCast(lhs, rhs), None);
                 continue
+            } else if op == AssocOp::Colon {
+                let rhs = try!(self.parse_ty());
+                lhs = self.mk_expr(lhs.span.lo, rhs.span.hi,
+                                   ExprType(lhs, rhs), None);
+                continue
             } else if op == AssocOp::DotDot {
                     // If we didn’t have to handle `x..`, it would be pretty easy to generalise
                     // it to the Fixity::None code.
@@ -2796,8 +2798,15 @@ impl<'a> Parser<'a> {
                     // We have 2 alternatives here: `x..y` and `x..` The other two variants are
                     // handled with `parse_prefix_range_expr` call above.
                     let rhs = if self.is_at_start_of_range_notation_rhs() {
-                        self.parse_assoc_expr_with(op.precedence() + 1,
-                                                   LhsExpr::NotYetParsed).ok()
+                        let rhs = self.parse_assoc_expr_with(op.precedence() + 1,
+                                                             LhsExpr::NotYetParsed);
+                        match rhs {
+                            Ok(e) => Some(e),
+                            Err(mut e) => {
+                                e.cancel();
+                                None
+                            }
+                        }
                     } else {
                         None
                     };
@@ -2811,18 +2820,26 @@ impl<'a> Parser<'a> {
                     break
             }
 
-
             let rhs = try!(match op.fixity() {
-                Fixity::Right => self.with_res(restrictions, |this|{
-                    this.parse_assoc_expr_with(op.precedence(), LhsExpr::NotYetParsed)
+                Fixity::Right => self.with_res(
+                    restrictions - Restrictions::RESTRICTION_STMT_EXPR,
+                    |this| {
+                        this.parse_assoc_expr_with(op.precedence(),
+                            LhsExpr::NotYetParsed)
                 }),
-                Fixity::Left => self.with_res(restrictions, |this|{
-                    this.parse_assoc_expr_with(op.precedence() + 1, LhsExpr::NotYetParsed)
+                Fixity::Left => self.with_res(
+                    restrictions - Restrictions::RESTRICTION_STMT_EXPR,
+                    |this| {
+                        this.parse_assoc_expr_with(op.precedence() + 1,
+                            LhsExpr::NotYetParsed)
                 }),
                 // We currently have no non-associative operators that are not handled above by
                 // the special cases. The code is here only for future convenience.
-                Fixity::None => self.with_res(restrictions, |this|{
-                    this.parse_assoc_expr_with(op.precedence() + 1, LhsExpr::NotYetParsed)
+                Fixity::None => self.with_res(
+                    restrictions - Restrictions::RESTRICTION_STMT_EXPR,
+                    |this| {
+                        this.parse_assoc_expr_with(op.precedence() + 1,
+                            LhsExpr::NotYetParsed)
                 }),
             });
 
@@ -2858,7 +2875,9 @@ impl<'a> Parser<'a> {
                     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)
                 }
-                AssocOp::As | AssocOp::DotDot => self.bug("As or DotDot branch reached")
+                AssocOp::As | AssocOp::Colon | AssocOp::DotDot => {
+                    self.bug("As, Colon or DotDot branch reached")
+                }
             };
 
             if op.fixity() == Fixity::None { break }
@@ -2872,15 +2891,16 @@ impl<'a> Parser<'a> {
     fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: &AssocOp) {
         debug_assert!(outer_op.is_comparison());
         match lhs.node {
-            ExprBinary(op, _, _) if ast_util::is_comparison_binop(op.node) => {
+            ExprBinary(op, _, _) if op.node.is_comparison() => {
                 // respan to include both operators
                 let op_span = mk_sp(op.span.lo, self.span.hi);
-                self.span_err(op_span,
+                let mut err = self.diagnostic().struct_span_err(op_span,
                     "chained comparison operators require parentheses");
                 if op.node == BiLt && *outer_op == AssocOp::Greater {
-                    self.fileline_help(op_span,
+                    err.fileline_help(op_span,
                         "use `::<...>` instead of `<...>` if you meant to specify type arguments");
                 }
+                err.emit();
             }
             _ => {}
         }
@@ -2889,12 +2909,12 @@ impl<'a> Parser<'a> {
     /// Parse prefix-forms of range notation: `..expr` and `..`
     fn parse_prefix_range_expr(&mut self,
                                already_parsed_attrs: Option<ThinAttributes>)
-                               -> PResult<P<Expr>> {
+                               -> PResult<'a, P<Expr>> {
         debug_assert!(self.token == token::DotDot);
         let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs));
         let lo = self.span.lo;
         let mut hi = self.span.hi;
-        try!(self.bump());
+        self.bump();
         let opt_end = if self.is_at_start_of_range_notation_rhs() {
             // RHS must be parsed with more associativity than DotDot.
             let next_prec = AssocOp::from_token(&token::DotDot).unwrap().precedence() + 1;
@@ -2924,7 +2944,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<P<Expr>> {
+    pub fn parse_if_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         if self.check_keyword(keywords::Let) {
             return self.parse_if_let_expr(attrs);
         }
@@ -2933,7 +2953,7 @@ impl<'a> Parser<'a> {
         let thn = try!(self.parse_block());
         let mut els: Option<P<Expr>> = None;
         let mut hi = thn.span.hi;
-        if try!(self.eat_keyword(keywords::Else) ){
+        if self.eat_keyword(keywords::Else) {
             let elexpr = try!(self.parse_else_expr());
             hi = elexpr.span.hi;
             els = Some(elexpr);
@@ -2943,14 +2963,14 @@ impl<'a> Parser<'a> {
 
     /// Parse an 'if let' expression ('if' token already eaten)
     pub fn parse_if_let_expr(&mut self, attrs: ThinAttributes)
-                             -> PResult<P<Expr>> {
+                             -> PResult<'a, P<Expr>> {
         let lo = self.last_span.lo;
         try!(self.expect_keyword(keywords::Let));
         let pat = try!(self.parse_pat());
         try!(self.expect(&token::Eq));
         let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None));
         let thn = try!(self.parse_block());
-        let (hi, els) = if try!(self.eat_keyword(keywords::Else) ){
+        let (hi, els) = if self.eat_keyword(keywords::Else) {
             let expr = try!(self.parse_else_expr());
             (expr.span.hi, Some(expr))
         } else {
@@ -2963,7 +2983,7 @@ impl<'a> Parser<'a> {
     pub fn parse_lambda_expr(&mut self, lo: BytePos,
                              capture_clause: CaptureClause,
                              attrs: ThinAttributes)
-                             -> PResult<P<Expr>>
+                             -> PResult<'a, P<Expr>>
     {
         let decl = try!(self.parse_fn_block_decl());
         let body = match decl.output {
@@ -2993,8 +3013,8 @@ impl<'a> Parser<'a> {
     }
 
     // `else` token already eaten
-    pub fn parse_else_expr(&mut self) -> PResult<P<Expr>> {
-        if try!(self.eat_keyword(keywords::If) ){
+    pub fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> {
+        if self.eat_keyword(keywords::If) {
             return self.parse_if_expr(None);
         } else {
             let blk = try!(self.parse_block());
@@ -3005,7 +3025,7 @@ impl<'a> Parser<'a> {
     /// Parse a 'for' .. 'in' expression ('for' token already eaten)
     pub fn parse_for_expr(&mut self, opt_ident: Option<ast::Ident>,
                           span_lo: BytePos,
-                          attrs: ThinAttributes) -> PResult<P<Expr>> {
+                          attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         // Parse: `for <src_pat> in <src_expr> <src_loop_block>`
 
         let pat = try!(self.parse_pat());
@@ -3024,7 +3044,7 @@ 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>,
                             span_lo: BytePos,
-                            attrs: ThinAttributes) -> PResult<P<Expr>> {
+                            attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         if self.token.is_keyword(keywords::Let) {
             return self.parse_while_let_expr(opt_ident, span_lo, attrs);
         }
@@ -3039,7 +3059,7 @@ impl<'a> Parser<'a> {
     /// Parse a 'while let' expression ('while' token already eaten)
     pub fn parse_while_let_expr(&mut self, opt_ident: Option<ast::Ident>,
                                 span_lo: BytePos,
-                                attrs: ThinAttributes) -> PResult<P<Expr>> {
+                                attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         try!(self.expect_keyword(keywords::Let));
         let pat = try!(self.parse_pat());
         try!(self.expect(&token::Eq));
@@ -3053,7 +3073,7 @@ impl<'a> Parser<'a> {
     // parse `loop {...}`, `loop` token already eaten
     pub fn parse_loop_expr(&mut self, opt_ident: Option<ast::Ident>,
                            span_lo: BytePos,
-                           attrs: ThinAttributes) -> PResult<P<Expr>> {
+                           attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         let (iattrs, body) = try!(self.parse_inner_attrs_and_block());
         let attrs = attrs.append(iattrs.into_thin_attrs());
         let hi = body.span.hi;
@@ -3061,14 +3081,15 @@ impl<'a> Parser<'a> {
     }
 
     // `match` token already eaten
-    fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<P<Expr>> {
+    fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         let match_span = self.last_span;
         let lo = self.last_span.lo;
         let discriminant = try!(self.parse_expr_res(
             Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None));
-        if let Err(e) = self.commit_expr_expecting(&*discriminant, token::OpenDelim(token::Brace)) {
+        if let Err(mut e) = self.commit_expr_expecting(&*discriminant,
+                                                       token::OpenDelim(token::Brace)) {
             if self.token == token::Token::Semi {
-                self.span_note(match_span, "did you mean to remove this `match` keyword?");
+                e.span_note(match_span, "did you mean to remove this `match` keyword?");
             }
             return Err(e)
         }
@@ -3079,17 +3100,17 @@ impl<'a> Parser<'a> {
             arms.push(try!(self.parse_arm()));
         }
         let hi = self.span.hi;
-        try!(self.bump());
+        self.bump();
         return Ok(self.mk_expr(lo, hi, ExprMatch(discriminant, arms), attrs));
     }
 
-    pub fn parse_arm(&mut self) -> PResult<Arm> {
+    pub fn parse_arm(&mut self) -> PResult<'a, Arm> {
         maybe_whole!(no_clone self, NtArm);
 
         let attrs = try!(self.parse_outer_attributes());
         let pats = try!(self.parse_pats());
         let mut guard = None;
-        if try!(self.eat_keyword(keywords::If) ){
+        if self.eat_keyword(keywords::If) {
             guard = Some(try!(self.parse_expr()));
         }
         try!(self.expect(&token::FatArrow));
@@ -3102,7 +3123,7 @@ impl<'a> Parser<'a> {
         if require_comma {
             try!(self.commit_expr(&*expr, &[token::Comma], &[token::CloseDelim(token::Brace)]));
         } else {
-            try!(self.eat(&token::Comma));
+            self.eat(&token::Comma);
         }
 
         Ok(ast::Arm {
@@ -3114,15 +3135,16 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an expression
-    pub fn parse_expr(&mut self) -> PResult<P<Expr>> {
+    pub fn parse_expr(&mut self) -> PResult<'a, P<Expr>> {
         self.parse_expr_res(Restrictions::empty(), None)
     }
 
     /// Evaluate the closure with restrictions in place.
     ///
     /// After the closure is evaluated, restrictions are reset.
-    pub fn with_res<F>(&mut self, r: Restrictions, f: F) -> PResult<P<Expr>>
-    where F: FnOnce(&mut Self) -> PResult<P<Expr>> {
+    pub fn with_res<F>(&mut self, r: Restrictions, f: F) -> PResult<'a, P<Expr>>
+        where F: FnOnce(&mut Self) -> PResult<'a,  P<Expr>>
+    {
         let old = self.restrictions;
         self.restrictions = r;
         let r = f(self);
@@ -3134,14 +3156,14 @@ 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>)
-                          -> PResult<P<Expr>> {
+                          -> PResult<'a, P<Expr>> {
         self.with_res(r, |this| this.parse_assoc_expr(already_parsed_attrs))
     }
 
     /// Parse the RHS of a local variable declaration (e.g. '= 14;')
-    fn parse_initializer(&mut self) -> PResult<Option<P<Expr>>> {
+    fn parse_initializer(&mut self) -> PResult<'a, Option<P<Expr>>> {
         if self.check(&token::Eq) {
-            try!(self.bump());
+            self.bump();
             Ok(Some(try!(self.parse_expr())))
         } else {
             Ok(None)
@@ -3149,21 +3171,21 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse patterns, separated by '|' s
-    fn parse_pats(&mut self) -> PResult<Vec<P<Pat>>> {
+    fn parse_pats(&mut self) -> PResult<'a, Vec<P<Pat>>> {
         let mut pats = Vec::new();
         loop {
             pats.push(try!(self.parse_pat()));
-            if self.check(&token::BinOp(token::Or)) { try!(self.bump());}
+            if self.check(&token::BinOp(token::Or)) { self.bump();}
             else { return Ok(pats); }
         };
     }
 
-    fn parse_pat_tuple_elements(&mut self) -> PResult<Vec<P<Pat>>> {
+    fn parse_pat_tuple_elements(&mut self) -> PResult<'a, Vec<P<Pat>>> {
         let mut fields = vec![];
         if !self.check(&token::CloseDelim(token::Paren)) {
             fields.push(try!(self.parse_pat()));
             if self.look_ahead(1, |t| *t != token::CloseDelim(token::Paren)) {
-                while try!(self.eat(&token::Comma)) &&
+                while self.eat(&token::Comma) &&
                       !self.check(&token::CloseDelim(token::Paren)) {
                     fields.push(try!(self.parse_pat()));
                 }
@@ -3177,7 +3199,7 @@ impl<'a> Parser<'a> {
 
     fn parse_pat_vec_elements(
         &mut self,
-    ) -> PResult<(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>)> {
+    ) -> PResult<'a, (Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>)> {
         let mut before = Vec::new();
         let mut slice = None;
         let mut after = Vec::new();
@@ -3198,7 +3220,7 @@ impl<'a> Parser<'a> {
 
             if before_slice {
                 if self.check(&token::DotDot) {
-                    try!(self.bump());
+                    self.bump();
 
                     if self.check(&token::Comma) ||
                             self.check(&token::CloseDelim(token::Bracket)) {
@@ -3215,7 +3237,7 @@ impl<'a> Parser<'a> {
 
             let subpat = try!(self.parse_pat());
             if before_slice && self.check(&token::DotDot) {
-                try!(self.bump());
+                self.bump();
                 slice = Some(subpat);
                 before_slice = false;
             } else if before_slice {
@@ -3229,7 +3251,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse the fields of a struct-like pattern
-    fn parse_pat_fields(&mut self) -> PResult<(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;
@@ -3246,7 +3268,7 @@ impl<'a> Parser<'a> {
             let hi;
 
             if self.check(&token::DotDot) {
-                try!(self.bump());
+                self.bump();
                 if self.token != token::CloseDelim(token::Brace) {
                     let token_str = self.this_token_to_string();
                     return Err(self.fatal(&format!("expected `{}`, found `{}`", "}",
@@ -3260,24 +3282,24 @@ impl<'a> Parser<'a> {
             let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) {
                 // Parsing a pattern of the form "fieldname: pat"
                 let fieldname = try!(self.parse_ident());
-                try!(self.bump());
+                self.bump();
                 let pat = try!(self.parse_pat());
                 hi = pat.span.hi;
                 (pat, fieldname, false)
             } else {
                 // Parsing a pattern of the form "(box) (ref) (mut) fieldname"
-                let is_box = try!(self.eat_keyword(keywords::Box));
+                let is_box = self.eat_keyword(keywords::Box);
                 let boxed_span_lo = self.span.lo;
-                let is_ref = try!(self.eat_keyword(keywords::Ref));
-                let is_mut = try!(self.eat_keyword(keywords::Mut));
+                let is_ref = self.eat_keyword(keywords::Ref);
+                let is_mut = self.eat_keyword(keywords::Mut);
                 let fieldname = try!(self.parse_ident());
                 hi = self.last_span.hi;
 
                 let bind_type = match (is_ref, is_mut) {
-                    (true, true) => BindByRef(MutMutable),
-                    (true, false) => BindByRef(MutImmutable),
-                    (false, true) => BindByValue(MutMutable),
-                    (false, false) => BindByValue(MutImmutable),
+                    (true, true) => BindingMode::ByRef(MutMutable),
+                    (true, false) => BindingMode::ByRef(MutImmutable),
+                    (false, true) => BindingMode::ByValue(MutMutable),
+                    (false, false) => BindingMode::ByValue(MutImmutable),
                 };
                 let fieldpath = codemap::Spanned{span:self.last_span, node:fieldname};
                 let fieldpat = P(ast::Pat{
@@ -3306,10 +3328,10 @@ impl<'a> Parser<'a> {
         return Ok((fields, etc));
     }
 
-    fn parse_pat_range_end(&mut self) -> PResult<P<Expr>> {
+    fn parse_pat_range_end(&mut self) -> PResult<'a, P<Expr>> {
         if self.is_path_start() {
             let lo = self.span.lo;
-            let (qself, path) = if try!(self.eat_lt()) {
+            let (qself, path) = if self.eat_lt() {
                 // Parse a qualified path
                 let (qself, path) =
                     try!(self.parse_qualified_path(NoTypesAllowed));
@@ -3332,7 +3354,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a pattern.
-    pub fn parse_pat(&mut self) -> PResult<P<Pat>> {
+    pub fn parse_pat(&mut self) -> PResult<'a, P<Pat>> {
         maybe_whole!(self, NtPat);
 
         let lo = self.span.lo;
@@ -3340,7 +3362,7 @@ impl<'a> Parser<'a> {
         match self.token {
           token::Underscore => {
             // Parse _
-            try!(self.bump());
+            self.bump();
             pat = PatWild;
           }
           token::BinOp(token::And) | token::AndAnd => {
@@ -3356,28 +3378,28 @@ impl<'a> Parser<'a> {
           }
           token::OpenDelim(token::Paren) => {
             // Parse (pat,pat,pat,...) as tuple pattern
-            try!(self.bump());
+            self.bump();
             let fields = try!(self.parse_pat_tuple_elements());
             try!(self.expect(&token::CloseDelim(token::Paren)));
             pat = PatTup(fields);
           }
           token::OpenDelim(token::Bracket) => {
             // Parse [pat,pat,...] as slice pattern
-            try!(self.bump());
+            self.bump();
             let (before, slice, after) = try!(self.parse_pat_vec_elements());
             try!(self.expect(&token::CloseDelim(token::Bracket)));
             pat = PatVec(before, slice, after);
           }
           _ => {
             // At this point, token != _, &, &&, (, [
-            if try!(self.eat_keyword(keywords::Mut)) {
+            if self.eat_keyword(keywords::Mut) {
                 // Parse mut ident @ pat
-                pat = try!(self.parse_pat_ident(BindByValue(MutMutable)));
-            } else if try!(self.eat_keyword(keywords::Ref)) {
+                pat = try!(self.parse_pat_ident(BindingMode::ByValue(MutMutable)));
+            } else if self.eat_keyword(keywords::Ref) {
                 // Parse ref ident @ pat / ref mut ident @ pat
                 let mutbl = try!(self.parse_mutability());
-                pat = try!(self.parse_pat_ident(BindByRef(mutbl)));
-            } else if try!(self.eat_keyword(keywords::Box)) {
+                pat = try!(self.parse_pat_ident(BindingMode::ByRef(mutbl)));
+            } else if self.eat_keyword(keywords::Box) {
                 // Parse box pat
                 let subpat = try!(self.parse_pat());
                 pat = PatBox(subpat);
@@ -3394,7 +3416,7 @@ impl<'a> Parser<'a> {
                         let ident = try!(self.parse_ident());
                         let ident_span = self.last_span;
                         let path = ident_to_path(ident_span, ident);
-                        try!(self.bump());
+                        self.bump();
                         let delim = try!(self.expect_open_delim());
                         let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim),
                                 seq_sep_none(), |p| p.parse_token_tree()));
@@ -3405,10 +3427,10 @@ impl<'a> Parser<'a> {
                         // Parse ident @ pat
                         // This can give false positives and parse nullary enums,
                         // they are dealt with later in resolve
-                        pat = try!(self.parse_pat_ident(BindByValue(MutImmutable)));
+                        pat = try!(self.parse_pat_ident(BindingMode::ByValue(MutImmutable)));
                     }
                 } else {
-                    let (qself, path) = if try!(self.eat_lt()) {
+                    let (qself, path) = if self.eat_lt() {
                         // Parse a qualified path
                         let (qself, path) =
                             try!(self.parse_qualified_path(NoTypesAllowed));
@@ -3422,7 +3444,7 @@ impl<'a> Parser<'a> {
                         // Parse range
                         let hi = self.last_span.hi;
                         let begin = self.mk_expr(lo, hi, ExprPath(qself, path), None);
-                        try!(self.bump());
+                        self.bump();
                         let end = try!(self.parse_pat_range_end());
                         pat = PatRange(begin, end);
                       }
@@ -3431,9 +3453,9 @@ impl<'a> Parser<'a> {
                             return Err(self.fatal("unexpected `{` after qualified path"));
                         }
                         // Parse struct pattern
-                        try!(self.bump());
+                        self.bump();
                         let (fields, etc) = try!(self.parse_pat_fields());
-                        try!(self.bump());
+                        self.bump();
                         pat = PatStruct(path, fields, etc);
                       }
                       token::OpenDelim(token::Paren) => {
@@ -3443,8 +3465,8 @@ impl<'a> Parser<'a> {
                         // Parse tuple struct or enum pattern
                         if self.look_ahead(1, |t| *t == token::DotDot) {
                             // This is a "top constructor only" pat
-                            try!(self.bump());
-                            try!(self.bump());
+                            self.bump();
+                            self.bump();
                             try!(self.expect(&token::CloseDelim(token::Paren)));
                             pat = PatEnum(path, None);
                         } else {
@@ -3469,7 +3491,7 @@ impl<'a> Parser<'a> {
             } else {
                 // Try to parse everything else as literal with optional minus
                 let begin = try!(self.parse_pat_literal_maybe_minus());
-                if try!(self.eat(&token::DotDotDot)) {
+                if self.eat(&token::DotDotDot) {
                     let end = try!(self.parse_pat_range_end());
                     pat = PatRange(begin, end);
                 } else {
@@ -3492,7 +3514,7 @@ impl<'a> Parser<'a> {
     /// error message when parsing mistakes like ref foo(a,b)
     fn parse_pat_ident(&mut self,
                        binding_mode: ast::BindingMode)
-                       -> PResult<ast::Pat_> {
+                       -> PResult<'a, ast::Pat_> {
         if !self.token.is_plain_ident() {
             let span = self.span;
             let tok_str = self.this_token_to_string();
@@ -3502,7 +3524,7 @@ impl<'a> Parser<'a> {
         let ident = try!(self.parse_ident());
         let last_span = self.last_span;
         let name = codemap::Spanned{span: last_span, node: ident};
-        let sub = if try!(self.eat(&token::At) ){
+        let sub = if self.eat(&token::At) {
             Some(try!(self.parse_pat()))
         } else {
             None
@@ -3525,12 +3547,12 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a local variable declaration
-    fn parse_local(&mut self, attrs: ThinAttributes) -> PResult<P<Local>> {
+    fn parse_local(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Local>> {
         let lo = self.span.lo;
         let pat = try!(self.parse_pat());
 
         let mut ty = None;
-        if try!(self.eat(&token::Colon) ){
+        if self.eat(&token::Colon) {
             ty = Some(try!(self.parse_ty_sum()));
         }
         let init = try!(self.parse_initializer());
@@ -3545,7 +3567,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a "let" stmt
-    fn parse_let(&mut self, attrs: ThinAttributes) -> PResult<P<Decl>> {
+    fn parse_let(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Decl>> {
         let lo = self.span.lo;
         let local = try!(self.parse_local(attrs));
         Ok(P(spanned(lo, self.last_span.hi, DeclLocal(local))))
@@ -3553,7 +3575,7 @@ impl<'a> Parser<'a> {
 
     /// Parse a structure field
     fn parse_name_and_ty(&mut self, pr: Visibility,
-                         attrs: Vec<Attribute> ) -> PResult<StructField> {
+                         attrs: Vec<Attribute> ) -> PResult<'a, StructField> {
         let lo = match pr {
             Inherited => self.span.lo,
             Public => self.last_span.lo,
@@ -3585,11 +3607,11 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a statement. may include decl.
-    pub fn parse_stmt(&mut self) -> PResult<Option<P<Stmt>>> {
+    pub fn parse_stmt(&mut self) -> PResult<'a, Option<P<Stmt>>> {
         Ok(try!(self.parse_stmt_()).map(P))
     }
 
-    fn parse_stmt_(&mut self) -> PResult<Option<Stmt>> {
+    fn parse_stmt_(&mut self) -> PResult<'a, Option<Stmt>> {
         maybe_whole!(Some deref self, NtStmt);
 
         let attrs = try!(self.parse_outer_attributes());
@@ -3609,7 +3631,7 @@ impl<'a> Parser<'a> {
             // Potential trouble: if we allow macros with paths instead of
             // idents, we'd need to look ahead past the whole path here...
             let pth = try!(self.parse_path(NoTypesAllowed));
-            try!(self.bump());
+            self.bump();
 
             let id = match self.token {
                 token::OpenDelim(_) => token::special_idents::invalid, // no special identifier
@@ -3662,7 +3684,7 @@ impl<'a> Parser<'a> {
                 //
                 // Require a semicolon or braces.
                 if style != MacStmtWithBraces {
-                    if !try!(self.eat(&token::Semi) ){
+                    if !self.eat(&token::Semi) {
                         let last_span = self.last_span;
                         self.span_err(last_span,
                                       "macros that expand to items must \
@@ -3698,7 +3720,7 @@ impl<'a> Parser<'a> {
                     // Do not attempt to parse an expression if we're done here.
                     if self.token == token::Semi {
                         unused_attrs(&attrs, self);
-                        try!(self.bump());
+                        self.bump();
                         return Ok(None);
                     }
 
@@ -3725,12 +3747,12 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a block. No inner attrs are allowed.
-    pub fn parse_block(&mut self) -> PResult<P<Block>> {
+    pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
         maybe_whole!(no_clone self, NtBlock);
 
         let lo = self.span.lo;
 
-        if !try!(self.eat(&token::OpenDelim(token::Brace)) ){
+        if !self.eat(&token::OpenDelim(token::Brace)) {
             let sp = self.span;
             let tok = self.this_token_to_string();
             return Err(self.span_fatal_help(sp,
@@ -3742,7 +3764,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a block. Inner attrs are allowed.
-    fn parse_inner_attrs_and_block(&mut self) -> PResult<(Vec<Attribute>, P<Block>)> {
+    fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
         maybe_whole!(pair_empty self, NtBlock);
 
         let lo = self.span.lo;
@@ -3753,11 +3775,11 @@ impl<'a> Parser<'a> {
 
     /// Parse the rest of a block expression or function body
     /// Precondition: already parsed the '{'.
-    fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<P<Block>> {
+    fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<'a, P<Block>> {
         let mut stmts = vec![];
         let mut expr = None;
 
-        while !try!(self.eat(&token::CloseDelim(token::Brace))) {
+        while !self.eat(&token::CloseDelim(token::Brace)) {
             let Spanned {node, span} = if let Some(s) = try!(self.parse_stmt_()) {
                 s
             } else {
@@ -3777,7 +3799,7 @@ impl<'a> Parser<'a> {
                                 node: StmtMac(mac, MacStmtWithSemicolon, attrs),
                                 span: mk_sp(span.lo, self.span.hi),
                             }));
-                            try!(self.bump());
+                            self.bump();
                         }
                         _ => {
                             let e = self.mk_mac_expr(span.lo, span.hi,
@@ -3801,7 +3823,7 @@ impl<'a> Parser<'a> {
                                 node: StmtMac(m, MacStmtWithSemicolon, attrs),
                                 span: mk_sp(span.lo, self.span.hi),
                             }));
-                            try!(self.bump());
+                            self.bump();
                         }
                         token::CloseDelim(token::Brace) => {
                             // if a block ends in `m!(arg)` without
@@ -3847,7 +3869,7 @@ impl<'a> Parser<'a> {
             e: P<Expr>,
             span: Span,
             stmts: &mut Vec<P<Stmt>>,
-            last_block_expr: &mut Option<P<Expr>>) -> PResult<()> {
+            last_block_expr: &mut Option<P<Expr>>) -> PResult<'a, ()> {
         // expression without semicolon
         if classify::expr_requires_semi_to_be_stmt(&*e) {
             // Just check for errors and recover; do not eat semicolon yet.
@@ -3857,7 +3879,7 @@ impl<'a> Parser<'a> {
 
         match self.token {
             token::Semi => {
-                try!(self.bump());
+                self.bump();
                 let span_with_semi = Span {
                     lo: span.lo,
                     hi: self.last_span.hi,
@@ -3883,10 +3905,10 @@ impl<'a> Parser<'a> {
     // otherwise returns empty list.
     fn parse_colon_then_ty_param_bounds(&mut self,
                                         mode: BoundParsingMode)
-                                        -> PResult<OwnedSlice<TyParamBound>>
+                                        -> PResult<'a, TyParamBounds>
     {
-        if !try!(self.eat(&token::Colon) ){
-            Ok(OwnedSlice::empty())
+        if !self.eat(&token::Colon) {
+            Ok(P::empty())
         } else {
             self.parse_ty_param_bounds(mode)
         }
@@ -3898,12 +3920,12 @@ impl<'a> Parser<'a> {
     // and     bound     = 'region | trait_ref
     fn parse_ty_param_bounds(&mut self,
                              mode: BoundParsingMode)
-                             -> PResult<OwnedSlice<TyParamBound>>
+                             -> PResult<'a, TyParamBounds>
     {
         let mut result = vec!();
         loop {
             let question_span = self.span;
-            let ate_question = try!(self.eat(&token::Question));
+            let ate_question = self.eat(&token::Question);
             match self.token {
                 token::Lifetime(lifetime) => {
                     if ate_question {
@@ -3915,7 +3937,7 @@ impl<'a> Parser<'a> {
                         span: self.span,
                         name: lifetime.name
                     }));
-                    try!(self.bump());
+                    self.bump();
                 }
                 token::ModSep | token::Ident(..) => {
                     let poly_trait_ref = try!(self.parse_poly_trait_ref());
@@ -3935,23 +3957,23 @@ impl<'a> Parser<'a> {
                 _ => break,
             }
 
-            if !try!(self.eat(&token::BinOp(token::Plus)) ){
+            if !self.eat(&token::BinOp(token::Plus)) {
                 break;
             }
         }
 
-        return Ok(OwnedSlice::from_vec(result));
+        return Ok(P::from_vec(result));
     }
 
     /// Matches typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?
-    fn parse_ty_param(&mut self) -> PResult<TyParam> {
+    fn parse_ty_param(&mut self) -> PResult<'a, TyParam> {
         let span = self.span;
         let ident = try!(self.parse_ident());
 
         let bounds = try!(self.parse_colon_then_ty_param_bounds(BoundParsingMode::Modified));
 
         let default = if self.check(&token::Eq) {
-            try!(self.bump());
+            self.bump();
             Some(try!(self.parse_ty_sum()))
         } else {
             None
@@ -3973,10 +3995,10 @@ impl<'a> Parser<'a> {
     /// matches generics = ( ) | ( < > ) | ( < typaramseq ( , )? > ) | ( < lifetimes ( , )? > )
     ///                  | ( < lifetimes , typaramseq ( , )? > )
     /// where   typaramseq = ( typaram ) | ( typaram , typaramseq )
-    pub fn parse_generics(&mut self) -> PResult<ast::Generics> {
+    pub fn parse_generics(&mut self) -> PResult<'a, ast::Generics> {
         maybe_whole!(self, NtGenerics);
 
-        if try!(self.eat(&token::Lt) ){
+        if self.eat(&token::Lt) {
             let lifetime_defs = try!(self.parse_lifetime_defs());
             let mut seen_default = false;
             let ty_params = try!(self.parse_seq_to_gt(Some(token::Comma), |p| {
@@ -4000,11 +4022,11 @@ impl<'a> Parser<'a> {
                 }
             })
         } else {
-            Ok(ast_util::empty_generics())
+            Ok(ast::Generics::default())
         }
     }
 
-    fn parse_generic_values_after_lt(&mut self) -> PResult<(Vec<ast::Lifetime>,
+    fn parse_generic_values_after_lt(&mut self) -> PResult<'a, (Vec<ast::Lifetime>,
                                                             Vec<P<Ty>>,
                                                             Vec<P<TypeBinding>>)> {
         let span_lo = self.span.lo;
@@ -4021,19 +4043,22 @@ impl<'a> Parser<'a> {
             let msg = format!("expected `,` or `>` after lifetime \
                               name, found `{}`",
                               self.this_token_to_string());
-            self.span_err(self.span, &msg);
+            let mut err = self.diagnostic().struct_span_err(self.span, &msg);
 
             let span_hi = self.span.hi;
-            let span_hi = if self.parse_ty().is_ok() {
-                self.span.hi
-            } else {
-                span_hi
+            let span_hi = match self.parse_ty() {
+                Ok(..) => self.span.hi,
+                Err(ref mut err) => {
+                    err.cancel();
+                    span_hi
+                }
             };
 
             let msg = format!("did you mean a single argument type &'a Type, \
                               or did you mean the comma-separated arguments \
                               'a, Type?");
-            self.span_note(mk_sp(span_lo, span_hi), &msg);
+            err.span_note(mk_sp(span_lo, span_hi), &msg);
+            err.emit();
 
             self.abort_if_errors()
         }
@@ -4063,7 +4088,7 @@ impl<'a> Parser<'a> {
                 try!(p.forbid_lifetime());
                 let lo = p.span.lo;
                 let ident = try!(p.parse_ident());
-                let found_eq = try!(p.eat(&token::Eq));
+                let found_eq = p.eat(&token::Eq);
                 if !found_eq {
                     let span = p.span;
                     p.span_warn(span, "whoops, no =?");
@@ -4081,7 +4106,7 @@ impl<'a> Parser<'a> {
         Ok((lifetimes, types.into_vec(), bindings.into_vec()))
     }
 
-    fn forbid_lifetime(&mut self) -> PResult<()> {
+    fn forbid_lifetime(&mut self) -> PResult<'a, ()> {
         if self.token.is_lifetime() {
             let span = self.span;
             return Err(self.span_fatal(span, "lifetime parameters must be declared \
@@ -4095,7 +4120,7 @@ impl<'a> Parser<'a> {
     /// ```ignore
     /// where T : Trait<U, V> + 'b, 'a : 'b
     /// ```
-    pub fn parse_where_clause(&mut self) -> PResult<ast::WhereClause> {
+    pub fn parse_where_clause(&mut self) -> PResult<'a, ast::WhereClause> {
         maybe_whole!(self, NtWhereClause);
 
         let mut where_clause = WhereClause {
@@ -4103,7 +4128,7 @@ impl<'a> Parser<'a> {
             predicates: Vec::new(),
         };
 
-        if !try!(self.eat_keyword(keywords::Where)) {
+        if !self.eat_keyword(keywords::Where) {
             return Ok(where_clause);
         }
 
@@ -4119,7 +4144,7 @@ impl<'a> Parser<'a> {
                     let bounded_lifetime =
                         try!(self.parse_lifetime());
 
-                    try!(self.eat(&token::Colon));
+                    self.eat(&token::Colon);
 
                     let bounds =
                         try!(self.parse_lifetimes(token::BinOp(token::Plus)));
@@ -4139,7 +4164,7 @@ impl<'a> Parser<'a> {
                 }
 
                 _ => {
-                    let bound_lifetimes = if try!(self.eat_keyword(keywords::For) ){
+                    let bound_lifetimes = if self.eat_keyword(keywords::For) {
                         // Higher ranked constraint.
                         try!(self.expect(&token::Lt));
                         let lifetime_defs = try!(self.parse_lifetime_defs());
@@ -4151,7 +4176,7 @@ impl<'a> Parser<'a> {
 
                     let bounded_ty = try!(self.parse_ty());
 
-                    if try!(self.eat(&token::Colon) ){
+                    if self.eat(&token::Colon) {
                         let bounds = try!(self.parse_ty_param_bounds(BoundParsingMode::Bare));
                         let hi = self.last_span.hi;
                         let span = mk_sp(lo, hi);
@@ -4171,7 +4196,7 @@ impl<'a> Parser<'a> {
                         }));
 
                         parsed_something = true;
-                    } else if try!(self.eat(&token::Eq) ){
+                    } else if self.eat(&token::Eq) {
                         // let ty = try!(self.parse_ty());
                         let hi = self.last_span.hi;
                         let span = mk_sp(lo, hi);
@@ -4195,7 +4220,7 @@ impl<'a> Parser<'a> {
                 }
             };
 
-            if !try!(self.eat(&token::Comma) ){
+            if !self.eat(&token::Comma) {
                 break
             }
         }
@@ -4211,7 +4236,7 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_fn_args(&mut self, named_args: bool, allow_variadic: bool)
-                     -> PResult<(Vec<Arg> , bool)> {
+                     -> PResult<'a, (Vec<Arg> , bool)> {
         let sp = self.span;
         let mut args: Vec<Option<Arg>> =
             try!(self.parse_unspanned_seq(
@@ -4220,7 +4245,7 @@ impl<'a> Parser<'a> {
                 seq_sep_trailing_allowed(token::Comma),
                 |p| {
                     if p.token == token::DotDotDot {
-                        try!(p.bump());
+                        p.bump();
                         if allow_variadic {
                             if p.token != token::CloseDelim(token::Paren) {
                                 let span = p.span;
@@ -4260,7 +4285,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse the argument list and result type of a function declaration
-    pub fn parse_fn_decl(&mut self, allow_variadic: bool) -> PResult<P<FnDecl>> {
+    pub fn parse_fn_decl(&mut self, allow_variadic: bool) -> PResult<'a, P<FnDecl>> {
 
         let (args, variadic) = try!(self.parse_fn_args(true, allow_variadic));
         let ret_ty = try!(self.parse_ret_ty());
@@ -4279,10 +4304,10 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn expect_self_ident(&mut self) -> PResult<ast::Ident> {
+    fn expect_self_ident(&mut self) -> PResult<'a, ast::Ident> {
         match self.token {
             token::Ident(id, token::Plain) if id.name == special_idents::self_.name => {
-                try!(self.bump());
+                self.bump();
                 Ok(id)
             },
             _ => {
@@ -4300,10 +4325,10 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn expect_self_type_ident(&mut self) -> PResult<ast::Ident> {
+    fn expect_self_type_ident(&mut self) -> PResult<'a, ast::Ident> {
         match self.token {
             token::Ident(id, token::Plain) if id.name == special_idents::type_self.name => {
-                try!(self.bump());
+                self.bump();
                 Ok(id)
             },
             _ => {
@@ -4317,11 +4342,11 @@ impl<'a> Parser<'a> {
     /// Parse the argument list and result type of a function
     /// that may have a self type.
     fn parse_fn_decl_with_self<F>(&mut self,
-                                  parse_arg_fn: F) -> PResult<(ExplicitSelf, P<FnDecl>)> where
-        F: FnMut(&mut Parser) -> PResult<Arg>,
+                                  parse_arg_fn: F) -> PResult<'a, (ExplicitSelf, P<FnDecl>)> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a,  Arg>,
     {
-        fn maybe_parse_borrowed_explicit_self(this: &mut Parser)
-                                              -> PResult<ast::ExplicitSelf_> {
+        fn maybe_parse_borrowed_explicit_self<'b>(this: &mut Parser<'b>)
+                                                  -> PResult<'b,  ast::ExplicitSelf_> {
             // The following things are possible to see here:
             //
             //     fn(&mut self)
@@ -4332,22 +4357,22 @@ impl<'a> Parser<'a> {
             // We already know that the current token is `&`.
 
             if this.look_ahead(1, |t| t.is_keyword(keywords::SelfValue)) {
-                try!(this.bump());
+                this.bump();
                 Ok(SelfRegion(None, MutImmutable, try!(this.expect_self_ident())))
             } else if this.look_ahead(1, |t| t.is_mutability()) &&
                       this.look_ahead(2, |t| t.is_keyword(keywords::SelfValue)) {
-                try!(this.bump());
+                this.bump();
                 let mutability = try!(this.parse_mutability());
                 Ok(SelfRegion(None, mutability, try!(this.expect_self_ident())))
             } else if this.look_ahead(1, |t| t.is_lifetime()) &&
                       this.look_ahead(2, |t| t.is_keyword(keywords::SelfValue)) {
-                try!(this.bump());
+                this.bump();
                 let lifetime = try!(this.parse_lifetime());
                 Ok(SelfRegion(Some(lifetime), MutImmutable, try!(this.expect_self_ident())))
             } else if this.look_ahead(1, |t| t.is_lifetime()) &&
                       this.look_ahead(2, |t| t.is_mutability()) &&
                       this.look_ahead(3, |t| t.is_keyword(keywords::SelfValue)) {
-                try!(this.bump());
+                this.bump();
                 let lifetime = try!(this.parse_lifetime());
                 let mutability = try!(this.parse_mutability());
                 Ok(SelfRegion(Some(lifetime), mutability, try!(this.expect_self_ident())))
@@ -4375,7 +4400,7 @@ impl<'a> Parser<'a> {
             token::BinOp(token::Star) => {
                 // Possibly "*self" or "*mut self" -- not supported. Try to avoid
                 // emitting cryptic "unexpected token" errors.
-                try!(self.bump());
+                self.bump();
                 let _mutability = if self.token.is_mutability() {
                     try!(self.parse_mutability())
                 } else {
@@ -4384,7 +4409,7 @@ impl<'a> Parser<'a> {
                 if self.is_self_ident() {
                     let span = self.span;
                     self.span_err(span, "cannot pass self by raw pointer");
-                    try!(self.bump());
+                    self.bump();
                 }
                 // error case, making bogus self ident:
                 SelfValue(special_idents::self_)
@@ -4395,7 +4420,7 @@ impl<'a> Parser<'a> {
 
                     // Determine whether this is the fully explicit form, `self:
                     // TYPE`.
-                    if try!(self.eat(&token::Colon) ){
+                    if self.eat(&token::Colon) {
                         SelfExplicit(try!(self.parse_ty_sum()), self_ident)
                     } else {
                         SelfValue(self_ident)
@@ -4407,7 +4432,7 @@ impl<'a> Parser<'a> {
 
                     // Determine whether this is the fully explicit form,
                     // `self: TYPE`.
-                    if try!(self.eat(&token::Colon) ){
+                    if self.eat(&token::Colon) {
                         SelfExplicit(try!(self.parse_ty_sum()), self_ident)
                     } else {
                         SelfValue(self_ident)
@@ -4429,7 +4454,7 @@ impl<'a> Parser<'a> {
             // If we parsed a self type, expect a comma before the argument list.
             match self.token {
                 token::Comma => {
-                    try!(self.bump());
+                    self.bump();
                     let sep = seq_sep_trailing_allowed(token::Comma);
                     let mut fn_inputs = try!(self.parse_seq_to_before_end(
                         &token::CloseDelim(token::Paren),
@@ -4479,9 +4504,9 @@ impl<'a> Parser<'a> {
     }
 
     // parse the |arg, arg| header on a lambda
-    fn parse_fn_block_decl(&mut self) -> PResult<P<FnDecl>> {
+    fn parse_fn_block_decl(&mut self) -> PResult<'a, P<FnDecl>> {
         let inputs_captures = {
-            if try!(self.eat(&token::OrOr) ){
+            if self.eat(&token::OrOr) {
                 Vec::new()
             } else {
                 try!(self.expect(&token::BinOp(token::Or)));
@@ -4491,7 +4516,7 @@ impl<'a> Parser<'a> {
                     seq_sep_trailing_allowed(token::Comma),
                     |p| p.parse_fn_block_arg()
                 ));
-                try!(self.bump());
+                self.bump();
                 args
             }
         };
@@ -4505,7 +4530,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse the name and optional generic types of a function header.
-    fn parse_fn_header(&mut self) -> PResult<(Ident, ast::Generics)> {
+    fn parse_fn_header(&mut self) -> PResult<'a, (Ident, ast::Generics)> {
         let id = try!(self.parse_ident());
         let generics = try!(self.parse_generics());
         Ok((id, generics))
@@ -4529,7 +4554,7 @@ impl<'a> Parser<'a> {
                      unsafety: Unsafety,
                      constness: Constness,
                      abi: abi::Abi)
-                     -> PResult<ItemInfo> {
+                     -> PResult<'a, ItemInfo> {
         let (ident, mut generics) = try!(self.parse_fn_header());
         let decl = try!(self.parse_fn_decl(false));
         generics.where_clause = try!(self.parse_where_clause());
@@ -4552,13 +4577,14 @@ impl<'a> Parser<'a> {
     /// - `const unsafe fn`
     /// - `extern fn`
     /// - etc
-    pub fn parse_fn_front_matter(&mut self) -> PResult<(ast::Constness, ast::Unsafety, abi::Abi)> {
-        let is_const_fn = try!(self.eat_keyword(keywords::Const));
+    pub fn parse_fn_front_matter(&mut self)
+                                 -> PResult<'a, (ast::Constness, ast::Unsafety, abi::Abi)> {
+        let is_const_fn = self.eat_keyword(keywords::Const);
         let unsafety = try!(self.parse_unsafety());
         let (constness, unsafety, abi) = if is_const_fn {
             (Constness::Const, unsafety, abi::Rust)
         } else {
-            let abi = if try!(self.eat_keyword(keywords::Extern)) {
+            let abi = if self.eat_keyword(keywords::Extern) {
                 try!(self.parse_opt_abi()).unwrap_or(abi::C)
             } else {
                 abi::Rust
@@ -4570,13 +4596,13 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an impl item.
-    pub fn parse_impl_item(&mut self) -> PResult<P<ImplItem>> {
+    pub fn parse_impl_item(&mut self) -> PResult<'a, P<ImplItem>> {
         maybe_whole!(no_clone self, NtImplItem);
 
         let mut attrs = try!(self.parse_outer_attributes());
         let lo = self.span.lo;
         let vis = try!(self.parse_visibility());
-        let (name, node) = if try!(self.eat_keyword(keywords::Type)) {
+        let (name, node) = if self.eat_keyword(keywords::Type) {
             let name = try!(self.parse_ident());
             try!(self.expect(&token::Eq));
             let typ = try!(self.parse_ty_sum());
@@ -4610,9 +4636,22 @@ impl<'a> Parser<'a> {
     fn complain_if_pub_macro(&mut self, visa: Visibility, span: Span) {
         match visa {
             Public => {
-                self.span_err(span, "can't qualify macro invocation with `pub`");
-                self.fileline_help(span, "try adjusting the macro to put `pub` inside \
-                                      the invocation");
+                let is_macro_rules: bool = match self.token {
+                    token::Ident(sid, _) => sid.name == intern("macro_rules"),
+                    _ => false,
+                };
+                if is_macro_rules {
+                    self.diagnostic().struct_span_err(span, "can't qualify macro_rules \
+                                                             invocation with `pub`")
+                                     .fileline_help(span, "did you mean #[macro_export]?")
+                                     .emit();
+                } else {
+                    self.diagnostic().struct_span_err(span, "can't qualify macro \
+                                                             invocation with `pub`")
+                                     .fileline_help(span, "try adjusting the macro to put `pub` \
+                                                           inside the invocation")
+                                     .emit();
+                }
             }
             Inherited => (),
         }
@@ -4620,7 +4659,7 @@ impl<'a> Parser<'a> {
 
     /// Parse a method or a macro invocation in a trait impl.
     fn parse_impl_method(&mut self, vis: Visibility)
-                         -> PResult<(Ident, Vec<ast::Attribute>, ast::ImplItemKind)> {
+                         -> PResult<'a, (Ident, Vec<ast::Attribute>, ast::ImplItemKind)> {
         // code copied from parse_macro_use_or_failure... abstraction!
         if !self.token.is_any_keyword()
             && self.look_ahead(1, |t| *t == token::Not)
@@ -4669,7 +4708,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse trait Foo { ... }
-    fn parse_item_trait(&mut self, unsafety: Unsafety) -> PResult<ItemInfo> {
+    fn parse_item_trait(&mut self, unsafety: Unsafety) -> PResult<'a, ItemInfo> {
 
         let ident = try!(self.parse_ident());
         let mut tps = try!(self.parse_generics());
@@ -4687,7 +4726,7 @@ impl<'a> Parser<'a> {
     ///    impl<T> Foo { ... }
     ///    impl<T> ToString for &'static T { ... }
     ///    impl Send for .. {}
-    fn parse_item_impl(&mut self, unsafety: ast::Unsafety) -> PResult<ItemInfo> {
+    fn parse_item_impl(&mut self, unsafety: ast::Unsafety) -> PResult<'a, ItemInfo> {
         let impl_span = self.span;
 
         // First, parse type parameters if necessary.
@@ -4698,7 +4737,7 @@ impl<'a> Parser<'a> {
         let could_be_trait = self.token != token::OpenDelim(token::Paren);
 
         let neg_span = self.span;
-        let polarity = if try!(self.eat(&token::Not) ){
+        let polarity = if self.eat(&token::Not) {
             ast::ImplPolarity::Negative
         } else {
             ast::ImplPolarity::Positive
@@ -4708,7 +4747,7 @@ impl<'a> Parser<'a> {
         let mut ty = try!(self.parse_ty_sum());
 
         // Parse traits, if necessary.
-        let opt_trait = if could_be_trait && try!(self.eat_keyword(keywords::For) ){
+        let opt_trait = if could_be_trait && self.eat_keyword(keywords::For) {
             // New-style trait. Reinterpret the type as a trait.
             match ty.node {
                 TyPath(None, ref path) => {
@@ -4734,7 +4773,7 @@ impl<'a> Parser<'a> {
             None
         };
 
-        if opt_trait.is_some() && try!(self.eat(&token::DotDot) ){
+        if opt_trait.is_some() && self.eat(&token::DotDot) {
             if generics.is_parameterized() {
                 self.span_err(impl_span, "default trait implementations are not \
                                           allowed to have generics");
@@ -4754,7 +4793,7 @@ impl<'a> Parser<'a> {
             let attrs = try!(self.parse_inner_attributes());
 
             let mut impl_items = vec![];
-            while !try!(self.eat(&token::CloseDelim(token::Brace))) {
+            while !self.eat(&token::CloseDelim(token::Brace)) {
                 impl_items.push(try!(self.parse_impl_item()));
             }
 
@@ -4765,15 +4804,15 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a::B<String,i32>
-    fn parse_trait_ref(&mut self) -> PResult<TraitRef> {
+    fn parse_trait_ref(&mut self) -> PResult<'a, TraitRef> {
         Ok(ast::TraitRef {
             path: try!(self.parse_path(LifetimeAndTypesWithoutColons)),
             ref_id: ast::DUMMY_NODE_ID,
         })
     }
 
-    fn parse_late_bound_lifetime_defs(&mut self) -> PResult<Vec<ast::LifetimeDef>> {
-        if try!(self.eat_keyword(keywords::For) ){
+    fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> {
+        if self.eat_keyword(keywords::For) {
             try!(self.expect(&token::Lt));
             let lifetime_defs = try!(self.parse_lifetime_defs());
             try!(self.expect_gt());
@@ -4784,7 +4823,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse for<'l> a::B<String,i32>
-    fn parse_poly_trait_ref(&mut self) -> PResult<PolyTraitRef> {
+    fn parse_poly_trait_ref(&mut self) -> PResult<'a, PolyTraitRef> {
         let lo = self.span.lo;
         let lifetime_defs = try!(self.parse_late_bound_lifetime_defs());
 
@@ -4796,7 +4835,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse struct Foo { ... }
-    fn parse_item_struct(&mut self) -> PResult<ItemInfo> {
+    fn parse_item_struct(&mut self) -> PResult<'a, ItemInfo> {
         let class_name = try!(self.parse_ident());
         let mut generics = try!(self.parse_generics());
 
@@ -4816,7 +4855,7 @@ impl<'a> Parser<'a> {
 
         let vdata = if self.token.is_keyword(keywords::Where) {
             generics.where_clause = try!(self.parse_where_clause());
-            if try!(self.eat(&token::Semi)) {
+            if self.eat(&token::Semi) {
                 // If we see a: `struct Foo<T> where T: Copy;` style decl.
                 VariantData::Unit(ast::DUMMY_NODE_ID)
             } else {
@@ -4825,7 +4864,7 @@ impl<'a> Parser<'a> {
                                     ast::DUMMY_NODE_ID)
             }
         // No `where` so: `struct Foo<T>;`
-        } else if try!(self.eat(&token::Semi) ){
+        } else if self.eat(&token::Semi) {
             VariantData::Unit(ast::DUMMY_NODE_ID)
         // Record-style struct definition
         } else if self.token == token::OpenDelim(token::Brace) {
@@ -4847,14 +4886,16 @@ impl<'a> Parser<'a> {
         Ok((class_name, ItemStruct(vdata, generics), None))
     }
 
-    pub fn parse_record_struct_body(&mut self, parse_pub: ParsePub) -> PResult<Vec<StructField>> {
+    pub fn parse_record_struct_body(&mut self,
+                                    parse_pub: ParsePub)
+                                    -> PResult<'a, Vec<StructField>> {
         let mut fields = Vec::new();
-        if try!(self.eat(&token::OpenDelim(token::Brace)) ){
+        if self.eat(&token::OpenDelim(token::Brace)) {
             while self.token != token::CloseDelim(token::Brace) {
                 fields.push(try!(self.parse_struct_decl_field(parse_pub)));
             }
 
-            try!(self.bump());
+            self.bump();
         } else {
             let token_str = self.this_token_to_string();
             return Err(self.fatal(&format!("expected `where`, or `{{` after struct \
@@ -4865,7 +4906,9 @@ impl<'a> Parser<'a> {
         Ok(fields)
     }
 
-    pub fn parse_tuple_struct_body(&mut self, parse_pub: ParsePub) -> PResult<Vec<StructField>> {
+    pub fn parse_tuple_struct_body(&mut self,
+                                   parse_pub: ParsePub)
+                                   -> PResult<'a, Vec<StructField>> {
         // This is the case where we find `struct Foo<T>(T) where T: Copy;`
         // Unit like structs are handled in parse_item_struct function
         let fields = try!(self.parse_unspanned_seq(
@@ -4897,11 +4940,11 @@ impl<'a> Parser<'a> {
     pub fn parse_single_struct_field(&mut self,
                                      vis: Visibility,
                                      attrs: Vec<Attribute> )
-                                     -> PResult<StructField> {
+                                     -> PResult<'a, StructField> {
         let a_var = try!(self.parse_name_and_ty(vis, attrs));
         match self.token {
             token::Comma => {
-                try!(self.bump());
+                self.bump();
             }
             token::CloseDelim(token::Brace) => {}
             _ => {
@@ -4917,11 +4960,11 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an element of a struct definition
-    fn parse_struct_decl_field(&mut self, parse_pub: ParsePub) -> PResult<StructField> {
+    fn parse_struct_decl_field(&mut self, parse_pub: ParsePub) -> PResult<'a, StructField> {
 
         let attrs = try!(self.parse_outer_attributes());
 
-        if try!(self.eat_keyword(keywords::Pub) ){
+        if self.eat_keyword(keywords::Pub) {
             if parse_pub == ParsePub::No {
                 let span = self.last_span;
                 self.span_err(span, "`pub` is not allowed here");
@@ -4933,19 +4976,19 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse visibility: PUB or nothing
-    fn parse_visibility(&mut self) -> PResult<Visibility> {
-        if try!(self.eat_keyword(keywords::Pub)) { Ok(Public) }
+    fn parse_visibility(&mut self) -> PResult<'a, Visibility> {
+        if self.eat_keyword(keywords::Pub) { Ok(Public) }
         else { Ok(Inherited) }
     }
 
     /// Given a termination token, parse all of the items in a module
-    fn parse_mod_items(&mut self, term: &token::Token, inner_lo: BytePos) -> PResult<Mod> {
+    fn parse_mod_items(&mut self, term: &token::Token, inner_lo: BytePos) -> PResult<'a, Mod> {
         let mut items = vec![];
         while let Some(item) = try!(self.parse_item()) {
             items.push(item);
         }
 
-        if !try!(self.eat(term)) {
+        if !self.eat(term) {
             let token_str = self.this_token_to_string();
             return Err(self.fatal(&format!("expected item, found `{}`", token_str)));
         }
@@ -4962,7 +5005,7 @@ impl<'a> Parser<'a> {
         })
     }
 
-    fn parse_item_const(&mut self, m: Option<Mutability>) -> PResult<ItemInfo> {
+    fn parse_item_const(&mut self, m: Option<Mutability>) -> PResult<'a, ItemInfo> {
         let id = try!(self.parse_ident());
         try!(self.expect(&token::Colon));
         let ty = try!(self.parse_ty_sum());
@@ -4977,11 +5020,11 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a `mod <foo> { ... }` or `mod <foo>;` item
-    fn parse_item_mod(&mut self, outer_attrs: &[Attribute]) -> PResult<ItemInfo> {
+    fn parse_item_mod(&mut self, outer_attrs: &[Attribute]) -> PResult<'a, ItemInfo> {
         let id_span = self.span;
         let id = try!(self.parse_ident());
         if self.check(&token::Semi) {
-            try!(self.bump());
+            self.bump();
             // This mod is in an external file. Let's go get it!
             let (m, attrs) = try!(self.eval_src_mod(id, outer_attrs, id_span));
             Ok((id, m, Some(attrs)))
@@ -5056,7 +5099,7 @@ impl<'a> Parser<'a> {
     fn submod_path(&mut self,
                    id: ast::Ident,
                    outer_attrs: &[ast::Attribute],
-                   id_sp: Span) -> PResult<ModulePathSuccess> {
+                   id_sp: Span) -> PResult<'a, ModulePathSuccess> {
         let mut prefix = PathBuf::from(&self.sess.codemap().span_to_filename(self.span));
         prefix.pop();
         let mut dir_path = prefix;
@@ -5071,21 +5114,23 @@ impl<'a> Parser<'a> {
         let paths = Parser::default_submod_path(id, &dir_path, self.sess.codemap());
 
         if !self.owns_directory {
-            self.span_err(id_sp, "cannot declare a new module at this location");
+            let mut err = self.diagnostic().struct_span_err(id_sp,
+                "cannot declare a new module at this location");
             let this_module = match self.mod_path_stack.last() {
                 Some(name) => name.to_string(),
                 None => self.root_module_name.as_ref().unwrap().clone(),
             };
-            self.span_note(id_sp,
-                           &format!("maybe move this module `{0}` to its own directory \
+            err.span_note(id_sp,
+                          &format!("maybe move this module `{0}` to its own directory \
                                      via `{0}/mod.rs`",
                                     this_module));
             if paths.path_exists {
-                self.span_note(id_sp,
-                               &format!("... or maybe `use` the module `{}` instead \
-                                         of possibly redeclaring it",
-                                        paths.name));
+                err.span_note(id_sp,
+                              &format!("... or maybe `use` the module `{}` instead \
+                                        of possibly redeclaring it",
+                                       paths.name));
             }
+            err.emit();
             self.abort_if_errors();
         }
 
@@ -5100,7 +5145,7 @@ impl<'a> Parser<'a> {
                     id: ast::Ident,
                     outer_attrs: &[ast::Attribute],
                     id_sp: Span)
-                    -> PResult<(ast::Item_, Vec<ast::Attribute> )> {
+                    -> PResult<'a, (ast::Item_, Vec<ast::Attribute> )> {
         let ModulePathSuccess { path, owns_directory } = try!(self.submod_path(id,
                                                                                outer_attrs,
                                                                                id_sp));
@@ -5115,7 +5160,7 @@ impl<'a> Parser<'a> {
                               path: PathBuf,
                               owns_directory: bool,
                               name: String,
-                              id_sp: Span) -> PResult<(ast::Item_, Vec<ast::Attribute> )> {
+                              id_sp: Span) -> PResult<'a, (ast::Item_, 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) => {
@@ -5148,7 +5193,7 @@ impl<'a> Parser<'a> {
 
     /// Parse a function declaration from a foreign module
     fn parse_item_foreign_fn(&mut self, vis: ast::Visibility, lo: BytePos,
-                             attrs: Vec<Attribute>) -> PResult<P<ForeignItem>> {
+                             attrs: Vec<Attribute>) -> PResult<'a, P<ForeignItem>> {
         try!(self.expect_keyword(keywords::Fn));
 
         let (ident, mut generics) = try!(self.parse_fn_header());
@@ -5168,9 +5213,9 @@ impl<'a> Parser<'a> {
 
     /// Parse a static item from a foreign module
     fn parse_item_foreign_static(&mut self, vis: ast::Visibility, lo: BytePos,
-                                 attrs: Vec<Attribute>) -> PResult<P<ForeignItem>> {
+                                 attrs: Vec<Attribute>) -> PResult<'a, P<ForeignItem>> {
         try!(self.expect_keyword(keywords::Static));
-        let mutbl = try!(self.eat_keyword(keywords::Mut));
+        let mutbl = self.eat_keyword(keywords::Mut);
 
         let ident = try!(self.parse_ident());
         try!(self.expect(&token::Colon));
@@ -5197,7 +5242,7 @@ impl<'a> Parser<'a> {
                                lo: BytePos,
                                visibility: Visibility,
                                attrs: Vec<Attribute>)
-                                -> PResult<P<Item>> {
+                                -> PResult<'a, P<Item>> {
 
         let crate_name = try!(self.parse_ident());
         let (maybe_path, ident) = if let Some(ident) = try!(self.parse_rename()) {
@@ -5238,7 +5283,7 @@ impl<'a> Parser<'a> {
                               opt_abi: Option<abi::Abi>,
                               visibility: Visibility,
                               mut attrs: Vec<Attribute>)
-                              -> PResult<P<Item>> {
+                              -> PResult<'a, P<Item>> {
         try!(self.expect(&token::OpenDelim(token::Brace)));
 
         let abi = opt_abi.unwrap_or(abi::C);
@@ -5265,7 +5310,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse type Foo = Bar;
-    fn parse_item_type(&mut self) -> PResult<ItemInfo> {
+    fn parse_item_type(&mut self) -> PResult<'a, ItemInfo> {
         let ident = try!(self.parse_ident());
         let mut tps = try!(self.parse_generics());
         tps.where_clause = try!(self.parse_where_clause());
@@ -5276,7 +5321,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse the part of an "enum" decl following the '{'
-    fn parse_enum_def(&mut self, _generics: &ast::Generics) -> PResult<EnumDef> {
+    fn parse_enum_def(&mut self, _generics: &ast::Generics) -> PResult<'a, EnumDef> {
         let mut variants = Vec::new();
         let mut all_nullary = true;
         let mut any_disr = None;
@@ -5296,7 +5341,7 @@ impl<'a> Parser<'a> {
                 all_nullary = false;
                 struct_def = VariantData::Tuple(try!(self.parse_tuple_struct_body(ParsePub::No)),
                                                 ast::DUMMY_NODE_ID);
-            } else if try!(self.eat(&token::Eq) ){
+            } else if self.eat(&token::Eq) {
                 disr_expr = Some(try!(self.parse_expr()));
                 any_disr = disr_expr.as_ref().map(|expr| expr.span);
                 struct_def = VariantData::Unit(ast::DUMMY_NODE_ID);
@@ -5312,7 +5357,7 @@ impl<'a> Parser<'a> {
             };
             variants.push(P(spanned(vlo, self.last_span.hi, vr)));
 
-            if !try!(self.eat(&token::Comma)) { break; }
+            if !self.eat(&token::Comma) { break; }
         }
         try!(self.expect(&token::CloseDelim(token::Brace)));
         match any_disr {
@@ -5326,7 +5371,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an "enum" declaration
-    fn parse_item_enum(&mut self) -> PResult<ItemInfo> {
+    fn parse_item_enum(&mut self) -> PResult<'a, ItemInfo> {
         let id = try!(self.parse_ident());
         let mut generics = try!(self.parse_generics());
         generics.where_clause = try!(self.parse_where_clause());
@@ -5338,12 +5383,12 @@ impl<'a> Parser<'a> {
 
     /// Parses a string as an ABI spec on an extern type or module. Consumes
     /// the `extern` keyword, if one is found.
-    fn parse_opt_abi(&mut self) -> PResult<Option<abi::Abi>> {
+    fn parse_opt_abi(&mut self) -> PResult<'a, Option<abi::Abi>> {
         match self.token {
             token::Literal(token::Str_(s), suf) | token::Literal(token::StrRaw(s, _), suf) => {
                 let sp = self.span;
                 self.expect_no_suffix(sp, "ABI spec", suf);
-                try!(self.bump());
+                self.bump();
                 match abi::lookup(&s.as_str()) {
                     Some(abi) => Ok(Some(abi)),
                     None => {
@@ -5367,7 +5412,7 @@ impl<'a> Parser<'a> {
     /// NB: this function no longer parses the items inside an
     /// extern crate.
     fn parse_item_(&mut self, attrs: Vec<Attribute>,
-                   macros_allowed: bool, attributes_allowed: bool) -> PResult<Option<P<Item>>> {
+                   macros_allowed: bool, attributes_allowed: bool) -> PResult<'a, Option<P<Item>>> {
         let nt_item = match self.token {
             token::Interpolated(token::NtItem(ref item)) => {
                 Some((**item).clone())
@@ -5376,7 +5421,7 @@ impl<'a> Parser<'a> {
         };
         match nt_item {
             Some(mut item) => {
-                try!(self.bump());
+                self.bump();
                 let mut attrs = attrs;
                 mem::swap(&mut item.attrs, &mut attrs);
                 item.attrs.extend(attrs);
@@ -5389,7 +5434,7 @@ impl<'a> Parser<'a> {
 
         let visibility = try!(self.parse_visibility());
 
-        if try!(self.eat_keyword(keywords::Use) ){
+        if self.eat_keyword(keywords::Use) {
             // USE ITEM
             let item_ = ItemUse(try!(self.parse_view_path()));
             try!(self.expect(&token::Semi));
@@ -5404,14 +5449,14 @@ impl<'a> Parser<'a> {
             return Ok(Some(item));
         }
 
-        if try!(self.eat_keyword(keywords::Extern)) {
-            if try!(self.eat_keyword(keywords::Crate)) {
+        if self.eat_keyword(keywords::Extern) {
+            if self.eat_keyword(keywords::Crate) {
                 return Ok(Some(try!(self.parse_item_extern_crate(lo, visibility, attrs))));
             }
 
             let opt_abi = try!(self.parse_opt_abi());
 
-            if try!(self.eat_keyword(keywords::Fn) ){
+            if self.eat_keyword(keywords::Fn) {
                 // EXTERN FUNCTION ITEM
                 let abi = opt_abi.unwrap_or(abi::C);
                 let (ident, item_, extra_attrs) =
@@ -5428,12 +5473,12 @@ impl<'a> Parser<'a> {
                 return Ok(Some(try!(self.parse_item_foreign_mod(lo, opt_abi, visibility, attrs))));
             }
 
-            try!(self.expect_one_of(&[], &[]));
+            try!(self.unexpected());
         }
 
-        if try!(self.eat_keyword(keywords::Static) ){
+        if self.eat_keyword(keywords::Static) {
             // STATIC ITEM
-            let m = if try!(self.eat_keyword(keywords::Mut)) {MutMutable} else {MutImmutable};
+            let m = if self.eat_keyword(keywords::Mut) {MutMutable} else {MutImmutable};
             let (ident, item_, extra_attrs) = try!(self.parse_item_const(Some(m)));
             let last_span = self.last_span;
             let item = self.mk_item(lo,
@@ -5444,17 +5489,17 @@ impl<'a> Parser<'a> {
                                     maybe_append(attrs, extra_attrs));
             return Ok(Some(item));
         }
-        if try!(self.eat_keyword(keywords::Const) ){
+        if self.eat_keyword(keywords::Const) {
             if self.check_keyword(keywords::Fn)
                 || (self.check_keyword(keywords::Unsafe)
                     && self.look_ahead(1, |t| t.is_keyword(keywords::Fn))) {
                 // CONST FUNCTION ITEM
-                let unsafety = if try!(self.eat_keyword(keywords::Unsafe) ){
+                let unsafety = if self.eat_keyword(keywords::Unsafe) {
                     Unsafety::Unsafe
                 } else {
                     Unsafety::Normal
                 };
-                try!(self.bump());
+                self.bump();
                 let (ident, item_, extra_attrs) =
                     try!(self.parse_item_fn(unsafety, Constness::Const, abi::Rust));
                 let last_span = self.last_span;
@@ -5468,10 +5513,11 @@ impl<'a> Parser<'a> {
             }
 
             // CONST ITEM
-            if try!(self.eat_keyword(keywords::Mut) ){
+            if self.eat_keyword(keywords::Mut) {
                 let last_span = self.last_span;
-                self.span_err(last_span, "const globals cannot be mutable");
-                self.fileline_help(last_span, "did you mean to declare a static?");
+                self.diagnostic().struct_span_err(last_span, "const globals cannot be mutable")
+                                 .fileline_help(last_span, "did you mean to declare a static?")
+                                 .emit();
             }
             let (ident, item_, extra_attrs) = try!(self.parse_item_const(None));
             let last_span = self.last_span;
@@ -5518,7 +5564,7 @@ impl<'a> Parser<'a> {
         }
         if self.check_keyword(keywords::Fn) {
             // FUNCTION ITEM
-            try!(self.bump());
+            self.bump();
             let (ident, item_, extra_attrs) =
                 try!(self.parse_item_fn(Unsafety::Normal, Constness::NotConst, abi::Rust));
             let last_span = self.last_span;
@@ -5533,8 +5579,8 @@ impl<'a> Parser<'a> {
         if self.check_keyword(keywords::Unsafe)
             && self.look_ahead(1, |t| *t != token::OpenDelim(token::Brace)) {
             // UNSAFE FUNCTION ITEM
-            try!(self.bump());
-            let abi = if try!(self.eat_keyword(keywords::Extern) ){
+            self.bump();
+            let abi = if self.eat_keyword(keywords::Extern) {
                 try!(self.parse_opt_abi()).unwrap_or(abi::C)
             } else {
                 abi::Rust
@@ -5551,7 +5597,7 @@ impl<'a> Parser<'a> {
                                     maybe_append(attrs, extra_attrs));
             return Ok(Some(item));
         }
-        if try!(self.eat_keyword(keywords::Mod) ){
+        if self.eat_keyword(keywords::Mod) {
             // MODULE ITEM
             let (ident, item_, extra_attrs) =
                 try!(self.parse_item_mod(&attrs[..]));
@@ -5564,7 +5610,7 @@ impl<'a> Parser<'a> {
                                     maybe_append(attrs, extra_attrs));
             return Ok(Some(item));
         }
-        if try!(self.eat_keyword(keywords::Type) ){
+        if self.eat_keyword(keywords::Type) {
             // TYPE ITEM
             let (ident, item_, extra_attrs) = try!(self.parse_item_type());
             let last_span = self.last_span;
@@ -5576,7 +5622,7 @@ impl<'a> Parser<'a> {
                                     maybe_append(attrs, extra_attrs));
             return Ok(Some(item));
         }
-        if try!(self.eat_keyword(keywords::Enum) ){
+        if self.eat_keyword(keywords::Enum) {
             // ENUM ITEM
             let (ident, item_, extra_attrs) = try!(self.parse_item_enum());
             let last_span = self.last_span;
@@ -5588,7 +5634,7 @@ impl<'a> Parser<'a> {
                                     maybe_append(attrs, extra_attrs));
             return Ok(Some(item));
         }
-        if try!(self.eat_keyword(keywords::Trait) ){
+        if self.eat_keyword(keywords::Trait) {
             // TRAIT ITEM
             let (ident, item_, extra_attrs) =
                 try!(self.parse_item_trait(ast::Unsafety::Normal));
@@ -5601,7 +5647,7 @@ impl<'a> Parser<'a> {
                                     maybe_append(attrs, extra_attrs));
             return Ok(Some(item));
         }
-        if try!(self.eat_keyword(keywords::Impl) ){
+        if self.eat_keyword(keywords::Impl) {
             // IMPL ITEM
             let (ident, item_, extra_attrs) = try!(self.parse_item_impl(ast::Unsafety::Normal));
             let last_span = self.last_span;
@@ -5613,7 +5659,7 @@ impl<'a> Parser<'a> {
                                     maybe_append(attrs, extra_attrs));
             return Ok(Some(item));
         }
-        if try!(self.eat_keyword(keywords::Struct) ){
+        if self.eat_keyword(keywords::Struct) {
             // STRUCT ITEM
             let (ident, item_, extra_attrs) = try!(self.parse_item_struct());
             let last_span = self.last_span;
@@ -5629,7 +5675,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a foreign item.
-    fn parse_foreign_item(&mut self) -> PResult<Option<P<ForeignItem>>> {
+    fn parse_foreign_item(&mut self) -> PResult<'a, Option<P<ForeignItem>>> {
         let attrs = try!(self.parse_outer_attributes());
         let lo = self.span.lo;
         let visibility = try!(self.parse_visibility());
@@ -5660,7 +5706,7 @@ impl<'a> Parser<'a> {
         attributes_allowed: bool,
         lo: BytePos,
         visibility: Visibility
-    ) -> PResult<Option<P<Item>>> {
+    ) -> PResult<'a, Option<P<Item>>> {
         if macros_allowed && !self.token.is_any_keyword()
                 && self.look_ahead(1, |t| *t == token::Not)
                 && (self.look_ahead(2, |t| t.is_plain_ident())
@@ -5697,7 +5743,7 @@ impl<'a> Parser<'a> {
                                                          self.last_span.hi) };
 
             if delim != token::Brace {
-                if !try!(self.eat(&token::Semi) ){
+                if !self.eat(&token::Semi) {
                     let last_span = self.last_span;
                     self.span_err(last_span,
                                   "macros that expand to items must either \
@@ -5732,7 +5778,7 @@ impl<'a> Parser<'a> {
         Ok(None)
     }
 
-    pub fn parse_item(&mut self) -> PResult<Option<P<Item>>> {
+    pub fn parse_item(&mut self) -> PResult<'a, Option<P<Item>>> {
         let attrs = try!(self.parse_outer_attributes());
         self.parse_item_(attrs, true, false)
     }
@@ -5743,12 +5789,12 @@ impl<'a> Parser<'a> {
     /// | MOD? non_global_path MOD_SEP LBRACE ident_seq RBRACE
     /// | MOD? non_global_path MOD_SEP STAR
     /// | MOD? non_global_path
-    fn parse_view_path(&mut self) -> PResult<P<ViewPath>> {
+    fn parse_view_path(&mut self) -> PResult<'a, P<ViewPath>> {
         let lo = self.span.lo;
 
         // Allow a leading :: because the paths are absolute either way.
         // This occurs with "use $crate::..." in macros.
-        try!(self.eat(&token::ModSep));
+        self.eat(&token::ModSep);
 
         if self.check(&token::OpenDelim(token::Brace)) {
             // use {foo,bar}
@@ -5770,7 +5816,7 @@ impl<'a> Parser<'a> {
         if let token::ModSep = self.token {
             // foo::bar or foo::{a,b,c} or foo::*
             while self.check(&token::ModSep) {
-                try!(self.bump());
+                self.bump();
 
                 match self.token {
                   token::Ident(..) => {
@@ -5801,7 +5847,7 @@ impl<'a> Parser<'a> {
 
                   // foo::bar::*
                   token::BinOp(token::Star) => {
-                    try!(self.bump());
+                    self.bump();
                     let path = ast::Path {
                         span: mk_sp(lo, self.span.hi),
                         global: false,
@@ -5839,8 +5885,8 @@ impl<'a> Parser<'a> {
         Ok(P(spanned(lo, self.last_span.hi, ViewPathSimple(rename_to, path))))
     }
 
-    fn parse_rename(&mut self) -> PResult<Option<Ident>> {
-        if try!(self.eat_keyword(keywords::As)) {
+    fn parse_rename(&mut self) -> PResult<'a, Option<Ident>> {
+        if self.eat_keyword(keywords::As) {
             self.parse_ident().map(Some)
         } else {
             Ok(None)
@@ -5849,7 +5895,7 @@ impl<'a> Parser<'a> {
 
     /// Parses a source module as a crate. This is the main
     /// entry point for the parser.
-    pub fn parse_crate_mod(&mut self) -> PResult<Crate> {
+    pub fn parse_crate_mod(&mut self) -> PResult<'a, Crate> {
         let lo = self.span.lo;
         Ok(ast::Crate {
             attrs: try!(self.parse_inner_attributes()),
@@ -5861,9 +5907,9 @@ impl<'a> Parser<'a> {
     }
 
     pub fn parse_optional_str(&mut self)
-                              -> PResult<Option<(InternedString,
-                                                 ast::StrStyle,
-                                                 Option<ast::Name>)>> {
+                              -> Option<(InternedString,
+                                         ast::StrStyle,
+                                         Option<ast::Name>)> {
         let ret = match self.token {
             token::Literal(token::Str_(s), suf) => {
                 (self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)), ast::CookedStr, suf)
@@ -5871,14 +5917,14 @@ impl<'a> Parser<'a> {
             token::Literal(token::StrRaw(s, n), suf) => {
                 (self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)), ast::RawStr(n), suf)
             }
-            _ => return Ok(None)
+            _ => return None
         };
-        try!(self.bump());
-        Ok(Some(ret))
+        self.bump();
+        Some(ret)
     }
 
-    pub fn parse_str(&mut self) -> PResult<(InternedString, StrStyle)> {
-        match try!(self.parse_optional_str()) {
+    pub fn parse_str(&mut self) -> PResult<'a, (InternedString, StrStyle)> {
+        match self.parse_optional_str() {
             Some((s, style, suf)) => {
                 let sp = self.last_span;
                 self.expect_no_suffix(sp, "string literal", suf);
index 17b7d8dbaece9588d1dbcf846a0ac32391381619..242626154fc8c4066ff0c617f3bd277274aad598 100644 (file)
@@ -377,7 +377,7 @@ pub enum Nonterminal {
     NtPat(P<ast::Pat>),
     NtExpr(P<ast::Expr>),
     NtTy(P<ast::Ty>),
-    NtIdent(Box<ast::Ident>, IdentStyle),
+    NtIdent(Box<ast::SpannedIdent>, IdentStyle),
     /// Stuff inside brackets for attributes
     NtMeta(P<ast::MetaItem>),
     NtPath(Box<ast::Path>),
@@ -495,9 +495,6 @@ macro_rules! declare_special_idents_and_keywords {(
     }
 
     fn mk_fresh_ident_interner() -> IdentInterner {
-        // The indices here must correspond to the numbers in
-        // special_idents, in Keyword to_name(), and in static
-        // constants below.
         let mut init_vec = Vec::new();
         $(init_vec.push($si_str);)*
         $(init_vec.push($sk_str);)*
index f7105951296724c550c2733c7571fb4d025ceee4..67817ee0740e6d38a0f5bbdba768d778e31b79bb 100644 (file)
@@ -15,13 +15,11 @@ use ast::{self, TokenTree};
 use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
 use ast::Attribute;
 use attr::ThinAttributesExt;
-use ast_util;
 use util::parser::AssocOp;
 use attr;
-use owned_slice::OwnedSlice;
 use attr::{AttrMetaMethods, AttributeMethods};
 use codemap::{self, CodeMap, BytePos};
-use diagnostic;
+use errors;
 use parse::token::{self, BinOpToken, Token, InternedString};
 use parse::lexer::comments;
 use parse;
@@ -100,7 +98,7 @@ pub const DEFAULT_COLUMNS: usize = 78;
 /// it can scan the input text for comments and literals to
 /// copy forward.
 pub fn print_crate<'a>(cm: &'a CodeMap,
-                       span_diagnostic: &diagnostic::SpanHandler,
+                       span_diagnostic: &errors::Handler,
                        krate: &ast::Crate,
                        filename: String,
                        input: &mut Read,
@@ -140,7 +138,7 @@ pub fn print_crate<'a>(cm: &'a CodeMap,
 
 impl<'a> State<'a> {
     pub fn new_from_input(cm: &'a CodeMap,
-                          span_diagnostic: &diagnostic::SpanHandler,
+                          span_diagnostic: &errors::Handler,
                           filename: String,
                           input: &mut Read,
                           out: Box<Write+'a>,
@@ -296,7 +294,7 @@ pub fn token_to_string(tok: &Token) -> String {
             token::NtBlock(ref e)       => block_to_string(&**e),
             token::NtStmt(ref e)        => stmt_to_string(&**e),
             token::NtPat(ref e)         => pat_to_string(&**e),
-            token::NtIdent(ref e, _)    => ident_to_string(**e),
+            token::NtIdent(ref e, _)    => ident_to_string(e.node),
             token::NtTT(ref e)          => tt_to_string(&**e),
             token::NtArm(ref e)         => arm_to_string(&*e),
             token::NtImplItem(ref e)    => impl_item_to_string(&**e),
@@ -446,7 +444,7 @@ fn needs_parentheses(expr: &ast::Expr) -> bool {
         ast::ExprAssign(..) | ast::ExprBinary(..) |
         ast::ExprClosure(..) |
         ast::ExprAssignOp(..) | ast::ExprCast(..) |
-        ast::ExprInPlace(..) => true,
+        ast::ExprInPlace(..) | ast::ExprType(..) => true,
         _ => false,
     }
 }
@@ -649,15 +647,15 @@ pub trait PrintState<'a> {
                 match t {
                     ast::SignedIntLit(st, ast::Plus) => {
                         word(self.writer(),
-                             &ast_util::int_val_to_string(st, i as i64))
+                             &st.val_to_string(i as i64))
                     }
                     ast::SignedIntLit(st, ast::Minus) => {
-                        let istr = ast_util::int_val_to_string(st, -(i as i64));
+                        let istr = st.val_to_string(-(i as i64));
                         word(self.writer(),
                              &format!("-{}", istr))
                     }
                     ast::UnsignedIntLit(ut) => {
-                        word(self.writer(), &ast_util::uint_val_to_string(ut, i))
+                        word(self.writer(), &ut.val_to_string(i))
                     }
                     ast::UnsuffixedIntLit(ast::Plus) => {
                         word(self.writer(), &format!("{}", i))
@@ -672,7 +670,7 @@ pub trait PrintState<'a> {
                      &format!(
                          "{}{}",
                          &f,
-                         &ast_util::float_ty_to_string(t)))
+                         t.ty_to_string()))
             }
             ast::LitFloatUnsuffixed(ref f) => word(self.writer(), &f[..]),
             ast::LitBool(val) => {
@@ -858,8 +856,8 @@ impl<'a> State<'a> {
                    indented: usize) -> io::Result<()> {
         self.bclose_maybe_open(span, indented, true)
     }
-    pub fn bclose_maybe_open (&mut self, span: codemap::Span,
-                              indented: usize, close_box: bool) -> io::Result<()> {
+    pub fn bclose_maybe_open(&mut self, span: codemap::Span,
+                             indented: usize, close_box: bool) -> io::Result<()> {
         try!(self.maybe_print_comment(span.hi));
         try!(self.break_offset_if_not_bol(1, -(indented as isize)));
         try!(word(&mut self.s, "}"));
@@ -1002,7 +1000,7 @@ impl<'a> State<'a> {
             ast::TyBareFn(ref f) => {
                 let generics = ast::Generics {
                     lifetimes: f.lifetimes.clone(),
-                    ty_params: OwnedSlice::empty(),
+                    ty_params: P::empty(),
                     where_clause: ast::WhereClause {
                         id: ast::DUMMY_NODE_ID,
                         predicates: Vec::new(),
@@ -1528,7 +1526,7 @@ impl<'a> State<'a> {
 
     pub fn print_variant(&mut self, v: &ast::Variant) -> io::Result<()> {
         try!(self.head(""));
-        let generics = ast_util::empty_generics();
+        let generics = ast::Generics::default();
         try!(self.print_struct(&v.node.data, &generics, v.node.name, v.span, false));
         match v.node.disr_expr {
             Some(ref d) => {
@@ -1948,7 +1946,7 @@ impl<'a> State<'a> {
             try!(self.print_expr(lhs));
         }
         try!(space(&mut self.s));
-        try!(self.word_space(ast_util::binop_to_string(op.node)));
+        try!(self.word_space(op.node.to_string()));
         if self.check_expr_bin_needs_paren(rhs, op) {
             self.print_expr_maybe_paren(rhs)
         } else {
@@ -1959,7 +1957,7 @@ impl<'a> State<'a> {
     fn print_expr_unary(&mut self,
                         op: ast::UnOp,
                         expr: &ast::Expr) -> io::Result<()> {
-        try!(word(&mut self.s, ast_util::unop_to_string(op)));
+        try!(word(&mut self.s, ast::UnOp::to_string(op)));
         self.print_expr_maybe_paren(expr)
     }
 
@@ -2037,6 +2035,11 @@ impl<'a> State<'a> {
                 try!(self.word_space("as"));
                 try!(self.print_type(&**ty));
             }
+            ast::ExprType(ref expr, ref ty) => {
+                try!(self.print_expr(&**expr));
+                try!(self.word_space(":"));
+                try!(self.print_type(&**ty));
+            }
             ast::ExprIf(ref test, ref blk, ref elseopt) => {
                 try!(self.print_if(&**test, &**blk, elseopt.as_ref().map(|e| &**e)));
             }
@@ -2151,7 +2154,7 @@ impl<'a> State<'a> {
             ast::ExprAssignOp(op, ref lhs, ref rhs) => {
                 try!(self.print_expr(&**lhs));
                 try!(space(&mut self.s));
-                try!(word(&mut self.s, ast_util::binop_to_string(op.node)));
+                try!(word(&mut self.s, op.node.to_string()));
                 try!(self.word_space("="));
                 try!(self.print_expr(&**rhs));
             }
@@ -2219,16 +2222,16 @@ impl<'a> State<'a> {
                 try!(self.word_space(":"));
 
                 try!(self.commasep(Inconsistent, &a.outputs,
-                                   |s, &(ref co, ref o, is_rw)| {
-                    match co.slice_shift_char() {
-                        Some(('=', operand)) if is_rw => {
+                                   |s, out| {
+                    match out.constraint.slice_shift_char() {
+                        Some(('=', operand)) if out.is_rw => {
                             try!(s.print_string(&format!("+{}", operand),
                                                 ast::CookedStr))
                         }
-                        _ => try!(s.print_string(&co, ast::CookedStr))
+                        _ => try!(s.print_string(&out.constraint, ast::CookedStr))
                     }
                     try!(s.popen());
-                    try!(s.print_expr(&**o));
+                    try!(s.print_expr(&*out.expr));
                     try!(s.pclose());
                     Ok(())
                 }));
@@ -2400,7 +2403,7 @@ impl<'a> State<'a> {
         }
 
         match *parameters {
-            ast::AngleBracketedParameters(ref data) => {
+            ast::PathParameters::AngleBracketed(ref data) => {
                 try!(word(&mut self.s, "<"));
 
                 let mut comma = false;
@@ -2437,7 +2440,7 @@ impl<'a> State<'a> {
                 try!(word(&mut self.s, ">"))
             }
 
-            ast::ParenthesizedParameters(ref data) => {
+            ast::PathParameters::Parenthesized(ref data) => {
                 try!(word(&mut self.s, "("));
                 try!(self.commasep(
                     Inconsistent,
@@ -2468,12 +2471,12 @@ impl<'a> State<'a> {
             ast::PatWild => try!(word(&mut self.s, "_")),
             ast::PatIdent(binding_mode, ref path1, ref sub) => {
                 match binding_mode {
-                    ast::BindByRef(mutbl) => {
+                    ast::BindingMode::ByRef(mutbl) => {
                         try!(self.word_nbsp("ref"));
                         try!(self.print_mutability(mutbl));
                     }
-                    ast::BindByValue(ast::MutImmutable) => {}
-                    ast::BindByValue(ast::MutMutable) => {
+                    ast::BindingMode::ByValue(ast::MutImmutable) => {}
+                    ast::BindingMode::ByValue(ast::MutMutable) => {
                         try!(self.word_nbsp("mut"));
                     }
                 }
@@ -2679,7 +2682,7 @@ impl<'a> State<'a> {
             let m = match *explicit_self {
                 ast::SelfStatic => ast::MutImmutable,
                 _ => match decl.inputs[0].pat.node {
-                    ast::PatIdent(ast::BindByValue(m), _, _) => m,
+                    ast::PatIdent(ast::BindingMode::ByValue(m), _, _) => m,
                     _ => ast::MutImmutable
                 }
             };
@@ -3025,7 +3028,7 @@ impl<'a> State<'a> {
         }
         let generics = ast::Generics {
             lifetimes: Vec::new(),
-            ty_params: OwnedSlice::empty(),
+            ty_params: P::empty(),
             where_clause: ast::WhereClause {
                 id: ast::DUMMY_NODE_ID,
                 predicates: Vec::new(),
@@ -3159,7 +3162,7 @@ mod tests {
             output: ast::DefaultReturn(codemap::DUMMY_SP),
             variadic: false
         };
-        let generics = ast_util::empty_generics();
+        let generics = ast::Generics::default();
         assert_eq!(fun_to_string(&decl, ast::Unsafety::Normal,
                                  ast::Constness::NotConst,
                                  abba_ident,
index 83e321f110c5875dfb39694307fbde3afd1f94f1..0504c313c91d66f019f4334fcde74979482db1a7 100644 (file)
 //!   Moreover, a switch to, e.g. `P<'a, T>` would be easy and mostly automated.
 
 use std::fmt::{self, Display, Debug};
-use std::hash::{Hash, Hasher};
+use std::iter::FromIterator;
 use std::ops::Deref;
-use std::ptr;
+use std::{ptr, slice, vec};
 
 use serialize::{Encodable, Decodable, Encoder, Decoder};
 
 /// An owned smart pointer.
-pub struct P<T> {
+#[derive(Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub struct P<T: ?Sized> {
     ptr: Box<T>
 }
 
@@ -92,14 +93,6 @@ impl<T: 'static + Clone> Clone for P<T> {
     }
 }
 
-impl<T: PartialEq> PartialEq for P<T> {
-    fn eq(&self, other: &P<T>) -> bool {
-        **self == **other
-    }
-}
-
-impl<T: Eq> Eq for P<T> {}
-
 impl<T: Debug> Debug for P<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         Debug::fmt(&**self, f)
@@ -111,19 +104,12 @@ impl<T: Display> Display for P<T> {
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
 impl<T> fmt::Pointer for P<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Pointer::fmt(&self.ptr, f)
     }
 }
 
-impl<T: Hash> Hash for P<T> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        (**self).hash(state);
-    }
-}
-
 impl<T: 'static + Decodable> Decodable for P<T> {
     fn decode<D: Decoder>(d: &mut D) -> Result<P<T>, D::Error> {
         Decodable::decode(d).map(P)
@@ -135,3 +121,112 @@ impl<T: Encodable> Encodable for P<T> {
         (**self).encode(s)
     }
 }
+
+
+impl<T:fmt::Debug> fmt::Debug for P<[T]> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        self.ptr.fmt(fmt)
+    }
+}
+
+impl<T> P<[T]> {
+    pub fn new() -> P<[T]> {
+        P::empty()
+    }
+
+    pub fn empty() -> P<[T]> {
+        P { ptr: Default::default() }
+    }
+
+    #[inline(never)]
+    pub fn from_vec(v: Vec<T>) -> P<[T]> {
+        P { ptr: v.into_boxed_slice() }
+    }
+
+    #[inline(never)]
+    pub fn into_vec(self) -> Vec<T> {
+        self.ptr.into_vec()
+    }
+
+    pub fn as_slice<'a>(&'a self) -> &'a [T] {
+        &*self.ptr
+    }
+
+    pub fn move_iter(self) -> vec::IntoIter<T> {
+        self.into_vec().into_iter()
+    }
+
+    pub fn map<U, F: FnMut(&T) -> U>(&self, f: F) -> P<[U]> {
+        self.iter().map(f).collect()
+    }
+}
+
+impl<T> Deref for P<[T]> {
+    type Target = [T];
+
+    fn deref(&self) -> &[T] {
+        self.as_slice()
+    }
+}
+
+impl<T> Default for P<[T]> {
+    fn default() -> P<[T]> {
+        P::empty()
+    }
+}
+
+impl<T: Clone> Clone for P<[T]> {
+    fn clone(&self) -> P<[T]> {
+        P::from_vec(self.to_vec())
+    }
+}
+
+impl<T> From<Vec<T>> for P<[T]> {
+    fn from(v: Vec<T>) -> Self {
+        P::from_vec(v)
+    }
+}
+
+impl<T> Into<Vec<T>> for P<[T]> {
+    fn into(self) -> Vec<T> {
+        self.into_vec()
+    }
+}
+
+impl<T> FromIterator<T> for P<[T]> {
+    fn from_iter<I: IntoIterator<Item=T>>(iter: I) -> P<[T]> {
+        P::from_vec(iter.into_iter().collect())
+    }
+}
+
+impl<T> IntoIterator for P<[T]> {
+    type Item = T;
+    type IntoIter = vec::IntoIter<T>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.into_vec().into_iter()
+    }
+}
+
+impl<'a, T> IntoIterator for &'a P<[T]> {
+    type Item = &'a T;
+    type IntoIter = slice::Iter<'a, T>;
+    fn into_iter(self) -> Self::IntoIter {
+        self.ptr.into_iter()
+    }
+}
+
+impl<T: Encodable> Encodable for P<[T]> {
+    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        Encodable::encode(&**self, s)
+    }
+}
+
+impl<T: Decodable> Decodable for P<[T]> {
+    fn decode<D: Decoder>(d: &mut D) -> Result<P<[T]>, D::Error> {
+        Ok(P::from_vec(match Decodable::decode(d) {
+            Ok(t) => t,
+            Err(e) => return Err(e)
+        }))
+    }
+}
index 6492cd4b095a775260897f12b606f360c8f0abad..5e3cd0773aa45df290384225020a9b4f9902f7f7 100644 (file)
@@ -16,7 +16,7 @@
 use std::str::FromStr;
 
 use ast;
-use diagnostic;
+use errors;
 use visit;
 use visit::Visitor;
 
@@ -40,28 +40,28 @@ impl FromStr for Mode {
 }
 
 struct ShowSpanVisitor<'a> {
-    span_diagnostic: &'a diagnostic::SpanHandler,
+    span_diagnostic: &'a errors::Handler,
     mode: Mode,
 }
 
 impl<'a, 'v> Visitor<'v> for ShowSpanVisitor<'a> {
     fn visit_expr(&mut self, e: &ast::Expr) {
         if let Mode::Expression = self.mode {
-            self.span_diagnostic.span_note(e.span, "expression");
+            self.span_diagnostic.span_warn(e.span, "expression");
         }
         visit::walk_expr(self, e);
     }
 
     fn visit_pat(&mut self, p: &ast::Pat) {
         if let Mode::Pattern = self.mode {
-            self.span_diagnostic.span_note(p.span, "pattern");
+            self.span_diagnostic.span_warn(p.span, "pattern");
         }
         visit::walk_pat(self, p);
     }
 
     fn visit_ty(&mut self, t: &ast::Ty) {
         if let Mode::Type = self.mode {
-            self.span_diagnostic.span_note(t.span, "type");
+            self.span_diagnostic.span_warn(t.span, "type");
         }
         visit::walk_ty(self, t);
     }
@@ -71,7 +71,7 @@ impl<'a, 'v> Visitor<'v> for ShowSpanVisitor<'a> {
     }
 }
 
-pub fn run(span_diagnostic: &diagnostic::SpanHandler,
+pub fn run(span_diagnostic: &errors::Handler,
            mode: &str,
            krate: &ast::Crate) {
     let mode = match mode.parse().ok() {
index 6fd3833a3cd50ab739dd8e3f1e4458bbd37cebc5..9a6d1f8fdab4dd3170c57c63370b8a9733fceb91 100644 (file)
@@ -23,7 +23,7 @@ use attr::AttrMetaMethods;
 use attr;
 use codemap::{DUMMY_SP, Span, ExpnInfo, NameAndSpan, MacroAttribute};
 use codemap;
-use diagnostic;
+use errors;
 use config;
 use entry::{self, EntryPointType};
 use ext::base::ExtCtxt;
@@ -32,7 +32,6 @@ use ext::expand::ExpansionConfig;
 use fold::Folder;
 use util::move_map::MoveMap;
 use fold;
-use owned_slice::OwnedSlice;
 use parse::token::{intern, InternedString};
 use parse::{token, ParseSess};
 use print::pprust;
@@ -55,7 +54,7 @@ struct Test {
 
 struct TestCtxt<'a> {
     sess: &'a ParseSess,
-    span_diagnostic: &'a diagnostic::SpanHandler,
+    span_diagnostic: &'a errors::Handler,
     path: Vec<ast::Ident>,
     ext_cx: ExtCtxt<'a>,
     testfns: Vec<Test>,
@@ -72,7 +71,7 @@ struct TestCtxt<'a> {
 pub fn modify_for_testing(sess: &ParseSess,
                           cfg: &ast::CrateConfig,
                           krate: ast::Crate,
-                          span_diagnostic: &diagnostic::SpanHandler) -> 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.
@@ -275,7 +274,7 @@ fn generate_test_harness(sess: &ParseSess,
                          reexport_test_harness_main: Option<InternedString>,
                          krate: ast::Crate,
                          cfg: &ast::CrateConfig,
-                         sd: &diagnostic::SpanHandler) -> ast::Crate {
+                         sd: &errors::Handler) -> ast::Crate {
     // Remove the entry points
     let mut cleaner = EntryPointCleaner { depth: 0 };
     let krate = cleaner.fold_crate(krate);
@@ -315,7 +314,7 @@ fn generate_test_harness(sess: &ParseSess,
     return res;
 }
 
-fn strip_test_functions(diagnostic: &diagnostic::SpanHandler, krate: ast::Crate)
+fn strip_test_functions(diagnostic: &errors::Handler, krate: ast::Crate)
                         -> ast::Crate {
     // When not compiling with --test we should not compile the
     // #[test] functions
@@ -500,7 +499,7 @@ fn mk_main(cx: &mut TestCtxt) -> P<ast::Item> {
     let main = ast::ItemFn(ecx.fn_decl(vec![], main_ret_ty),
                            ast::Unsafety::Normal,
                            ast::Constness::NotConst,
-                           ::abi::Rust, empty_generics(), main_body);
+                           ::abi::Rust, ast::Generics::default(), main_body);
     let main = P(ast::Item {
         ident: token::str_to_ident("main"),
         attrs: vec![main_attr],
@@ -688,7 +687,7 @@ fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> P<ast::Expr> {
         Some(id) => vec![id],
         None => {
             let diag = cx.span_diagnostic;
-            diag.handler.bug("expected to find top-level re-export name, but found None");
+            diag.bug("expected to find top-level re-export name, but found None");
         }
     };
     visible_path.extend(path);
index 9bf96311122e0e3c51a891ec349afedc2012cbfe..e0796c34e57efde74e2e0e046780785547279196 100644 (file)
@@ -8,50 +8,64 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use ast::Name;
 use std::cmp;
+use parse::token::InternedString;
 
-pub fn lev_distance(me: &str, t: &str) -> usize {
-    if me.is_empty() { return t.chars().count(); }
-    if t.is_empty() { return me.chars().count(); }
+/// To find the Levenshtein distance between two strings
+pub fn lev_distance(a: &str, b: &str) -> usize {
+    // cases which don't require further computation
+    if a.is_empty() {
+        return b.chars().count();
+    } else if b.is_empty() {
+        return a.chars().count();
+    }
 
-    let mut dcol: Vec<_> = (0..t.len() + 1).collect();
+    let mut dcol: Vec<_> = (0..b.len() + 1).collect();
     let mut t_last = 0;
 
-    for (i, sc) in me.chars().enumerate() {
-
+    for (i, sc) in a.chars().enumerate() {
         let mut current = i;
         dcol[0] = current + 1;
 
-        for (j, tc) in t.chars().enumerate() {
-
+        for (j, tc) in b.chars().enumerate() {
             let next = dcol[j + 1];
-
             if sc == tc {
                 dcol[j + 1] = current;
             } else {
                 dcol[j + 1] = cmp::min(current, next);
                 dcol[j + 1] = cmp::min(dcol[j + 1], dcol[j]) + 1;
             }
-
             current = next;
             t_last = j;
         }
-    }
-
-    dcol[t_last + 1]
+    } dcol[t_last + 1]
 }
 
-pub fn max_suggestion_distance(name: &str) -> usize {
-    use std::cmp::max;
-    // As a loose rule to avoid obviously incorrect suggestions, clamp the
-    // maximum edit distance we will accept for a suggestion to one third of
-    // the typo'd name's length.
-    max(name.len(), 3) / 3
+/// To find the best match for a given string from an iterator of names
+/// As a loose rule to avoid the obviously incorrect suggestions, it takes
+/// an optional limit for the maximum allowable edit distance, which defaults
+/// to one-third of the given word
+pub fn find_best_match_for_name<'a, T>(iter_names: T,
+                                       lookup: &str,
+                                       dist: Option<usize>) -> Option<InternedString>
+    where T: Iterator<Item = &'a Name> {
+    let max_dist = dist.map_or_else(|| cmp::max(lookup.len(), 3) / 3, |d| d);
+    iter_names
+    .filter_map(|name| {
+        let dist = lev_distance(lookup, &name.as_str());
+        match dist <= max_dist {    // filter the unwanted cases
+            true => Some((name.as_str(), dist)),
+            false => None,
+        }
+    })
+    .min_by_key(|&(_, val)| val)    // extract the tuple containing the minimum edit distance
+    .map(|(s, _)| s)                // and return only the string
 }
 
 #[test]
 fn test_lev_distance() {
-    use std::char::{ from_u32, MAX };
+    use std::char::{from_u32, MAX};
     // Test bytelength agnosticity
     for c in (0..MAX as u32)
              .filter_map(|i| from_u32(i))
index 95c24c66630f048298814882ddebfccdc77693b6..e1078b719bf0679f07c4a64be63f6b817e556a42 100644 (file)
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use owned_slice::OwnedSlice;
-
 use std::ptr;
 
 pub trait MoveMap<T>: Sized {
@@ -69,11 +67,11 @@ impl<T> MoveMap<T> for Vec<T> {
     }
 }
 
-impl<T> MoveMap<T> for OwnedSlice<T> {
+impl<T> MoveMap<T> for ::ptr::P<[T]> {
     fn move_flat_map<F, I>(self, f: F) -> Self
         where F: FnMut(T) -> I,
               I: IntoIterator<Item=T>
     {
-        OwnedSlice::from_vec(self.into_vec().move_flat_map(f))
+        ::ptr::P::from_vec(self.into_vec().move_flat_map(f))
     }
 }
index bf3a8def39011bc243c8e7f239e55b72ec80b33a..87ef96d87ff5c627d72cb3578e22af16ce47679e 100644 (file)
@@ -60,7 +60,9 @@ pub enum AssocOp {
     /// `as`
     As,
     /// `..` range
-    DotDot
+    DotDot,
+    /// `:`
+    Colon,
 }
 
 #[derive(Debug, PartialEq, Eq)]
@@ -100,6 +102,7 @@ impl AssocOp {
             Token::AndAnd => Some(LAnd),
             Token::OrOr => Some(LOr),
             Token::DotDot => Some(DotDot),
+            Token::Colon => Some(Colon),
             _ if t.is_keyword(keywords::As) => Some(As),
             _ => None
         }
@@ -134,7 +137,7 @@ impl AssocOp {
     pub fn precedence(&self) -> usize {
         use self::AssocOp::*;
         match *self {
-            As => 14,
+            As | Colon => 14,
             Multiply | Divide | Modulus => 13,
             Add | Subtract => 12,
             ShiftLeft | ShiftRight => 11,
@@ -158,7 +161,7 @@ impl AssocOp {
             Inplace | Assign | AssignOp(_) => Fixity::Right,
             As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd |
             BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual |
-            LAnd | LOr => Fixity::Left,
+            LAnd | LOr | Colon => Fixity::Left,
             DotDot => Fixity::None
         }
     }
@@ -168,7 +171,7 @@ impl AssocOp {
         match *self {
             Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
             Inplace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract |
-            ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot => false
+            ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | Colon => false
         }
     }
 
@@ -178,7 +181,7 @@ impl AssocOp {
             Assign | AssignOp(_) | Inplace => true,
             Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply | Divide |
             Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd |
-            LOr | DotDot => false
+            LOr | DotDot | Colon => false
         }
     }
 
@@ -203,8 +206,7 @@ impl AssocOp {
             BitOr => Some(ast::BiBitOr),
             LAnd => Some(ast::BiAnd),
             LOr => Some(ast::BiOr),
-            Inplace | Assign | AssignOp(_) | As | DotDot => None
+            Inplace | Assign | AssignOp(_) | As | DotDot | Colon => None
         }
     }
-
 }
index a462dbeb6e4edb7412b6d8f54c3a6614735fc53d..454b925a4945e7133930276e1fe53d6c27edd57e 100644 (file)
@@ -30,10 +30,9 @@ pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a>
                                source_str)
 }
 
-fn with_error_checking_parse<T, F>(s: String, f: F) -> T where
-    F: FnOnce(&mut Parser) -> PResult<T>,
+fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T where
+    F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
 {
-    let ps = ParseSess::new();
     let mut p = string_to_parser(&ps, s);
     let x = panictry!(f(&mut p));
     p.abort_if_errors();
@@ -42,28 +41,32 @@ fn with_error_checking_parse<T, F>(s: String, f: F) -> T where
 
 /// Parse a string, return a crate.
 pub fn string_to_crate (source_str : String) -> ast::Crate {
-    with_error_checking_parse(source_str, |p| {
+    let ps = ParseSess::new();
+    with_error_checking_parse(source_str, &ps, |p| {
         p.parse_crate_mod()
     })
 }
 
 /// Parse a string, return an expr
 pub fn string_to_expr (source_str : String) -> P<ast::Expr> {
-    with_error_checking_parse(source_str, |p| {
+    let ps = ParseSess::new();
+    with_error_checking_parse(source_str, &ps, |p| {
         p.parse_expr()
     })
 }
 
 /// Parse a string, return an item
 pub fn string_to_item (source_str : String) -> Option<P<ast::Item>> {
-    with_error_checking_parse(source_str, |p| {
+    let ps = ParseSess::new();
+    with_error_checking_parse(source_str, &ps, |p| {
         p.parse_item()
     })
 }
 
 /// Parse a string, return a stmt
 pub fn string_to_stmt(source_str : String) -> Option<P<ast::Stmt>> {
-    with_error_checking_parse(source_str, |p| {
+    let ps = ParseSess::new();
+    with_error_checking_parse(source_str, &ps, |p| {
         p.parse_stmt()
     })
 }
@@ -71,7 +74,8 @@ pub fn string_to_stmt(source_str : String) -> Option<P<ast::Stmt>> {
 /// Parse a string, return a pat. Uses "irrefutable"... which doesn't
 /// (currently) affect parsing.
 pub fn string_to_pat(source_str: String) -> P<ast::Pat> {
-    with_error_checking_parse(source_str, |p| {
+    let ps = ParseSess::new();
+    with_error_checking_parse(source_str, &ps, |p| {
         p.parse_pat()
     })
 }
index ee183d7f3e96a3455f024fa33c0fdd65b5062ded..8b07b21c578c9e05c46e6d6cbbb1db0f48ac3fd5 100644 (file)
@@ -127,13 +127,6 @@ impl<T> SmallVector<T> {
         }
     }
 
-    /// Deprecated: use `into_iter`.
-    #[unstable(feature = "rustc_private", issue = "0")]
-    #[rustc_deprecated(since = "1.0.0", reason = "use into_iter")]
-    pub fn move_iter(self) -> IntoIter<T> {
-        self.into_iter()
-    }
-
     pub fn len(&self) -> usize {
         match self.repr {
             Zero => 0,
index cdc11fb2c1cb0262cef3e0ce563290ee0d2b737c..9b102cd99f305faac1e88a662cd0284c27bcd63c 100644 (file)
@@ -399,12 +399,12 @@ pub fn walk_path_parameters<'v, V: Visitor<'v>>(visitor: &mut V,
                                                 _path_span: Span,
                                                 path_parameters: &'v PathParameters) {
     match *path_parameters {
-        AngleBracketedParameters(ref data) => {
+        PathParameters::AngleBracketed(ref data) => {
             walk_list!(visitor, visit_ty, &data.types);
             walk_list!(visitor, visit_lifetime, &data.lifetimes);
             walk_list!(visitor, visit_assoc_type_binding, &data.bindings);
         }
-        ParenthesizedParameters(ref data) => {
+        PathParameters::Parenthesized(ref data) => {
             walk_list!(visitor, visit_ty, &data.inputs);
             walk_list!(visitor, visit_ty, &data.output);
         }
@@ -693,7 +693,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
             visitor.visit_expr(subexpression)
         }
         ExprLit(_) => {}
-        ExprCast(ref subexpression, ref typ) => {
+        ExprCast(ref subexpression, ref typ) | ExprType(ref subexpression, ref typ) => {
             visitor.visit_expr(subexpression);
             visitor.visit_ty(typ)
         }
@@ -786,8 +786,8 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
             for &(_, ref input) in &ia.inputs {
                 visitor.visit_expr(&input)
             }
-            for &(_, ref output, _) in &ia.outputs {
-                visitor.visit_expr(&output)
+            for output in &ia.outputs {
+                visitor.visit_expr(&output.expr)
             }
         }
     }
diff --git a/src/libsyntax_ext/asm.rs b/src/libsyntax_ext/asm.rs
new file mode 100644 (file)
index 0000000..2f50f61
--- /dev/null
@@ -0,0 +1,264 @@
+// 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.
+
+/*
+ * Inline assembly support.
+ */
+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;
+use syntax::parse::token::intern;
+use syntax::parse::{self, token};
+use syntax::ptr::P;
+use syntax::ast::AsmDialect;
+
+enum State {
+    Asm,
+    Outputs,
+    Inputs,
+    Clobbers,
+    Options,
+    StateNone
+}
+
+impl State {
+    fn next(&self) -> State {
+        match *self {
+            Asm       => Outputs,
+            Outputs   => Inputs,
+            Inputs    => Clobbers,
+            Clobbers  => Options,
+            Options   => StateNone,
+            StateNone => StateNone
+        }
+    }
+}
+
+const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
+
+pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+                       -> Box<base::MacResult+'cx> {
+    if !cx.ecfg.enable_asm() {
+        feature_gate::emit_feature_err(
+            &cx.parse_sess.span_diagnostic, "asm", sp,
+            feature_gate::GateIssue::Language,
+            feature_gate::EXPLAIN_ASM);
+        return DummyResult::expr(sp);
+    }
+
+    // Split the tts before the first colon, to avoid `asm!("x": y)`  being
+    // 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,
+            _ => false
+        }
+    }).unwrap_or(tts.len());
+    let mut p = cx.new_parser_from_tts(&tts[first_colon..]);
+    let mut asm = token::InternedString::new("");
+    let mut asm_str_style = None;
+    let mut outputs = Vec::new();
+    let mut inputs = Vec::new();
+    let mut clobs = Vec::new();
+    let mut volatile = false;
+    let mut alignstack = false;
+    let mut dialect = AsmDialect::Att;
+
+    let mut state = Asm;
+
+    'statement: loop {
+        match state {
+            Asm => {
+                if asm_str_style.is_some() {
+                    // If we already have a string with instructions,
+                    // ending up in Asm state again is an error.
+                    cx.span_err(sp, "malformed inline assembly");
+                    return DummyResult::expr(sp);
+                }
+                // Nested parser, stop before the first colon (see above).
+                let mut p2 = cx.new_parser_from_tts(&tts[..first_colon]);
+                let (s, style) = match expr_to_string(cx, panictry!(p2.parse_expr()),
+                                                   "inline assembly must be a string literal") {
+                    Some((s, st)) => (s, st),
+                    // let compilation continue
+                    None => return DummyResult::expr(sp),
+                };
+
+                // This is most likely malformed.
+                if p2.token != token::Eof {
+                    let mut extra_tts = panictry!(p2.parse_all_token_trees());
+                    extra_tts.extend(tts[first_colon..].iter().cloned());
+                    p = parse::tts_to_parser(cx.parse_sess, extra_tts, cx.cfg());
+                }
+
+                asm = s;
+                asm_str_style = Some(style);
+            }
+            Outputs => {
+                while p.token != token::Eof &&
+                      p.token != token::Colon &&
+                      p.token != token::ModSep {
+
+                    if !outputs.is_empty() {
+                        p.eat(&token::Comma);
+                    }
+
+                    let (constraint, _str_style) = panictry!(p.parse_str());
+
+                    let span = p.last_span;
+
+                    panictry!(p.expect(&token::OpenDelim(token::Paren)));
+                    let out = panictry!(p.parse_expr());
+                    panictry!(p.expect(&token::CloseDelim(token::Paren)));
+
+                    // Expands a read+write operand into two operands.
+                    //
+                    // Use '+' modifier when you want the same expression
+                    // to be both an input and an output at the same time.
+                    // It's the opposite of '=&' which means that the memory
+                    // cannot be shared with any other operand (usually when
+                    // a register is clobbered early.)
+                    let output = match constraint.slice_shift_char() {
+                        Some(('=', _)) => None,
+                        Some(('+', operand)) => {
+                            Some(token::intern_and_get_ident(&format!(
+                                        "={}", operand)))
+                        }
+                        _ => {
+                            cx.span_err(span, "output operand constraint lacks '=' or '+'");
+                            None
+                        }
+                    };
+
+                    let is_rw = output.is_some();
+                    let is_indirect = constraint.contains("*");
+                    outputs.push(ast::InlineAsmOutput {
+                        constraint: output.unwrap_or(constraint),
+                        expr: out,
+                        is_rw: is_rw,
+                        is_indirect: is_indirect,
+                    });
+                }
+            }
+            Inputs => {
+                while p.token != token::Eof &&
+                      p.token != token::Colon &&
+                      p.token != token::ModSep {
+
+                    if !inputs.is_empty() {
+                        p.eat(&token::Comma);
+                    }
+
+                    let (constraint, _str_style) = panictry!(p.parse_str());
+
+                    if constraint.starts_with("=") {
+                        cx.span_err(p.last_span, "input operand constraint contains '='");
+                    } else if constraint.starts_with("+") {
+                        cx.span_err(p.last_span, "input operand constraint contains '+'");
+                    }
+
+                    panictry!(p.expect(&token::OpenDelim(token::Paren)));
+                    let input = panictry!(p.parse_expr());
+                    panictry!(p.expect(&token::CloseDelim(token::Paren)));
+
+                    inputs.push((constraint, input));
+                }
+            }
+            Clobbers => {
+                while p.token != token::Eof &&
+                      p.token != token::Colon &&
+                      p.token != token::ModSep {
+
+                    if !clobs.is_empty() {
+                        p.eat(&token::Comma);
+                    }
+
+                    let (s, _str_style) = panictry!(p.parse_str());
+
+                    if OPTIONS.iter().any(|&opt| s == opt) {
+                        cx.span_warn(p.last_span, "expected a clobber, found an option");
+                    }
+                    clobs.push(s);
+                }
+            }
+            Options => {
+                let (option, _str_style) = panictry!(p.parse_str());
+
+                if option == "volatile" {
+                    // Indicates that the inline assembly has side effects
+                    // and must not be optimized out along with its outputs.
+                    volatile = true;
+                } else if option == "alignstack" {
+                    alignstack = true;
+                } else if option == "intel" {
+                    dialect = AsmDialect::Intel;
+                } else {
+                    cx.span_warn(p.last_span, "unrecognized option");
+                }
+
+                if p.token == token::Comma {
+                    p.eat(&token::Comma);
+                }
+            }
+            StateNone => ()
+        }
+
+        loop {
+            // MOD_SEP is a double colon '::' without space in between.
+            // When encountered, the state must be advanced twice.
+            match (&p.token, state.next(), state.next().next()) {
+                (&token::Colon, StateNone, _)   |
+                (&token::ModSep, _, StateNone) => {
+                    p.bump();
+                    break 'statement;
+                }
+                (&token::Colon, st, _)   |
+                (&token::ModSep, _, st) => {
+                    p.bump();
+                    state = st;
+                }
+                (&token::Eof, _, _) => break 'statement,
+                _ => break
+            }
+        }
+    }
+
+    let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
+        call_site: sp,
+        callee: codemap::NameAndSpan {
+            format: codemap::MacroBang(intern("asm")),
+            span: None,
+            allow_internal_unstable: false,
+        },
+    });
+
+    MacEager::expr(P(ast::Expr {
+        id: ast::DUMMY_NODE_ID,
+        node: ast::ExprInlineAsm(ast::InlineAsm {
+            asm: token::intern_and_get_ident(&asm),
+            asm_str_style: asm_str_style.unwrap(),
+            outputs: outputs,
+            inputs: inputs,
+            clobbers: clobs,
+            volatile: volatile,
+            alignstack: alignstack,
+            dialect: dialect,
+            expn_id: expn_id,
+        }),
+        span: sp,
+        attrs: None,
+    }))
+}
diff --git a/src/libsyntax_ext/cfg.rs b/src/libsyntax_ext/cfg.rs
new file mode 100644 (file)
index 0000000..bae0462
--- /dev/null
@@ -0,0 +1,44 @@
+// 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.
+
+/// The compiler code necessary to support the cfg! extension, which expands to
+/// 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::parse::token;
+use syntax::config::CfgDiagReal;
+
+pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
+                       sp: Span,
+                       tts: &[ast::TokenTree])
+                       -> Box<base::MacResult+'static> {
+    let mut p = cx.new_parser_from_tts(tts);
+    let cfg = panictry!(p.parse_meta_item());
+
+    if !p.eat(&token::Eof) {
+        cx.span_err(sp, "expected 1 cfg-pattern");
+        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)
+    };
+    MacEager::expr(cx.expr_bool(sp, matches_cfg))
+}
diff --git a/src/libsyntax_ext/concat.rs b/src/libsyntax_ext/concat.rs
new file mode 100644 (file)
index 0000000..de913fe
--- /dev/null
@@ -0,0 +1,66 @@
+// 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.
+
+use syntax::ast;
+use syntax::codemap;
+use syntax::ext::base;
+use syntax::ext::build::AstBuilder;
+use syntax::parse::token;
+
+use std::string::String;
+
+pub fn expand_syntax_ext(cx: &mut base::ExtCtxt,
+                         sp: codemap::Span,
+                         tts: &[ast::TokenTree])
+                         -> Box<base::MacResult+'static> {
+    let es = match base::get_exprs_from_tts(cx, sp, tts) {
+        Some(e) => e,
+        None => return base::DummyResult::expr(sp)
+    };
+    let mut accumulator = String::new();
+    for e in es {
+        match e.node {
+            ast::ExprLit(ref lit) => {
+                match lit.node {
+                    ast::LitStr(ref s, _) |
+                    ast::LitFloat(ref s, _) |
+                    ast::LitFloatUnsuffixed(ref s) => {
+                        accumulator.push_str(&s);
+                    }
+                    ast::LitChar(c) => {
+                        accumulator.push(c);
+                    }
+                    ast::LitInt(i, ast::UnsignedIntLit(_)) |
+                    ast::LitInt(i, ast::SignedIntLit(_, ast::Plus)) |
+                    ast::LitInt(i, ast::UnsuffixedIntLit(ast::Plus)) => {
+                        accumulator.push_str(&format!("{}", i));
+                    }
+                    ast::LitInt(i, ast::SignedIntLit(_, ast::Minus)) |
+                    ast::LitInt(i, ast::UnsuffixedIntLit(ast::Minus)) => {
+                        accumulator.push_str(&format!("-{}", i));
+                    }
+                    ast::LitBool(b) => {
+                        accumulator.push_str(&format!("{}", b));
+                    }
+                    ast::LitByte(..) |
+                    ast::LitByteStr(..) => {
+                        cx.span_err(e.span, "cannot concatenate a byte string literal");
+                    }
+                }
+            }
+            _ => {
+                cx.span_err(e.span, "expected a literal");
+            }
+        }
+    }
+    base::MacEager::expr(cx.expr_str(
+            sp,
+            token::intern_and_get_ident(&accumulator[..])))
+}
diff --git a/src/libsyntax_ext/concat_idents.rs b/src/libsyntax_ext/concat_idents.rs
new file mode 100644 (file)
index 0000000..9702b24
--- /dev/null
@@ -0,0 +1,73 @@
+// 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.
+
+use syntax::ast::{self, TokenTree};
+use syntax::codemap::Span;
+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;
+
+pub fn expand_syntax_ext<'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,
+                                       "concat_idents",
+                                       sp,
+                                       feature_gate::GateIssue::Language,
+                                       feature_gate::EXPLAIN_CONCAT_IDENTS);
+        return base::DummyResult::expr(sp);
+    }
+
+    let mut res_str = String::new();
+    for (i, e) in tts.iter().enumerate() {
+        if i & 1 == 1 {
+            match *e {
+                TokenTree::Token(_, token::Comma) => {},
+                _ => {
+                    cx.span_err(sp, "concat_idents! expecting comma.");
+                    return DummyResult::expr(sp);
+                },
+            }
+        } else {
+            match *e {
+                TokenTree::Token(_, token::Ident(ident, _)) => {
+                    res_str.push_str(&ident.name.as_str())
+                },
+                _ => {
+                    cx.span_err(sp, "concat_idents! requires ident args.");
+                    return DummyResult::expr(sp);
+                },
+            }
+        }
+    }
+    let res = str_to_ident(&res_str);
+
+    let e = P(ast::Expr {
+        id: ast::DUMMY_NODE_ID,
+        node: ast::ExprPath(None,
+            ast::Path {
+                 span: sp,
+                 global: false,
+                 segments: vec!(
+                    ast::PathSegment {
+                        identifier: res,
+                        parameters: ast::PathParameters::none(),
+                    }
+                )
+            }
+        ),
+        span: sp,
+        attrs: None,
+    });
+    MacEager::expr(e)
+}
diff --git a/src/libsyntax_ext/deriving/bounds.rs b/src/libsyntax_ext/deriving/bounds.rs
new file mode 100644 (file)
index 0000000..9bc0e08
--- /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 deriving::generic::*;
+use deriving::generic::ty::*;
+
+use syntax::ast::MetaItem;
+use syntax::codemap::Span;
+use syntax::ext::base::{ExtCtxt, Annotatable};
+
+pub fn expand_deriving_unsafe_bound(cx: &mut ExtCtxt,
+                                    span: Span,
+                                    _: &MetaItem,
+                                    _: &Annotatable,
+                                    _: &mut FnMut(Annotatable))
+{
+    cx.span_err(span, "this unsafe trait should be implemented explicitly");
+}
+
+pub fn expand_deriving_copy(cx: &mut ExtCtxt,
+                            span: Span,
+                            mitem: &MetaItem,
+                            item: &Annotatable,
+                            push: &mut FnMut(Annotatable))
+{
+    let mut v = cx.crate_root.map(|s| vec![s]).unwrap_or(Vec::new());
+    v.push("marker");
+    v.push("Copy");
+    let path = Path::new(v);
+
+    let trait_def = TraitDef {
+        span: span,
+        attributes: Vec::new(),
+        path: path,
+        additional_bounds: Vec::new(),
+        generics: LifetimeBounds::empty(),
+        is_unsafe: false,
+        methods: Vec::new(),
+        associated_types: Vec::new(),
+    };
+
+    trait_def.expand(cx, mitem, item, push);
+}
diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs
new file mode 100644 (file)
index 0000000..1825c3d
--- /dev/null
@@ -0,0 +1,115 @@
+// 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.
+
+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;
+
+pub fn expand_deriving_clone(cx: &mut ExtCtxt,
+                             span: Span,
+                             mitem: &MetaItem,
+                             item: &Annotatable,
+                             push: &mut FnMut(Annotatable))
+{
+    let inline = cx.meta_word(span, InternedString::new("inline"));
+    let attrs = vec!(cx.attribute(span, inline));
+    let trait_def = TraitDef {
+        span: span,
+        attributes: Vec::new(),
+        path: path_std!(cx, core::clone::Clone),
+        additional_bounds: Vec::new(),
+        generics: LifetimeBounds::empty(),
+        is_unsafe: false,
+        methods: vec!(
+            MethodDef {
+                name: "clone",
+                generics: LifetimeBounds::empty(),
+                explicit_self: borrowed_explicit_self(),
+                args: Vec::new(),
+                ret_ty: Self_,
+                attributes: attrs,
+                is_unsafe: false,
+                combine_substructure: combine_substructure(Box::new(|c, s, sub| {
+                    cs_clone("Clone", c, s, sub)
+                })),
+            }
+        ),
+        associated_types: Vec::new(),
+    };
+
+    trait_def.expand(cx, mitem, item, push)
+}
+
+fn cs_clone(
+    name: &str,
+    cx: &mut ExtCtxt, trait_span: Span,
+    substr: &Substructure) -> P<Expr> {
+    let ctor_path;
+    let all_fields;
+    let fn_path = cx.std_path(&["clone", "Clone", "clone"]);
+    let subcall = |field: &FieldInfo| {
+        let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
+
+        cx.expr_call_global(field.span, fn_path.clone(), args)
+    };
+
+    match *substr.fields {
+        Struct(ref af) => {
+            ctor_path = cx.path(trait_span, vec![substr.type_ident]);
+            all_fields = af;
+        }
+        EnumMatching(_, variant, ref af) => {
+            ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.name]);
+            all_fields = af;
+        },
+        EnumNonMatchingCollapsed (..) => {
+            cx.span_bug(trait_span,
+                        &format!("non-matching enum variants in \
+                                 `derive({})`", name))
+        }
+        StaticEnum(..) | StaticStruct(..) => {
+            cx.span_bug(trait_span,
+                        &format!("static method in `derive({})`", name))
+        }
+    }
+
+    if !all_fields.is_empty() && all_fields[0].name.is_none() {
+        // enum-like
+        let subcalls = all_fields.iter().map(subcall).collect();
+        let path = cx.expr_path(ctor_path);
+        cx.expr_call(trait_span, path, subcalls)
+    } else {
+        // struct-like
+        let fields = all_fields.iter().map(|field| {
+            let ident = match field.name {
+                Some(i) => i,
+                None => {
+                    cx.span_bug(trait_span,
+                                &format!("unnamed field in normal struct in \
+                                         `derive({})`", name))
+                }
+            };
+            cx.field_imm(field.span, ident, subcall(field))
+        }).collect::<Vec<_>>();
+
+        if fields.is_empty() {
+            // no fields, so construct like `None`
+            cx.expr_path(ctor_path)
+        } else {
+            cx.expr_struct(trait_span, ctor_path, fields)
+        }
+    }
+}
diff --git a/src/libsyntax_ext/deriving/cmp/eq.rs b/src/libsyntax_ext/deriving/cmp/eq.rs
new file mode 100644 (file)
index 0000000..1b855c5
--- /dev/null
@@ -0,0 +1,73 @@
+// 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.
+
+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;
+
+pub fn expand_deriving_eq(cx: &mut ExtCtxt,
+                          span: Span,
+                          mitem: &MetaItem,
+                          item: &Annotatable,
+                          push: &mut FnMut(Annotatable))
+{
+    fn cs_total_eq_assert(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
+        cs_same_method(
+            |cx, span, exprs| {
+                // 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);
+                cx.expr_block(block)
+            },
+            Box::new(|cx, sp, _, _| {
+                cx.span_bug(sp, "non matching enums in derive(Eq)?") }),
+            cx,
+            span,
+            substr
+        )
+    }
+
+    let inline = cx.meta_word(span, InternedString::new("inline"));
+    let hidden = cx.meta_word(span, InternedString::new("hidden"));
+    let doc = cx.meta_list(span, InternedString::new("doc"), vec!(hidden));
+    let attrs = vec!(cx.attribute(span, inline),
+                     cx.attribute(span, doc));
+    let trait_def = TraitDef {
+        span: span,
+        attributes: Vec::new(),
+        path: path_std!(cx, core::cmp::Eq),
+        additional_bounds: Vec::new(),
+        generics: LifetimeBounds::empty(),
+        is_unsafe: false,
+        methods: vec!(
+            MethodDef {
+                name: "assert_receiver_is_total_eq",
+                generics: LifetimeBounds::empty(),
+                explicit_self: borrowed_explicit_self(),
+                args: vec!(),
+                ret_ty: nil_ty(),
+                attributes: attrs,
+                is_unsafe: false,
+                combine_substructure: combine_substructure(Box::new(|a, b, c| {
+                    cs_total_eq_assert(a, b, c)
+                }))
+            }
+        ),
+        associated_types: Vec::new(),
+    };
+    trait_def.expand(cx, mitem, item, push)
+}
diff --git a/src/libsyntax_ext/deriving/cmp/ord.rs b/src/libsyntax_ext/deriving/cmp/ord.rs
new file mode 100644 (file)
index 0000000..95a5d18
--- /dev/null
@@ -0,0 +1,136 @@
+// 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.
+
+use deriving::generic::*;
+use deriving::generic::ty::*;
+
+use syntax::ast;
+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;
+
+pub fn expand_deriving_ord(cx: &mut ExtCtxt,
+                           span: Span,
+                           mitem: &MetaItem,
+                           item: &Annotatable,
+                           push: &mut FnMut(Annotatable))
+{
+    let inline = cx.meta_word(span, InternedString::new("inline"));
+    let attrs = vec!(cx.attribute(span, inline));
+    let trait_def = TraitDef {
+        span: span,
+        attributes: Vec::new(),
+        path: path_std!(cx, core::cmp::Ord),
+        additional_bounds: Vec::new(),
+        generics: LifetimeBounds::empty(),
+        is_unsafe: false,
+        methods: vec!(
+            MethodDef {
+                name: "cmp",
+                generics: LifetimeBounds::empty(),
+                explicit_self: borrowed_explicit_self(),
+                args: vec!(borrowed_self()),
+                ret_ty: Literal(path_std!(cx, core::cmp::Ordering)),
+                attributes: attrs,
+                is_unsafe: false,
+                combine_substructure: combine_substructure(Box::new(|a, b, c| {
+                    cs_cmp(a, b, c)
+                })),
+            }
+        ),
+        associated_types: Vec::new(),
+    };
+
+    trait_def.expand(cx, mitem, item, push)
+}
+
+
+pub fn ordering_collapsed(cx: &mut ExtCtxt,
+                          span: Span,
+                          self_arg_tags: &[ast::Ident]) -> P<ast::Expr> {
+    let lft = cx.expr_ident(span, self_arg_tags[0]);
+    let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
+    cx.expr_method_call(span, lft, cx.ident_of("cmp"), vec![rgt])
+}
+
+pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
+              substr: &Substructure) -> P<Expr> {
+    let test_id = cx.ident_of("__test");
+    let equals_path = cx.path_global(span,
+                                     cx.std_path(&["cmp", "Ordering", "Equal"]));
+
+    let cmp_path = cx.std_path(&["cmp", "Ord", "cmp"]);
+
+    /*
+    Builds:
+
+    let __test = ::std::cmp::Ord::cmp(&self_field1, &other_field1);
+    if other == ::std::cmp::Ordering::Equal {
+        let __test = ::std::cmp::Ord::cmp(&self_field2, &other_field2);
+        if __test == ::std::cmp::Ordering::Equal {
+            ...
+        } else {
+            __test
+        }
+    } else {
+        __test
+    }
+
+    FIXME #6449: These `if`s could/should be `match`es.
+    */
+    cs_fold(
+        // foldr nests the if-elses correctly, leaving the first field
+        // as the outermost one, and the last as the innermost.
+        false,
+        |cx, span, old, self_f, other_fs| {
+            // let __test = new;
+            // if __test == ::std::cmp::Ordering::Equal {
+            //    old
+            // } else {
+            //    __test
+            // }
+
+            let new = {
+                let other_f = match (other_fs.len(), other_fs.get(0)) {
+                    (1, Some(o_f)) => o_f,
+                    _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
+                };
+
+                let args = vec![
+                    cx.expr_addr_of(span, self_f),
+                    cx.expr_addr_of(span, other_f.clone()),
+                ];
+
+                cx.expr_call_global(span, cmp_path.clone(), args)
+            };
+
+            let assign = cx.stmt_let(span, false, test_id, new);
+
+            let cond = cx.expr_binary(span, ast::BiEq,
+                                      cx.expr_ident(span, test_id),
+                                      cx.expr_path(equals_path.clone()));
+            let if_ = cx.expr_if(span,
+                                 cond,
+                                 old, Some(cx.expr_ident(span, test_id)));
+            cx.expr_block(cx.block(span, vec!(assign), Some(if_)))
+        },
+        cx.expr_path(equals_path.clone()),
+        Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
+            if self_args.len() != 2 {
+                cx.span_bug(span, "not exactly 2 arguments in `derives(Ord)`")
+            } else {
+                ordering_collapsed(cx, span, tag_tuple)
+            }
+        }),
+        cx, span, substr)
+}
diff --git a/src/libsyntax_ext/deriving/cmp/partial_eq.rs b/src/libsyntax_ext/deriving/cmp/partial_eq.rs
new file mode 100644 (file)
index 0000000..29be5a7
--- /dev/null
@@ -0,0 +1,97 @@
+// 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.
+
+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;
+
+pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt,
+                                  span: Span,
+                                  mitem: &MetaItem,
+                                  item: &Annotatable,
+                                  push: &mut FnMut(Annotatable))
+{
+    // structures are equal if all fields are equal, and non equal, if
+    // any fields are not equal or if the enum variants are different
+    fn cs_eq(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
+        cs_fold(
+            true,  // use foldl
+            |cx, span, subexpr, self_f, other_fs| {
+                let other_f = match (other_fs.len(), other_fs.get(0)) {
+                    (1, Some(o_f)) => o_f,
+                    _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`")
+                };
+
+                let eq = cx.expr_binary(span, ast::BiEq, self_f, other_f.clone());
+
+                cx.expr_binary(span, ast::BiAnd, subexpr, eq)
+            },
+            cx.expr_bool(span, true),
+            Box::new(|cx, span, _, _| cx.expr_bool(span, false)),
+            cx, span, substr)
+    }
+    fn cs_ne(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
+        cs_fold(
+            true,  // use foldl
+            |cx, span, subexpr, self_f, other_fs| {
+                let other_f = match (other_fs.len(), other_fs.get(0)) {
+                    (1, Some(o_f)) => o_f,
+                    _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`")
+                };
+
+                let eq = cx.expr_binary(span, ast::BiNe, self_f, other_f.clone());
+
+                cx.expr_binary(span, ast::BiOr, subexpr, eq)
+            },
+            cx.expr_bool(span, false),
+            Box::new(|cx, span, _, _| cx.expr_bool(span, true)),
+            cx, span, substr)
+    }
+
+    macro_rules! md {
+        ($name:expr, $f:ident) => { {
+            let inline = cx.meta_word(span, InternedString::new("inline"));
+            let attrs = vec!(cx.attribute(span, inline));
+            MethodDef {
+                name: $name,
+                generics: LifetimeBounds::empty(),
+                explicit_self: borrowed_explicit_self(),
+                args: vec!(borrowed_self()),
+                ret_ty: Literal(path_local!(bool)),
+                attributes: attrs,
+                is_unsafe: false,
+                combine_substructure: combine_substructure(Box::new(|a, b, c| {
+                    $f(a, b, c)
+                }))
+            }
+        } }
+    }
+
+    let trait_def = TraitDef {
+        span: span,
+        attributes: Vec::new(),
+        path: path_std!(cx, core::cmp::PartialEq),
+        additional_bounds: Vec::new(),
+        generics: LifetimeBounds::empty(),
+        is_unsafe: false,
+        methods: vec!(
+            md!("eq", cs_eq),
+            md!("ne", cs_ne)
+        ),
+        associated_types: Vec::new(),
+    };
+    trait_def.expand(cx, mitem, item, push)
+}
diff --git a/src/libsyntax_ext/deriving/cmp/partial_ord.rs b/src/libsyntax_ext/deriving/cmp/partial_ord.rs
new file mode 100644 (file)
index 0000000..bd825e5
--- /dev/null
@@ -0,0 +1,233 @@
+// 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.
+
+pub use self::OrderingOp::*;
+
+use deriving::generic::*;
+use deriving::generic::ty::*;
+
+use syntax::ast;
+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;
+
+pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
+                                   span: Span,
+                                   mitem: &MetaItem,
+                                   item: &Annotatable,
+                                   push: &mut FnMut(Annotatable))
+{
+    macro_rules! md {
+        ($name:expr, $op:expr, $equal:expr) => { {
+            let inline = cx.meta_word(span, InternedString::new("inline"));
+            let attrs = vec!(cx.attribute(span, inline));
+            MethodDef {
+                name: $name,
+                generics: LifetimeBounds::empty(),
+                explicit_self: borrowed_explicit_self(),
+                args: vec!(borrowed_self()),
+                ret_ty: Literal(path_local!(bool)),
+                attributes: attrs,
+                is_unsafe: false,
+                combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
+                    cs_op($op, $equal, cx, span, substr)
+                }))
+            }
+        } }
+    }
+
+    let ordering_ty = Literal(path_std!(cx, core::cmp::Ordering));
+    let ret_ty = Literal(Path::new_(pathvec_std!(cx, core::option::Option),
+                                    None,
+                                    vec![Box::new(ordering_ty)],
+                                    true));
+
+    let inline = cx.meta_word(span, InternedString::new("inline"));
+    let attrs = vec!(cx.attribute(span, inline));
+
+    let partial_cmp_def = MethodDef {
+        name: "partial_cmp",
+        generics: LifetimeBounds::empty(),
+        explicit_self: borrowed_explicit_self(),
+        args: vec![borrowed_self()],
+        ret_ty: ret_ty,
+        attributes: attrs,
+        is_unsafe: false,
+        combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
+            cs_partial_cmp(cx, span, substr)
+        }))
+    };
+
+    let trait_def = TraitDef {
+        span: span,
+        attributes: vec![],
+        path: path_std!(cx, core::cmp::PartialOrd),
+        additional_bounds: vec![],
+        generics: LifetimeBounds::empty(),
+        is_unsafe: false,
+        methods: vec![
+            partial_cmp_def,
+            md!("lt", true, false),
+            md!("le", true, true),
+            md!("gt", false, false),
+            md!("ge", false, true)
+        ],
+        associated_types: Vec::new(),
+    };
+    trait_def.expand(cx, mitem, item, push)
+}
+
+#[derive(Copy, Clone)]
+pub enum OrderingOp {
+    PartialCmpOp, LtOp, LeOp, GtOp, GeOp,
+}
+
+pub fn some_ordering_collapsed(cx: &mut ExtCtxt,
+                               span: Span,
+                               op: OrderingOp,
+                               self_arg_tags: &[ast::Ident]) -> P<ast::Expr> {
+    let lft = cx.expr_ident(span, self_arg_tags[0]);
+    let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
+    let op_str = match op {
+        PartialCmpOp => "partial_cmp",
+        LtOp => "lt", LeOp => "le",
+        GtOp => "gt", GeOp => "ge",
+    };
+    cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt])
+}
+
+pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span,
+              substr: &Substructure) -> P<Expr> {
+    let test_id = cx.ident_of("__test");
+    let ordering = cx.path_global(span,
+                                  cx.std_path(&["cmp", "Ordering", "Equal"]));
+    let ordering = cx.expr_path(ordering);
+    let equals_expr = cx.expr_some(span, ordering);
+
+    let partial_cmp_path = cx.std_path(&["cmp", "PartialOrd", "partial_cmp"]);
+
+    /*
+    Builds:
+
+    let __test = ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1);
+    if __test == ::std::option::Option::Some(::std::cmp::Ordering::Equal) {
+        let __test = ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2);
+        if __test == ::std::option::Option::Some(::std::cmp::Ordering::Equal) {
+            ...
+        } else {
+            __test
+        }
+    } else {
+        __test
+    }
+
+    FIXME #6449: These `if`s could/should be `match`es.
+    */
+    cs_fold(
+        // foldr nests the if-elses correctly, leaving the first field
+        // as the outermost one, and the last as the innermost.
+        false,
+        |cx, span, old, self_f, other_fs| {
+            // let __test = new;
+            // if __test == Some(::std::cmp::Ordering::Equal) {
+            //    old
+            // } else {
+            //    __test
+            // }
+
+            let new = {
+                let other_f = match (other_fs.len(), other_fs.get(0)) {
+                    (1, Some(o_f)) => o_f,
+                    _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
+                };
+
+                let args = vec![
+                    cx.expr_addr_of(span, self_f),
+                    cx.expr_addr_of(span, other_f.clone()),
+                ];
+
+                cx.expr_call_global(span, partial_cmp_path.clone(), args)
+            };
+
+            let assign = cx.stmt_let(span, false, test_id, new);
+
+            let cond = cx.expr_binary(span, ast::BiEq,
+                                      cx.expr_ident(span, test_id),
+                                      equals_expr.clone());
+            let if_ = cx.expr_if(span,
+                                 cond,
+                                 old, Some(cx.expr_ident(span, test_id)));
+            cx.expr_block(cx.block(span, vec!(assign), Some(if_)))
+        },
+        equals_expr.clone(),
+        Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
+            if self_args.len() != 2 {
+                cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
+            } else {
+                some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple)
+            }
+        }),
+        cx, span, substr)
+}
+
+/// Strict inequality.
+fn cs_op(less: bool, equal: bool, cx: &mut ExtCtxt,
+         span: Span, substr: &Substructure) -> P<Expr> {
+    let op = if less {ast::BiLt} else {ast::BiGt};
+    cs_fold(
+        false, // need foldr,
+        |cx, span, subexpr, self_f, other_fs| {
+            /*
+            build up a series of chain ||'s and &&'s from the inside
+            out (hence foldr) to get lexical ordering, i.e. for op ==
+            `ast::lt`
+
+            ```
+            self.f1 < other.f1 || (!(other.f1 < self.f1) &&
+                (self.f2 < other.f2 || (!(other.f2 < self.f2) &&
+                    (false)
+                ))
+            )
+            ```
+
+            The optimiser should remove the redundancy. We explicitly
+            get use the binops to avoid auto-deref dereferencing too many
+            layers of pointers, if the type includes pointers.
+            */
+            let other_f = match (other_fs.len(), other_fs.get(0)) {
+                (1, Some(o_f)) => o_f,
+                _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
+            };
+
+            let cmp = cx.expr_binary(span, op, self_f.clone(), other_f.clone());
+
+            let not_cmp = cx.expr_unary(span, ast::UnNot,
+                                        cx.expr_binary(span, op, other_f.clone(), self_f));
+
+            let and = cx.expr_binary(span, ast::BiAnd, not_cmp, subexpr);
+            cx.expr_binary(span, ast::BiOr, cmp, and)
+        },
+        cx.expr_bool(span, equal),
+        Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
+            if self_args.len() != 2 {
+                cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
+            } else {
+                let op = match (less, equal) {
+                    (true,  true) => LeOp, (true,  false) => LtOp,
+                    (false, true) => GeOp, (false, false) => GtOp,
+                };
+                some_ordering_collapsed(cx, span, op, tag_tuple)
+            }
+        }),
+        cx, span, substr)
+}
diff --git a/src/libsyntax_ext/deriving/debug.rs b/src/libsyntax_ext/deriving/debug.rs
new file mode 100644 (file)
index 0000000..ed3f764
--- /dev/null
@@ -0,0 +1,156 @@
+// 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 deriving::generic::*;
+use deriving::generic::ty::*;
+
+use syntax::ast;
+use syntax::ast::{MetaItem, Expr};
+use syntax::codemap::{Span, respan};
+use syntax::ext::base::{ExtCtxt, Annotatable};
+use syntax::ext::build::AstBuilder;
+use syntax::parse::token;
+use syntax::ptr::P;
+
+pub fn expand_deriving_debug(cx: &mut ExtCtxt,
+                            span: Span,
+                            mitem: &MetaItem,
+                            item: &Annotatable,
+                            push: &mut FnMut(Annotatable))
+{
+    // &mut ::std::fmt::Formatter
+    let fmtr = Ptr(Box::new(Literal(path_std!(cx, core::fmt::Formatter))),
+                   Borrowed(None, ast::MutMutable));
+
+    let trait_def = TraitDef {
+        span: span,
+        attributes: Vec::new(),
+        path: path_std!(cx, core::fmt::Debug),
+        additional_bounds: Vec::new(),
+        generics: LifetimeBounds::empty(),
+        is_unsafe: false,
+        methods: vec![
+            MethodDef {
+                name: "fmt",
+                generics: LifetimeBounds::empty(),
+                explicit_self: borrowed_explicit_self(),
+                args: vec!(fmtr),
+                ret_ty: Literal(path_std!(cx, core::fmt::Result)),
+                attributes: Vec::new(),
+                is_unsafe: false,
+                combine_substructure: combine_substructure(Box::new(|a, b, c| {
+                    show_substructure(a, b, c)
+                }))
+            }
+        ],
+        associated_types: Vec::new(),
+    };
+    trait_def.expand(cx, mitem, item, push)
+}
+
+/// We use the debug builders to do the heavy lifting here
+fn show_substructure(cx: &mut ExtCtxt, span: Span,
+                     substr: &Substructure) -> P<Expr> {
+    // build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build()
+    // or fmt.debug_tuple(<name>).field(&<fieldval>)....build()
+    // based on the "shape".
+    let ident = match *substr.fields {
+        Struct(_) => substr.type_ident,
+        EnumMatching(_, v, _) => v.node.name,
+        EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
+            cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
+        }
+    };
+
+    // We want to make sure we have the expn_id set so that we can use unstable methods
+    let span = Span { expn_id: cx.backtrace(), .. span };
+    let name = cx.expr_lit(span, ast::Lit_::LitStr(ident.name.as_str(),
+                                                   ast::StrStyle::CookedStr));
+    let builder = token::str_to_ident("builder");
+    let builder_expr = cx.expr_ident(span, builder.clone());
+
+    let fmt = substr.nonself_args[0].clone();
+
+    let stmts = match *substr.fields {
+        Struct(ref fields) | EnumMatching(_, _, ref fields) => {
+            let mut stmts = vec![];
+            if fields.is_empty() || fields[0].name.is_none() {
+                // tuple struct/"normal" variant
+                let expr = cx.expr_method_call(span,
+                                               fmt,
+                                               token::str_to_ident("debug_tuple"),
+                                               vec![name]);
+                stmts.push(cx.stmt_let(span, true, builder, expr));
+
+                for field in fields {
+                    // Use double indirection to make sure this works for unsized types
+                    let field = cx.expr_addr_of(field.span, field.self_.clone());
+                    let field = cx.expr_addr_of(field.span, field);
+
+                    let expr = cx.expr_method_call(span,
+                                                   builder_expr.clone(),
+                                                   token::str_to_ident("field"),
+                                                   vec![field]);
+
+                    // Use `let _ = expr;` to avoid triggering the
+                    // unused_results lint.
+                    stmts.push(stmt_let_undescore(cx, span, expr));
+                }
+            } else {
+                // normal struct/struct variant
+                let expr = cx.expr_method_call(span,
+                                               fmt,
+                                               token::str_to_ident("debug_struct"),
+                                               vec![name]);
+                stmts.push(cx.stmt_let(span, true, builder, expr));
+
+                for field in fields {
+                    let name = cx.expr_lit(field.span, ast::Lit_::LitStr(
+                            field.name.unwrap().name.as_str(),
+                            ast::StrStyle::CookedStr));
+
+                    // Use double indirection to make sure this works for unsized types
+                    let field = cx.expr_addr_of(field.span, field.self_.clone());
+                    let field = cx.expr_addr_of(field.span, field);
+                    let expr = cx.expr_method_call(span,
+                                                   builder_expr.clone(),
+                                                   token::str_to_ident("field"),
+                                                   vec![name, field]);
+                    stmts.push(stmt_let_undescore(cx, span, expr));
+                }
+            }
+            stmts
+        }
+        _ => unreachable!()
+    };
+
+    let expr = cx.expr_method_call(span,
+                                   builder_expr,
+                                   token::str_to_ident("finish"),
+                                   vec![]);
+
+    let block = cx.block(span, stmts, Some(expr));
+    cx.expr_block(block)
+}
+
+fn stmt_let_undescore(cx: &mut ExtCtxt,
+                      sp: Span,
+                      expr: P<ast::Expr>) -> P<ast::Stmt> {
+    let local = P(ast::Local {
+        pat: cx.pat_wild(sp),
+        ty: None,
+        init: Some(expr),
+        id: ast::DUMMY_NODE_ID,
+        span: sp,
+        attrs: None,
+    });
+    let decl = respan(sp, ast::DeclLocal(local));
+    P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
+}
diff --git a/src/libsyntax_ext/deriving/decodable.rs b/src/libsyntax_ext/deriving/decodable.rs
new file mode 100644 (file)
index 0000000..4ea4f04
--- /dev/null
@@ -0,0 +1,224 @@
+// 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 compiler code necessary for `#[derive(Decodable)]`. See encodable.rs for more.
+
+use deriving::generic::*;
+use deriving::generic::ty::*;
+
+use syntax::ast;
+use syntax::ast::{MetaItem, Expr, MutMutable};
+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;
+
+pub fn expand_deriving_rustc_decodable(cx: &mut ExtCtxt,
+                                       span: Span,
+                                       mitem: &MetaItem,
+                                       item: &Annotatable,
+                                       push: &mut FnMut(Annotatable))
+{
+    expand_deriving_decodable_imp(cx, span, mitem, item, push, "rustc_serialize")
+}
+
+pub fn expand_deriving_decodable(cx: &mut ExtCtxt,
+                                 span: Span,
+                                 mitem: &MetaItem,
+                                 item: &Annotatable,
+                                 push: &mut FnMut(Annotatable))
+{
+    expand_deriving_decodable_imp(cx, span, mitem, item, push, "serialize")
+}
+
+fn expand_deriving_decodable_imp(cx: &mut ExtCtxt,
+                                 span: Span,
+                                 mitem: &MetaItem,
+                                 item: &Annotatable,
+                                 push: &mut FnMut(Annotatable),
+                                 krate: &'static str)
+{
+    if cx.crate_root != Some("std") {
+        // FIXME(#21880): lift this requirement.
+        cx.span_err(span, "this trait cannot be derived with #![no_std] \
+                           or #![no_core]");
+        return
+    }
+
+    let trait_def = TraitDef {
+        span: span,
+        attributes: Vec::new(),
+        path: Path::new_(vec!(krate, "Decodable"), None, vec!(), true),
+        additional_bounds: Vec::new(),
+        generics: LifetimeBounds::empty(),
+        is_unsafe: false,
+        methods: vec!(
+            MethodDef {
+                name: "decode",
+                generics: LifetimeBounds {
+                    lifetimes: Vec::new(),
+                    bounds: vec!(("__D", vec!(Path::new_(
+                                    vec!(krate, "Decoder"), None,
+                                    vec!(), true))))
+                },
+                explicit_self: None,
+                args: vec!(Ptr(Box::new(Literal(Path::new_local("__D"))),
+                            Borrowed(None, MutMutable))),
+                ret_ty: Literal(Path::new_(
+                    pathvec_std!(cx, core::result::Result),
+                    None,
+                    vec!(Box::new(Self_), Box::new(Literal(Path::new_(
+                        vec!["__D", "Error"], None, vec![], false
+                    )))),
+                    true
+                )),
+                attributes: Vec::new(),
+                is_unsafe: false,
+                combine_substructure: combine_substructure(Box::new(|a, b, c| {
+                    decodable_substructure(a, b, c, krate)
+                })),
+            }
+        ),
+        associated_types: Vec::new(),
+    };
+
+    trait_def.expand(cx, mitem, item, push)
+}
+
+fn decodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
+                          substr: &Substructure,
+                          krate: &str) -> P<Expr> {
+    let decoder = substr.nonself_args[0].clone();
+    let recurse = vec!(cx.ident_of(krate),
+                    cx.ident_of("Decodable"),
+                    cx.ident_of("decode"));
+    let exprdecode = cx.expr_path(cx.path_global(trait_span, recurse));
+    // throw an underscore in front to suppress unused variable warnings
+    let blkarg = cx.ident_of("_d");
+    let blkdecoder = cx.expr_ident(trait_span, blkarg);
+
+    return match *substr.fields {
+        StaticStruct(_, ref summary) => {
+            let nfields = match *summary {
+                Unnamed(ref fields) => fields.len(),
+                Named(ref fields) => fields.len()
+            };
+            let read_struct_field = cx.ident_of("read_struct_field");
+
+            let path = cx.path_ident(trait_span, substr.type_ident);
+            let result = decode_static_fields(cx,
+                                              trait_span,
+                                              path,
+                                              summary,
+                                              |cx, span, name, field| {
+                cx.expr_try(span,
+                    cx.expr_method_call(span, blkdecoder.clone(), read_struct_field,
+                                        vec!(cx.expr_str(span, name),
+                                          cx.expr_usize(span, field),
+                                          exprdecode.clone())))
+            });
+            let result = cx.expr_ok(trait_span, result);
+            cx.expr_method_call(trait_span,
+                                decoder,
+                                cx.ident_of("read_struct"),
+                                vec!(
+                cx.expr_str(trait_span, substr.type_ident.name.as_str()),
+                cx.expr_usize(trait_span, nfields),
+                cx.lambda_expr_1(trait_span, result, blkarg)
+            ))
+        }
+        StaticEnum(_, ref fields) => {
+            let variant = cx.ident_of("i");
+
+            let mut arms = Vec::new();
+            let mut variants = Vec::new();
+            let rvariant_arg = cx.ident_of("read_enum_variant_arg");
+
+            for (i, &(ident, v_span, ref parts)) in fields.iter().enumerate() {
+                variants.push(cx.expr_str(v_span, ident.name.as_str()));
+
+                let path = cx.path(trait_span, vec![substr.type_ident, ident]);
+                let decoded = decode_static_fields(cx,
+                                                   v_span,
+                                                   path,
+                                                   parts,
+                                                   |cx, span, _, field| {
+                    let idx = cx.expr_usize(span, field);
+                    cx.expr_try(span,
+                        cx.expr_method_call(span, blkdecoder.clone(), rvariant_arg,
+                                            vec!(idx, exprdecode.clone())))
+                });
+
+                arms.push(cx.arm(v_span,
+                                 vec!(cx.pat_lit(v_span, cx.expr_usize(v_span, i))),
+                                 decoded));
+            }
+
+            arms.push(cx.arm_unreachable(trait_span));
+
+            let result = cx.expr_ok(trait_span,
+                                    cx.expr_match(trait_span,
+                                                  cx.expr_ident(trait_span, variant), arms));
+            let lambda = cx.lambda_expr(trait_span, vec!(blkarg, variant), result);
+            let variant_vec = cx.expr_vec(trait_span, variants);
+            let variant_vec = cx.expr_addr_of(trait_span, variant_vec);
+            let result = cx.expr_method_call(trait_span, blkdecoder,
+                                             cx.ident_of("read_enum_variant"),
+                                             vec!(variant_vec, lambda));
+            cx.expr_method_call(trait_span,
+                                decoder,
+                                cx.ident_of("read_enum"),
+                                vec!(
+                cx.expr_str(trait_span, substr.type_ident.name.as_str()),
+                cx.lambda_expr_1(trait_span, result, blkarg)
+            ))
+        }
+        _ => cx.bug("expected StaticEnum or StaticStruct in derive(Decodable)")
+    };
+}
+
+/// Create a decoder for a single enum variant/struct:
+/// - `outer_pat_path` is the path to this enum variant/struct
+/// - `getarg` should retrieve the `usize`-th field with name `@str`.
+fn decode_static_fields<F>(cx: &mut ExtCtxt,
+                           trait_span: Span,
+                           outer_pat_path: ast::Path,
+                           fields: &StaticFields,
+                           mut getarg: F)
+                           -> P<Expr> where
+    F: FnMut(&mut ExtCtxt, Span, InternedString, usize) -> P<Expr>,
+{
+    match *fields {
+        Unnamed(ref fields) => {
+            let path_expr = cx.expr_path(outer_pat_path);
+            if fields.is_empty() {
+                path_expr
+            } else {
+                let fields = fields.iter().enumerate().map(|(i, &span)| {
+                    getarg(cx, span,
+                           token::intern_and_get_ident(&format!("_field{}", i)),
+                           i)
+                }).collect();
+
+                cx.expr_call(trait_span, path_expr, fields)
+            }
+        }
+        Named(ref fields) => {
+            // use the field's span to get nicer error messages.
+            let fields = fields.iter().enumerate().map(|(i, &(ident, span))| {
+                let arg = getarg(cx, span, ident.name.as_str(), i);
+                cx.field_imm(span, ident, arg)
+            }).collect();
+            cx.expr_struct(trait_span, outer_pat_path, fields)
+        }
+    }
+}
diff --git a/src/libsyntax_ext/deriving/default.rs b/src/libsyntax_ext/deriving/default.rs
new file mode 100644 (file)
index 0000000..bee63a9
--- /dev/null
@@ -0,0 +1,85 @@
+// 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.
+
+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;
+
+pub fn expand_deriving_default(cx: &mut ExtCtxt,
+                               span: Span,
+                               mitem: &MetaItem,
+                               item: &Annotatable,
+                               push: &mut FnMut(Annotatable))
+{
+    let inline = cx.meta_word(span, InternedString::new("inline"));
+    let attrs = vec!(cx.attribute(span, inline));
+    let trait_def = TraitDef {
+        span: span,
+        attributes: Vec::new(),
+        path: path_std!(cx, core::default::Default),
+        additional_bounds: Vec::new(),
+        generics: LifetimeBounds::empty(),
+        is_unsafe: false,
+        methods: vec!(
+            MethodDef {
+                name: "default",
+                generics: LifetimeBounds::empty(),
+                explicit_self: None,
+                args: Vec::new(),
+                ret_ty: Self_,
+                attributes: attrs,
+                is_unsafe: false,
+                combine_substructure: combine_substructure(Box::new(|a, b, c| {
+                    default_substructure(a, b, c)
+                }))
+            }
+        ),
+        associated_types: Vec::new(),
+    };
+    trait_def.expand(cx, mitem, item, push)
+}
+
+fn default_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
+    let default_ident = cx.std_path(&["default", "Default", "default"]);
+    let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new());
+
+    return match *substr.fields {
+        StaticStruct(_, ref summary) => {
+            match *summary {
+                Unnamed(ref fields) => {
+                    if fields.is_empty() {
+                        cx.expr_ident(trait_span, substr.type_ident)
+                    } else {
+                        let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
+                        cx.expr_call_ident(trait_span, substr.type_ident, exprs)
+                    }
+                }
+                Named(ref fields) => {
+                    let default_fields = fields.iter().map(|&(ident, span)| {
+                        cx.field_imm(span, ident, default_call(span))
+                    }).collect();
+                    cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
+                }
+            }
+        }
+        StaticEnum(..) => {
+            cx.span_err(trait_span, "`Default` cannot be derived for enums, only structs");
+            // let compilation continue
+            cx.expr_usize(trait_span, 0)
+        }
+        _ => cx.span_bug(trait_span, "Non-static method in `derive(Default)`")
+    };
+}
diff --git a/src/libsyntax_ext/deriving/encodable.rs b/src/libsyntax_ext/deriving/encodable.rs
new file mode 100644 (file)
index 0000000..02747d3
--- /dev/null
@@ -0,0 +1,289 @@
+// 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 compiler code necessary to implement the `#[derive(Encodable)]`
+//! (and `Decodable`, in decodable.rs) extension.  The idea here is that
+//! type-defining items may be tagged with `#[derive(Encodable, Decodable)]`.
+//!
+//! For example, a type like:
+//!
+//! ```ignore
+//! #[derive(Encodable, Decodable)]
+//! struct Node { id: usize }
+//! ```
+//!
+//! would generate two implementations like:
+//!
+//! ```ignore
+//! impl<S: Encoder<E>, E> Encodable<S, E> for Node {
+//!     fn encode(&self, s: &mut S) -> Result<(), E> {
+//!         s.emit_struct("Node", 1, |this| {
+//!             this.emit_struct_field("id", 0, |this| {
+//!                 Encodable::encode(&self.id, this)
+//!                 /* this.emit_usize(self.id) can also be used */
+//!             })
+//!         })
+//!     }
+//! }
+//!
+//! impl<D: Decoder<E>, E> Decodable<D, E> for Node {
+//!     fn decode(d: &mut D) -> Result<Node, E> {
+//!         d.read_struct("Node", 1, |this| {
+//!             match this.read_struct_field("id", 0, |this| Decodable::decode(this)) {
+//!                 Ok(id) => Ok(Node { id: id }),
+//!                 Err(e) => Err(e),
+//!             }
+//!         })
+//!     }
+//! }
+//! ```
+//!
+//! Other interesting scenarios are when the item has type parameters or
+//! references other non-built-in types.  A type definition like:
+//!
+//! ```ignore
+//! #[derive(Encodable, Decodable)]
+//! struct Spanned<T> { node: T, span: Span }
+//! ```
+//!
+//! would yield functions like:
+//!
+//! ```ignore
+//! impl<
+//!     S: Encoder<E>,
+//!     E,
+//!     T: Encodable<S, E>
+//! > Encodable<S, E> for Spanned<T> {
+//!     fn encode(&self, s: &mut S) -> Result<(), E> {
+//!         s.emit_struct("Spanned", 2, |this| {
+//!             this.emit_struct_field("node", 0, |this| self.node.encode(this))
+//!                 .unwrap();
+//!             this.emit_struct_field("span", 1, |this| self.span.encode(this))
+//!         })
+//!     }
+//! }
+//!
+//! impl<
+//!     D: Decoder<E>,
+//!     E,
+//!     T: Decodable<D, E>
+//! > Decodable<D, E> for Spanned<T> {
+//!     fn decode(d: &mut D) -> Result<Spanned<T>, E> {
+//!         d.read_struct("Spanned", 2, |this| {
+//!             Ok(Spanned {
+//!                 node: this.read_struct_field("node", 0, |this| Decodable::decode(this))
+//!                     .unwrap(),
+//!                 span: this.read_struct_field("span", 1, |this| Decodable::decode(this))
+//!                     .unwrap(),
+//!             })
+//!         })
+//!     }
+//! }
+//! ```
+
+use deriving::generic::*;
+use deriving::generic::ty::*;
+
+use syntax::ast::{MetaItem, Expr, ExprRet, MutMutable};
+use syntax::codemap::Span;
+use syntax::ext::base::{ExtCtxt,Annotatable};
+use syntax::ext::build::AstBuilder;
+use syntax::parse::token;
+use syntax::ptr::P;
+
+pub fn expand_deriving_rustc_encodable(cx: &mut ExtCtxt,
+                                       span: Span,
+                                       mitem: &MetaItem,
+                                       item: &Annotatable,
+                                       push: &mut FnMut(Annotatable))
+{
+    expand_deriving_encodable_imp(cx, span, mitem, item, push, "rustc_serialize")
+}
+
+pub fn expand_deriving_encodable(cx: &mut ExtCtxt,
+                                 span: Span,
+                                 mitem: &MetaItem,
+                                 item: &Annotatable,
+                                 push: &mut FnMut(Annotatable))
+{
+    expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize")
+}
+
+fn expand_deriving_encodable_imp(cx: &mut ExtCtxt,
+                                 span: Span,
+                                 mitem: &MetaItem,
+                                 item: &Annotatable,
+                                 push: &mut FnMut(Annotatable),
+                                 krate: &'static str)
+{
+    if cx.crate_root != Some("std") {
+        // FIXME(#21880): lift this requirement.
+        cx.span_err(span, "this trait cannot be derived with #![no_std] \
+                           or #![no_core]");
+        return;
+    }
+
+    let trait_def = TraitDef {
+        span: span,
+        attributes: Vec::new(),
+        path: Path::new_(vec!(krate, "Encodable"), None, vec!(), true),
+        additional_bounds: Vec::new(),
+        generics: LifetimeBounds::empty(),
+        is_unsafe: false,
+        methods: vec!(
+            MethodDef {
+                name: "encode",
+                generics: LifetimeBounds {
+                    lifetimes: Vec::new(),
+                    bounds: vec!(("__S", vec!(Path::new_(
+                                    vec!(krate, "Encoder"), None,
+                                    vec!(), true))))
+                },
+                explicit_self: borrowed_explicit_self(),
+                args: vec!(Ptr(Box::new(Literal(Path::new_local("__S"))),
+                            Borrowed(None, MutMutable))),
+                ret_ty: Literal(Path::new_(
+                    pathvec_std!(cx, core::result::Result),
+                    None,
+                    vec!(Box::new(Tuple(Vec::new())), Box::new(Literal(Path::new_(
+                        vec!["__S", "Error"], None, vec![], false
+                    )))),
+                    true
+                )),
+                attributes: Vec::new(),
+                is_unsafe: false,
+                combine_substructure: combine_substructure(Box::new(|a, b, c| {
+                    encodable_substructure(a, b, c)
+                })),
+            }
+        ),
+        associated_types: Vec::new(),
+    };
+
+    trait_def.expand(cx, mitem, item, push)
+}
+
+fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
+                          substr: &Substructure) -> P<Expr> {
+    let encoder = substr.nonself_args[0].clone();
+    // throw an underscore in front to suppress unused variable warnings
+    let blkarg = cx.ident_of("_e");
+    let blkencoder = cx.expr_ident(trait_span, blkarg);
+    let encode = cx.ident_of("encode");
+
+    return match *substr.fields {
+        Struct(ref fields) => {
+            let emit_struct_field = cx.ident_of("emit_struct_field");
+            let mut stmts = Vec::new();
+            for (i, &FieldInfo {
+                    name,
+                    ref self_,
+                    span,
+                    ..
+                }) in fields.iter().enumerate() {
+                let name = match name {
+                    Some(id) => id.name.as_str(),
+                    None => {
+                        token::intern_and_get_ident(&format!("_field{}", i))
+                    }
+                };
+                let enc = cx.expr_method_call(span, self_.clone(),
+                                              encode, vec!(blkencoder.clone()));
+                let lambda = cx.lambda_expr_1(span, enc, blkarg);
+                let call = cx.expr_method_call(span, blkencoder.clone(),
+                                               emit_struct_field,
+                                               vec!(cx.expr_str(span, name),
+                                                 cx.expr_usize(span, i),
+                                                 lambda));
+
+                // last call doesn't need a try!
+                let last = fields.len() - 1;
+                let call = if i != last {
+                    cx.expr_try(span, call)
+                } else {
+                    cx.expr(span, ExprRet(Some(call)))
+                };
+                stmts.push(cx.stmt_expr(call));
+            }
+
+            // unit structs have no fields and need to return Ok()
+            if stmts.is_empty() {
+                let ret_ok = cx.expr(trait_span,
+                                     ExprRet(Some(cx.expr_ok(trait_span,
+                                                             cx.expr_tuple(trait_span, vec![])))));
+                stmts.push(cx.stmt_expr(ret_ok));
+            }
+
+            let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
+            cx.expr_method_call(trait_span,
+                                encoder,
+                                cx.ident_of("emit_struct"),
+                                vec!(
+                cx.expr_str(trait_span, substr.type_ident.name.as_str()),
+                cx.expr_usize(trait_span, fields.len()),
+                blk
+            ))
+        }
+
+        EnumMatching(idx, variant, ref fields) => {
+            // We're not generating an AST that the borrow checker is expecting,
+            // so we need to generate a unique local variable to take the
+            // mutable loan out on, otherwise we get conflicts which don't
+            // actually exist.
+            let me = cx.stmt_let(trait_span, false, blkarg, encoder);
+            let encoder = cx.expr_ident(trait_span, blkarg);
+            let emit_variant_arg = cx.ident_of("emit_enum_variant_arg");
+            let mut stmts = Vec::new();
+            if !fields.is_empty() {
+                let last = fields.len() - 1;
+                for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() {
+                    let enc = cx.expr_method_call(span, self_.clone(),
+                                                  encode, vec!(blkencoder.clone()));
+                    let lambda = cx.lambda_expr_1(span, enc, blkarg);
+                    let call = cx.expr_method_call(span, blkencoder.clone(),
+                                                   emit_variant_arg,
+                                                   vec!(cx.expr_usize(span, i),
+                                                        lambda));
+                    let call = if i != last {
+                        cx.expr_try(span, call)
+                    } else {
+                        cx.expr(span, ExprRet(Some(call)))
+                    };
+                    stmts.push(cx.stmt_expr(call));
+                }
+            } else {
+                let ret_ok = cx.expr(trait_span,
+                                     ExprRet(Some(cx.expr_ok(trait_span,
+                                                             cx.expr_tuple(trait_span, vec![])))));
+                stmts.push(cx.stmt_expr(ret_ok));
+            }
+
+            let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
+            let name = cx.expr_str(trait_span, variant.node.name.name.as_str());
+            let call = cx.expr_method_call(trait_span, blkencoder,
+                                           cx.ident_of("emit_enum_variant"),
+                                           vec!(name,
+                                             cx.expr_usize(trait_span, idx),
+                                             cx.expr_usize(trait_span, fields.len()),
+                                             blk));
+            let blk = cx.lambda_expr_1(trait_span, call, blkarg);
+            let ret = cx.expr_method_call(trait_span,
+                                          encoder,
+                                          cx.ident_of("emit_enum"),
+                                          vec!(
+                cx.expr_str(trait_span, substr.type_ident.name.as_str()),
+                blk
+            ));
+            cx.expr_block(cx.block(trait_span, vec!(me), Some(ret)))
+        }
+
+        _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)")
+    };
+}
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
new file mode 100644 (file)
index 0000000..3af7017
--- /dev/null
@@ -0,0 +1,1633 @@
+// Copyright 2013-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.
+
+//! Some code that abstracts away much of the boilerplate of writing
+//! `derive` instances for traits. Among other things it manages getting
+//! access to the fields of the 4 different sorts of structs and enum
+//! variants, as well as creating the method and impl ast instances.
+//!
+//! Supported features (fairly exhaustive):
+//!
+//! - Methods taking any number of parameters of any type, and returning
+//!   any type, other than vectors, bottom and closures.
+//! - Generating `impl`s for types with type parameters and lifetimes
+//!   (e.g. `Option<T>`), the parameters are automatically given the
+//!   current trait as a bound. (This includes separate type parameters
+//!   and lifetimes for methods.)
+//! - Additional bounds on the type parameters (`TraitDef.additional_bounds`)
+//!
+//! The most important thing for implementers is the `Substructure` and
+//! `SubstructureFields` objects. The latter groups 5 possibilities of the
+//! arguments:
+//!
+//! - `Struct`, when `Self` is a struct (including tuple structs, e.g
+//!   `struct T(i32, char)`).
+//! - `EnumMatching`, when `Self` is an enum and all the arguments are the
+//!   same variant of the enum (e.g. `Some(1)`, `Some(3)` and `Some(4)`)
+//! - `EnumNonMatchingCollapsed` when `Self` is an enum and the arguments
+//!   are not the same variant (e.g. `None`, `Some(1)` and `None`).
+//! - `StaticEnum` and `StaticStruct` for static methods, where the type
+//!   being derived upon is either an enum or struct respectively. (Any
+//!   argument with type Self is just grouped among the non-self
+//!   arguments.)
+//!
+//! In the first two cases, the values from the corresponding fields in
+//! all the arguments are grouped together. For `EnumNonMatchingCollapsed`
+//! this isn't possible (different variants have different fields), so the
+//! fields are inaccessible. (Previous versions of the deriving infrastructure
+//! had a way to expand into code that could access them, at the cost of
+//! generating exponential amounts of code; see issue #15375). There are no
+//! fields with values in the static cases, so these are treated entirely
+//! differently.
+//!
+//! The non-static cases have `Option<ident>` in several places associated
+//! with field `expr`s. This represents the name of the field it is
+//! associated with. It is only not `None` when the associated field has
+//! an identifier in the source code. For example, the `x`s in the
+//! following snippet
+//!
+//! ```rust
+//! # #![allow(dead_code)]
+//! struct A { x : i32 }
+//!
+//! struct B(i32);
+//!
+//! enum C {
+//!     C0(i32),
+//!     C1 { x: i32 }
+//! }
+//! ```
+//!
+//! The `i32`s in `B` and `C0` don't have an identifier, so the
+//! `Option<ident>`s would be `None` for them.
+//!
+//! In the static cases, the structure is summarised, either into the just
+//! spans of the fields or a list of spans and the field idents (for tuple
+//! structs and record structs, respectively), or a list of these, for
+//! enums (one for each variant). For empty struct and empty enum
+//! variants, it is represented as a count of 0.
+//!
+//! # "`cs`" functions
+//!
+//! The `cs_...` functions ("combine substructure) are designed to
+//! make life easier by providing some pre-made recipes for common
+//! threads; mostly calling the function being derived on all the
+//! arguments and then combining them back together in some way (or
+//! letting the user chose that). They are not meant to be the only
+//! way to handle the structures that this code creates.
+//!
+//! # Examples
+//!
+//! The following simplified `PartialEq` is used for in-code examples:
+//!
+//! ```rust
+//! trait PartialEq {
+//!     fn eq(&self, other: &Self) -> bool;
+//! }
+//! impl PartialEq for i32 {
+//!     fn eq(&self, other: &i32) -> bool {
+//!         *self == *other
+//!     }
+//! }
+//! ```
+//!
+//! Some examples of the values of `SubstructureFields` follow, using the
+//! above `PartialEq`, `A`, `B` and `C`.
+//!
+//! ## Structs
+//!
+//! When generating the `expr` for the `A` impl, the `SubstructureFields` is
+//!
+//! ```{.text}
+//! Struct(vec![FieldInfo {
+//!            span: <span of x>
+//!            name: Some(<ident of x>),
+//!            self_: <expr for &self.x>,
+//!            other: vec![<expr for &other.x]
+//!          }])
+//! ```
+//!
+//! For the `B` impl, called with `B(a)` and `B(b)`,
+//!
+//! ```{.text}
+//! Struct(vec![FieldInfo {
+//!           span: <span of `i32`>,
+//!           name: None,
+//!           self_: <expr for &a>
+//!           other: vec![<expr for &b>]
+//!          }])
+//! ```
+//!
+//! ## Enums
+//!
+//! When generating the `expr` for a call with `self == C0(a)` and `other
+//! == C0(b)`, the SubstructureFields is
+//!
+//! ```{.text}
+//! EnumMatching(0, <ast::Variant for C0>,
+//!              vec![FieldInfo {
+//!                 span: <span of i32>
+//!                 name: None,
+//!                 self_: <expr for &a>,
+//!                 other: vec![<expr for &b>]
+//!               }])
+//! ```
+//!
+//! For `C1 {x}` and `C1 {x}`,
+//!
+//! ```{.text}
+//! EnumMatching(1, <ast::Variant for C1>,
+//!              vec![FieldInfo {
+//!                 span: <span of x>
+//!                 name: Some(<ident of x>),
+//!                 self_: <expr for &self.x>,
+//!                 other: vec![<expr for &other.x>]
+//!                }])
+//! ```
+//!
+//! For `C0(a)` and `C1 {x}` ,
+//!
+//! ```{.text}
+//! EnumNonMatchingCollapsed(
+//!     vec![<ident of self>, <ident of __arg_1>],
+//!     &[<ast::Variant for C0>, <ast::Variant for C1>],
+//!     &[<ident for self index value>, <ident of __arg_1 index value>])
+//! ```
+//!
+//! It is the same for when the arguments are flipped to `C1 {x}` and
+//! `C0(a)`; the only difference is what the values of the identifiers
+//! <ident for self index value> and <ident of __arg_1 index value> will
+//! be in the generated code.
+//!
+//! `EnumNonMatchingCollapsed` deliberately provides far less information
+//! than is generally available for a given pair of variants; see #15375
+//! for discussion.
+//!
+//! ## Static
+//!
+//! A static method on the types above would result in,
+//!
+//! ```{.text}
+//! StaticStruct(<ast::VariantData of A>, Named(vec![(<ident of x>, <span of x>)]))
+//!
+//! StaticStruct(<ast::VariantData of B>, Unnamed(vec![<span of x>]))
+//!
+//! StaticEnum(<ast::EnumDef of C>,
+//!            vec![(<ident of C0>, <span of C0>, Unnamed(vec![<span of i32>])),
+//!                 (<ident of C1>, <span of C1>, Named(vec![(<ident of x>, <span of x>)]))])
+//! ```
+
+pub use self::StaticFields::*;
+pub use self::SubstructureFields::*;
+use self::StructType::*;
+
+use std::cell::RefCell;
+use std::collections::HashSet;
+use std::vec;
+
+use syntax::abi::Abi;
+use syntax::abi;
+use syntax::ast;
+use syntax::ast::{EnumDef, Expr, Ident, Generics, VariantData};
+use syntax::ast_util;
+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::util::move_map::MoveMap;
+use syntax::parse::token::{intern, InternedString};
+use syntax::parse::token::special_idents;
+use syntax::ptr::P;
+
+use self::ty::{LifetimeBounds, Path, Ptr, PtrTy, Self_, Ty};
+
+pub mod ty;
+
+pub struct TraitDef<'a> {
+    /// The span for the current #[derive(Foo)] header.
+    pub span: Span,
+
+    pub attributes: Vec<ast::Attribute>,
+
+    /// Path of the trait, including any type parameters
+    pub path: Path<'a>,
+
+    /// Additional bounds required of any type parameters of the type,
+    /// other than the current trait
+    pub additional_bounds: Vec<Ty<'a>>,
+
+    /// Any extra lifetimes and/or bounds, e.g. `D: serialize::Decoder`
+    pub generics: LifetimeBounds<'a>,
+
+    /// Is it an `unsafe` trait?
+    pub is_unsafe: bool,
+
+    pub methods: Vec<MethodDef<'a>>,
+
+    pub associated_types: Vec<(ast::Ident, Ty<'a>)>,
+}
+
+
+pub struct MethodDef<'a> {
+    /// name of the method
+    pub name: &'a str,
+    /// List of generics, e.g. `R: rand::Rng`
+    pub generics: LifetimeBounds<'a>,
+
+    /// Whether there is a self argument (outer Option) i.e. whether
+    /// this is a static function, and whether it is a pointer (inner
+    /// Option)
+    pub explicit_self: Option<Option<PtrTy<'a>>>,
+
+    /// Arguments other than the self argument
+    pub args: Vec<Ty<'a>>,
+
+    /// Return type
+    pub ret_ty: Ty<'a>,
+
+    pub attributes: Vec<ast::Attribute>,
+
+    // Is it an `unsafe fn`?
+    pub is_unsafe: bool,
+
+    pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>,
+}
+
+/// All the data about the data structure/method being derived upon.
+pub struct Substructure<'a> {
+    /// ident of self
+    pub type_ident: Ident,
+    /// ident of the method
+    pub method_ident: Ident,
+    /// dereferenced access to any `Self_` or `Ptr(Self_, _)` arguments
+    pub self_args: &'a [P<Expr>],
+    /// verbatim access to any other arguments
+    pub nonself_args: &'a [P<Expr>],
+    pub fields: &'a SubstructureFields<'a>
+}
+
+/// Summary of the relevant parts of a struct/enum field.
+pub struct FieldInfo<'a> {
+    pub span: Span,
+    /// None for tuple structs/normal enum variants, Some for normal
+    /// structs/struct enum variants.
+    pub name: Option<Ident>,
+    /// The expression corresponding to this field of `self`
+    /// (specifically, a reference to it).
+    pub self_: P<Expr>,
+    /// The expressions corresponding to references to this field in
+    /// the other `Self` arguments.
+    pub other: Vec<P<Expr>>,
+    /// The attributes on the field
+    pub attrs: &'a [ast::Attribute],
+}
+
+/// Fields for a static method
+pub enum StaticFields {
+    /// Tuple structs/enum variants like this.
+    Unnamed(Vec<Span>),
+    /// Normal structs/struct variants.
+    Named(Vec<(Ident, Span)>),
+}
+
+/// A summary of the possible sets of fields.
+pub enum SubstructureFields<'a> {
+    Struct(Vec<FieldInfo<'a>>),
+    /// Matching variants of the enum: variant index, ast::Variant,
+    /// fields: the field name is only non-`None` in the case of a struct
+    /// variant.
+    EnumMatching(usize, &'a ast::Variant, Vec<FieldInfo<'a>>),
+
+    /// Non-matching variants of the enum, but with all state hidden from
+    /// the consequent code.  The first component holds `Ident`s for all of
+    /// the `Self` arguments; the second component is a slice of all of the
+    /// variants for the enum itself, and the third component is a list of
+    /// `Ident`s bound to the variant index values for each of the actual
+    /// input `Self` arguments.
+    EnumNonMatchingCollapsed(Vec<Ident>, &'a [P<ast::Variant>], &'a [Ident]),
+
+    /// A static method where `Self` is a struct.
+    StaticStruct(&'a ast::VariantData, StaticFields),
+    /// A static method where `Self` is an enum.
+    StaticEnum(&'a ast::EnumDef, Vec<(Ident, Span, StaticFields)>),
+}
+
+
+
+/// Combine the values of all the fields together. The last argument is
+/// all the fields of all the structures.
+pub type CombineSubstructureFunc<'a> =
+    Box<FnMut(&mut ExtCtxt, Span, &Substructure) -> P<Expr> + 'a>;
+
+/// Deal with non-matching enum variants.  The tuple is a list of
+/// identifiers (one for each `Self` argument, which could be any of the
+/// variants since they have been collapsed together) and the identifiers
+/// holding the variant index value for each of the `Self` arguments.  The
+/// last argument is all the non-`Self` args of the method being derived.
+pub type EnumNonMatchCollapsedFunc<'a> =
+    Box<FnMut(&mut ExtCtxt, Span, (&[Ident], &[Ident]), &[P<Expr>]) -> P<Expr> + 'a>;
+
+pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>)
+    -> RefCell<CombineSubstructureFunc<'a>> {
+    RefCell::new(f)
+}
+
+/// 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>> {
+    use syntax::visit;
+
+    struct Visitor<'a> {
+        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) {
+            match ty.node {
+                ast::TyPath(_, 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()));
+                            }
+                        }
+                        None => {}
+                    }
+                }
+                _ => {}
+            }
+
+            visit::walk_ty(self, ty)
+        }
+    }
+
+    let mut visitor = Visitor {
+        ty_param_names: ty_param_names,
+        types: Vec::new(),
+    };
+
+    visit::Visitor::visit_ty(&mut visitor, ty);
+
+    visitor.types
+}
+
+impl<'a> TraitDef<'a> {
+    pub fn expand(&self,
+                  cx: &mut ExtCtxt,
+                  mitem: &ast::MetaItem,
+                  item: &'a Annotatable,
+                  push: &mut FnMut(Annotatable))
+    {
+        match *item {
+            Annotatable::Item(ref item) => {
+                let newitem = match item.node {
+                    ast::ItemStruct(ref struct_def, ref generics) => {
+                        self.expand_struct_def(cx,
+                                               &struct_def,
+                                               item.ident,
+                                               generics)
+                    }
+                    ast::ItemEnum(ref enum_def, ref generics) => {
+                        self.expand_enum_def(cx,
+                                             enum_def,
+                                             &item.attrs,
+                                             item.ident,
+                                             generics)
+                    }
+                    _ => {
+                        cx.span_err(mitem.span,
+                                    "`derive` may only be applied to structs and enums");
+                        return;
+                    }
+                };
+                // Keep the lint attributes of the previous item to control how the
+                // generated implementations are linted
+                let mut attrs = newitem.attrs.clone();
+                attrs.extend(item.attrs.iter().filter(|a| {
+                    match &a.name()[..] {
+                        "allow" | "warn" | "deny" | "forbid" | "stable" | "unstable" => true,
+                        _ => false,
+                    }
+                }).cloned());
+                push(Annotatable::Item(P(ast::Item {
+                    attrs: attrs,
+                    ..(*newitem).clone()
+                })))
+            }
+            _ => {
+                cx.span_err(mitem.span, "`derive` may only be applied to structs and enums");
+            }
+        }
+    }
+
+    /// Given that we are deriving a trait `DerivedTrait` for a type like:
+    ///
+    /// ```ignore
+    /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
+    ///     a: A,
+    ///     b: B::Item,
+    ///     b1: <B as DeclaredTrait>::Item,
+    ///     c1: <C as WhereTrait>::Item,
+    ///     c2: Option<<C as WhereTrait>::Item>,
+    ///     ...
+    /// }
+    /// ```
+    ///
+    /// create an impl like:
+    ///
+    /// ```ignore
+    /// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ...  Z> where
+    ///     C:                       WhereTrait,
+    ///     A: DerivedTrait + B1 + ... + BN,
+    ///     B: DerivedTrait + B1 + ... + BN,
+    ///     C: DerivedTrait + B1 + ... + BN,
+    ///     B::Item:                 DerivedTrait + B1 + ... + BN,
+    ///     <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
+    ///     ...
+    /// {
+    ///     ...
+    /// }
+    /// ```
+    ///
+    /// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and
+    /// therefore does not get bound by the derived trait.
+    fn create_derived_impl(&self,
+                           cx: &mut ExtCtxt,
+                           type_ident: Ident,
+                           generics: &Generics,
+                           field_tys: Vec<P<ast::Ty>>,
+                           methods: Vec<P<ast::ImplItem>>) -> P<ast::Item> {
+        let trait_path = self.path.to_path(cx, self.span, type_ident, generics);
+
+        // Transform associated types from `deriving::ty::Ty` into `ast::ImplItem`
+        let associated_types = self.associated_types.iter().map(|&(ident, ref type_def)| {
+            P(ast::ImplItem {
+                id: ast::DUMMY_NODE_ID,
+                span: self.span,
+                ident: ident,
+                vis: ast::Inherited,
+                attrs: Vec::new(),
+                node: ast::ImplItemKind::Type(type_def.to_ty(cx,
+                    self.span,
+                    type_ident,
+                    generics
+                )),
+            })
+        });
+
+        let Generics { mut lifetimes, ty_params, mut where_clause } =
+            self.generics.to_generics(cx, self.span, type_ident, generics);
+        let mut ty_params = ty_params.into_vec();
+
+        // Copy the lifetimes
+        lifetimes.extend(generics.lifetimes.iter().cloned());
+
+        // Create the type parameters.
+        ty_params.extend(generics.ty_params.iter().map(|ty_param| {
+            // I don't think this can be moved out of the loop, since
+            // a TyParamBound requires an ast id
+            let mut bounds: Vec<_> =
+                // extra restrictions on the generics parameters to the type being derived upon
+                self.additional_bounds.iter().map(|p| {
+                    cx.typarambound(p.to_path(cx, self.span,
+                                                  type_ident, generics))
+                }).collect();
+
+            // require the current trait
+            bounds.push(cx.typarambound(trait_path.clone()));
+
+            // also add in any bounds from the declaration
+            for declared_bound in ty_param.bounds.iter() {
+                bounds.push((*declared_bound).clone());
+            }
+
+            cx.typaram(self.span,
+                       ty_param.ident,
+                       P::from_vec(bounds),
+                       None)
+        }));
+
+        // and similarly for where clauses
+        where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| {
+            match *clause {
+                ast::WherePredicate::BoundPredicate(ref wb) => {
+                    ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
+                        span: self.span,
+                        bound_lifetimes: wb.bound_lifetimes.clone(),
+                        bounded_ty: wb.bounded_ty.clone(),
+                        bounds: P::from_vec(wb.bounds.iter().cloned().collect())
+                    })
+                }
+                ast::WherePredicate::RegionPredicate(ref rb) => {
+                    ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
+                        span: self.span,
+                        lifetime: rb.lifetime,
+                        bounds: rb.bounds.iter().cloned().collect()
+                    })
+                }
+                ast::WherePredicate::EqPredicate(ref we) => {
+                    ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
+                        id: ast::DUMMY_NODE_ID,
+                        span: self.span,
+                        path: we.path.clone(),
+                        ty: we.ty.clone()
+                    })
+                }
+            }
+        }));
+
+        if !ty_params.is_empty() {
+            let ty_param_names: Vec<ast::Name> = ty_params.iter()
+                .map(|ty_param| ty_param.ident.name)
+                .collect();
+
+            let mut processed_field_types = HashSet::new();
+            for field_ty in field_tys {
+                let tys = find_type_parameters(&*field_ty, &ty_param_names);
+
+                for ty in tys {
+                    // if we have already handled this type, skip it
+                    if let ast::TyPath(_, ref p) = ty.node {
+                        if p.segments.len() == 1
+                            && ty_param_names.contains(&p.segments[0].identifier.name)
+                            || processed_field_types.contains(&p.segments) {
+                            continue;
+                        };
+                        processed_field_types.insert(p.segments.clone());
+                    }
+                    let mut bounds: Vec<_> = self.additional_bounds.iter().map(|p| {
+                        cx.typarambound(p.to_path(cx, self.span, type_ident, generics))
+                    }).collect();
+
+                    // require the current trait
+                    bounds.push(cx.typarambound(trait_path.clone()));
+
+                    let predicate = ast::WhereBoundPredicate {
+                        span: self.span,
+                        bound_lifetimes: vec![],
+                        bounded_ty: ty,
+                        bounds: P::from_vec(bounds),
+                    };
+
+                    let predicate = ast::WherePredicate::BoundPredicate(predicate);
+                    where_clause.predicates.push(predicate);
+                }
+            }
+        }
+
+        let trait_generics = Generics {
+            lifetimes: lifetimes,
+            ty_params: P::from_vec(ty_params),
+            where_clause: where_clause
+        };
+
+        // Create the reference to the trait.
+        let trait_ref = cx.trait_ref(trait_path);
+
+        // Create the type parameters on the `self` path.
+        let self_ty_params = generics.ty_params.map(|ty_param| {
+            cx.ty_ident(self.span, ty_param.ident)
+        });
+
+        let self_lifetimes: Vec<ast::Lifetime> =
+            generics.lifetimes
+            .iter()
+            .map(|ld| ld.lifetime)
+            .collect();
+
+        // Create the type of `self`.
+        let self_type = cx.ty_path(
+            cx.path_all(self.span, false, vec!( type_ident ), self_lifetimes,
+                        self_ty_params.into_vec(), Vec::new()));
+
+        let attr = cx.attribute(
+            self.span,
+            cx.meta_word(self.span,
+                         InternedString::new("automatically_derived")));
+        // Just mark it now since we know that it'll end up used downstream
+        attr::mark_used(&attr);
+        let opt_trait_ref = Some(trait_ref);
+        let ident = ast_util::impl_pretty_name(&opt_trait_ref, Some(&*self_type));
+        let unused_qual = cx.attribute(
+            self.span,
+            cx.meta_list(self.span,
+                         InternedString::new("allow"),
+                         vec![cx.meta_word(self.span,
+                                           InternedString::new("unused_qualifications"))]));
+        let mut a = vec![attr, unused_qual];
+        a.extend(self.attributes.iter().cloned());
+
+        let unsafety = if self.is_unsafe {
+            ast::Unsafety::Unsafe
+        } else {
+            ast::Unsafety::Normal
+        };
+
+        cx.item(
+            self.span,
+            ident,
+            a,
+            ast::ItemImpl(unsafety,
+                          ast::ImplPolarity::Positive,
+                          trait_generics,
+                          opt_trait_ref,
+                          self_type,
+                          methods.into_iter().chain(associated_types).collect()))
+    }
+
+    fn expand_struct_def(&self,
+                         cx: &mut ExtCtxt,
+                         struct_def: &'a VariantData,
+                         type_ident: Ident,
+                         generics: &Generics) -> P<ast::Item> {
+        let field_tys: Vec<P<ast::Ty>> = struct_def.fields().iter()
+            .map(|field| field.node.ty.clone())
+            .collect();
+
+        let methods = self.methods.iter().map(|method_def| {
+            let (explicit_self, self_args, nonself_args, tys) =
+                method_def.split_self_nonself_args(
+                    cx, self, type_ident, generics);
+
+            let body = if method_def.is_static() {
+                method_def.expand_static_struct_method_body(
+                    cx,
+                    self,
+                    struct_def,
+                    type_ident,
+                    &self_args[..],
+                    &nonself_args[..])
+            } else {
+                method_def.expand_struct_method_body(cx,
+                                                     self,
+                                                     struct_def,
+                                                     type_ident,
+                                                     &self_args[..],
+                                                     &nonself_args[..])
+            };
+
+            method_def.create_method(cx,
+                                     self,
+                                     type_ident,
+                                     generics,
+                                     abi::Rust,
+                                     explicit_self,
+                                     tys,
+                                     body)
+        }).collect();
+
+        self.create_derived_impl(cx, type_ident, generics, field_tys, methods)
+    }
+
+    fn expand_enum_def(&self,
+                       cx: &mut ExtCtxt,
+                       enum_def: &'a EnumDef,
+                       type_attrs: &[ast::Attribute],
+                       type_ident: Ident,
+                       generics: &Generics) -> P<ast::Item> {
+        let mut field_tys = Vec::new();
+
+        for variant in &enum_def.variants {
+            field_tys.extend(variant.node.data.fields().iter()
+                .map(|field| field.node.ty.clone()));
+        }
+
+        let methods = self.methods.iter().map(|method_def| {
+            let (explicit_self, self_args, nonself_args, tys) =
+                method_def.split_self_nonself_args(cx, self,
+                                                   type_ident, generics);
+
+            let body = if method_def.is_static() {
+                method_def.expand_static_enum_method_body(
+                    cx,
+                    self,
+                    enum_def,
+                    type_ident,
+                    &self_args[..],
+                    &nonself_args[..])
+            } else {
+                method_def.expand_enum_method_body(cx,
+                                                   self,
+                                                   enum_def,
+                                                   type_attrs,
+                                                   type_ident,
+                                                   self_args,
+                                                   &nonself_args[..])
+            };
+
+            method_def.create_method(cx,
+                                     self,
+                                     type_ident,
+                                     generics,
+                                     abi::Rust,
+                                     explicit_self,
+                                     tys,
+                                     body)
+        }).collect();
+
+        self.create_derived_impl(cx, type_ident, generics, field_tys, methods)
+    }
+}
+
+fn find_repr_type_name(diagnostic: &Handler,
+                       type_attrs: &[ast::Attribute]) -> &'static str {
+    let mut repr_type_name = "i32";
+    for a in type_attrs {
+        for r in &attr::find_repr_attrs(diagnostic, a) {
+            repr_type_name = match *r {
+                attr::ReprAny | attr::ReprPacked | attr::ReprSimd => continue,
+                attr::ReprExtern => "i32",
+
+                attr::ReprInt(_, attr::SignedInt(ast::TyIs)) => "isize",
+                attr::ReprInt(_, attr::SignedInt(ast::TyI8)) => "i8",
+                attr::ReprInt(_, attr::SignedInt(ast::TyI16)) => "i16",
+                attr::ReprInt(_, attr::SignedInt(ast::TyI32)) => "i32",
+                attr::ReprInt(_, attr::SignedInt(ast::TyI64)) => "i64",
+
+                attr::ReprInt(_, attr::UnsignedInt(ast::TyUs)) => "usize",
+                attr::ReprInt(_, attr::UnsignedInt(ast::TyU8)) => "u8",
+                attr::ReprInt(_, attr::UnsignedInt(ast::TyU16)) => "u16",
+                attr::ReprInt(_, attr::UnsignedInt(ast::TyU32)) => "u32",
+                attr::ReprInt(_, attr::UnsignedInt(ast::TyU64)) => "u64",
+            }
+        }
+    }
+    repr_type_name
+}
+
+impl<'a> MethodDef<'a> {
+    fn call_substructure_method(&self,
+                                cx: &mut ExtCtxt,
+                                trait_: &TraitDef,
+                                type_ident: Ident,
+                                self_args: &[P<Expr>],
+                                nonself_args: &[P<Expr>],
+                                fields: &SubstructureFields)
+        -> P<Expr> {
+        let substructure = Substructure {
+            type_ident: type_ident,
+            method_ident: cx.ident_of(self.name),
+            self_args: self_args,
+            nonself_args: nonself_args,
+            fields: fields
+        };
+        let mut f = self.combine_substructure.borrow_mut();
+        let f: &mut CombineSubstructureFunc = &mut *f;
+        f(cx, trait_.span, &substructure)
+    }
+
+    fn get_ret_ty(&self,
+                  cx: &mut ExtCtxt,
+                  trait_: &TraitDef,
+                  generics: &Generics,
+                  type_ident: Ident)
+                  -> P<ast::Ty> {
+        self.ret_ty.to_ty(cx, trait_.span, type_ident, generics)
+    }
+
+    fn is_static(&self) -> bool {
+        self.explicit_self.is_none()
+    }
+
+    fn split_self_nonself_args(&self,
+                               cx: &mut ExtCtxt,
+                               trait_: &TraitDef,
+                               type_ident: Ident,
+                               generics: &Generics)
+        -> (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);
+
+                self_args.push(self_expr);
+                nonstatic = true;
+
+                explicit_self
+            }
+            None => codemap::respan(trait_.span, ast::SelfStatic),
+        };
+
+        for (i, ty) in self.args.iter().enumerate() {
+            let ast_ty = ty.to_ty(cx, trait_.span, type_ident, generics);
+            let ident = cx.ident_of(&format!("__arg_{}", i));
+            arg_tys.push((ident, ast_ty));
+
+            let arg_expr = cx.expr_ident(trait_.span, ident);
+
+            match *ty {
+                // for static methods, just treat any Self
+                // arguments as a normal arg
+                Self_ if nonstatic  => {
+                    self_args.push(arg_expr);
+                }
+                Ptr(ref ty, _) if **ty == Self_ && nonstatic => {
+                    self_args.push(cx.expr_deref(trait_.span, arg_expr))
+                }
+                _ => {
+                    nonself_args.push(arg_expr);
+                }
+            }
+        }
+
+        (ast_explicit_self, self_args, nonself_args, arg_tys)
+    }
+
+    fn create_method(&self,
+                     cx: &mut ExtCtxt,
+                     trait_: &TraitDef,
+                     type_ident: Ident,
+                     generics: &Generics,
+                     abi: Abi,
+                     explicit_self: ast::ExplicitSelf,
+                     arg_types: Vec<(Ident, P<ast::Ty>)> ,
+                     body: P<Expr>) -> P<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::SelfStatic => None,
+            // creating fresh self id
+            _ => Some(ast::Arg::new_self(trait_.span, ast::MutImmutable, special_idents::self_))
+        };
+        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 ret_type = self.get_ret_ty(cx, trait_, generics, type_ident);
+
+        let method_ident = cx.ident_of(self.name);
+        let fn_decl = cx.fn_decl(args, ret_type);
+        let body_block = cx.block_expr(body);
+
+        let unsafety = if self.is_unsafe {
+            ast::Unsafety::Unsafe
+        } else {
+            ast::Unsafety::Normal
+        };
+
+        // Create the method.
+        P(ast::ImplItem {
+            id: ast::DUMMY_NODE_ID,
+            attrs: self.attributes.clone(),
+            span: trait_.span,
+            vis: ast::Inherited,
+            ident: method_ident,
+            node: ast::ImplItemKind::Method(ast::MethodSig {
+                generics: fn_generics,
+                abi: abi,
+                explicit_self: explicit_self,
+                unsafety: unsafety,
+                constness: ast::Constness::NotConst,
+                decl: fn_decl
+            }, body_block)
+        })
+    }
+
+    /// ```ignore
+    /// #[derive(PartialEq)]
+    /// struct A { x: i32, y: i32 }
+    ///
+    /// // equivalent to:
+    /// impl PartialEq for A {
+    ///     fn eq(&self, __arg_1: &A) -> bool {
+    ///         match *self {
+    ///             A {x: ref __self_0_0, y: ref __self_0_1} => {
+    ///                 match *__arg_1 {
+    ///                     A {x: ref __self_1_0, y: ref __self_1_1} => {
+    ///                         __self_0_0.eq(__self_1_0) && __self_0_1.eq(__self_1_1)
+    ///                     }
+    ///                 }
+    ///             }
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    fn expand_struct_method_body<'b>(&self,
+                                 cx: &mut ExtCtxt,
+                                 trait_: &TraitDef<'b>,
+                                 struct_def: &'b VariantData,
+                                 type_ident: Ident,
+                                 self_args: &[P<Expr>],
+                                 nonself_args: &[P<Expr>])
+        -> P<Expr> {
+
+        let mut raw_fields = Vec::new(); // Vec<[fields of self],
+                                 // [fields of next Self arg], [etc]>
+        let mut patterns = Vec::new();
+        for i in 0..self_args.len() {
+            let struct_path= cx.path(DUMMY_SP, vec!( type_ident ));
+            let (pat, ident_expr) =
+                trait_.create_struct_pattern(cx,
+                                             struct_path,
+                                             struct_def,
+                                             &format!("__self_{}",
+                                                     i),
+                                             ast::MutImmutable);
+            patterns.push(pat);
+            raw_fields.push(ident_expr);
+        }
+
+        // transpose raw_fields
+        let fields = if !raw_fields.is_empty() {
+            let mut raw_fields = raw_fields.into_iter().map(|v| v.into_iter());
+            let first_field = raw_fields.next().unwrap();
+            let mut other_fields: Vec<vec::IntoIter<_>>
+                = raw_fields.collect();
+            first_field.map(|(span, opt_id, field, attrs)| {
+                FieldInfo {
+                    span: span,
+                    name: opt_id,
+                    self_: field,
+                    other: other_fields.iter_mut().map(|l| {
+                        match l.next().unwrap() {
+                            (_, _, ex, _) => ex
+                        }
+                    }).collect(),
+                    attrs: attrs,
+                }
+            }).collect()
+        } else {
+            cx.span_bug(trait_.span,
+                        "no self arguments to non-static method in generic \
+                         `derive`")
+        };
+
+        // body of the inner most destructuring match
+        let mut body = self.call_substructure_method(
+            cx,
+            trait_,
+            type_ident,
+            self_args,
+            nonself_args,
+            &Struct(fields));
+
+        // make a series of nested matches, to destructure the
+        // structs. This is actually right-to-left, but it shouldn't
+        // matter.
+        for (arg_expr, pat) in self_args.iter().zip(patterns) {
+            body = cx.expr_match(trait_.span, arg_expr.clone(),
+                                     vec!( cx.arm(trait_.span, vec!(pat.clone()), body) ))
+        }
+        body
+    }
+
+    fn expand_static_struct_method_body(&self,
+                                        cx: &mut ExtCtxt,
+                                        trait_: &TraitDef,
+                                        struct_def: &VariantData,
+                                        type_ident: Ident,
+                                        self_args: &[P<Expr>],
+                                        nonself_args: &[P<Expr>])
+        -> P<Expr> {
+        let summary = trait_.summarise_struct(cx, struct_def);
+
+        self.call_substructure_method(cx,
+                                      trait_,
+                                      type_ident,
+                                      self_args, nonself_args,
+                                      &StaticStruct(struct_def, summary))
+    }
+
+    /// ```ignore
+    /// #[derive(PartialEq)]
+    /// enum A {
+    ///     A1,
+    ///     A2(i32)
+    /// }
+    ///
+    /// // is equivalent to
+    ///
+    /// impl PartialEq for A {
+    ///     fn eq(&self, __arg_1: &A) -> ::bool {
+    ///         match (&*self, &*__arg_1) {
+    ///             (&A1, &A1) => true,
+    ///             (&A2(ref __self_0),
+    ///              &A2(ref __arg_1_0)) => (*__self_0).eq(&(*__arg_1_0)),
+    ///             _ => {
+    ///                 let __self_vi = match *self { A1(..) => 0, A2(..) => 1 };
+    ///                 let __arg_1_vi = match *__arg_1 { A1(..) => 0, A2(..) => 1 };
+    ///                 false
+    ///             }
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// (Of course `__self_vi` and `__arg_1_vi` are unused for
+    /// `PartialEq`, and those subcomputations will hopefully be removed
+    /// as their results are unused.  The point of `__self_vi` and
+    /// `__arg_1_vi` is for `PartialOrd`; see #15503.)
+    fn expand_enum_method_body<'b>(&self,
+                               cx: &mut ExtCtxt,
+                               trait_: &TraitDef<'b>,
+                               enum_def: &'b EnumDef,
+                               type_attrs: &[ast::Attribute],
+                               type_ident: Ident,
+                               self_args: Vec<P<Expr>>,
+                               nonself_args: &[P<Expr>])
+                               -> P<Expr> {
+        self.build_enum_match_tuple(
+            cx, trait_, enum_def, type_attrs, type_ident, self_args, nonself_args)
+    }
+
+
+    /// Creates a match for a tuple of all `self_args`, where either all
+    /// variants match, or it falls into a catch-all for when one variant
+    /// does not match.
+
+    /// There are N + 1 cases because is a case for each of the N
+    /// variants where all of the variants match, and one catch-all for
+    /// when one does not match.
+
+    /// As an optimization we generate code which checks whether all variants
+    /// match first which makes llvm see that C-like enums can be compiled into
+    /// a simple equality check (for PartialEq).
+
+    /// The catch-all handler is provided access the variant index values
+    /// for each of the self-args, carried in precomputed variables.
+
+    /// ```{.text}
+    /// let __self0_vi = unsafe {
+    ///     std::intrinsics::discriminant_value(&self) } as i32;
+    /// let __self1_vi = unsafe {
+    ///     std::intrinsics::discriminant_value(&__arg1) } as i32;
+    /// let __self2_vi = unsafe {
+    ///     std::intrinsics::discriminant_value(&__arg2) } as i32;
+    ///
+    /// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... {
+    ///     match (...) {
+    ///         (Variant1, Variant1, ...) => Body1
+    ///         (Variant2, Variant2, ...) => Body2,
+    ///         ...
+    ///         _ => ::core::intrinsics::unreachable()
+    ///     }
+    /// }
+    /// else {
+    ///     ... // catch-all remainder can inspect above variant index values.
+    /// }
+    /// ```
+    fn build_enum_match_tuple<'b>(
+        &self,
+        cx: &mut ExtCtxt,
+        trait_: &TraitDef<'b>,
+        enum_def: &'b EnumDef,
+        type_attrs: &[ast::Attribute],
+        type_ident: Ident,
+        self_args: Vec<P<Expr>>,
+        nonself_args: &[P<Expr>]) -> P<Expr> {
+
+        let sp = trait_.span;
+        let variants = &enum_def.variants;
+
+        let self_arg_names = self_args.iter().enumerate()
+            .map(|(arg_count, _self_arg)| {
+                if arg_count == 0 {
+                    "__self".to_string()
+                } else {
+                    format!("__arg_{}", arg_count)
+                }
+            })
+            .collect::<Vec<String>>();
+
+        let self_arg_idents = self_arg_names.iter()
+            .map(|name|cx.ident_of(&name[..]))
+            .collect::<Vec<ast::Ident>>();
+
+        // The `vi_idents` will be bound, solely in the catch-all, to
+        // a series of let statements mapping each self_arg to an int
+        // value corresponding to its discriminant.
+        let vi_idents: Vec<ast::Ident> = self_arg_names.iter()
+            .map(|name| { let vi_suffix = format!("{}_vi", &name[..]);
+                          cx.ident_of(&vi_suffix[..]) })
+            .collect::<Vec<ast::Ident>>();
+
+        // Builds, via callback to call_substructure_method, the
+        // delegated expression that handles the catch-all case,
+        // using `__variants_tuple` to drive logic if necessary.
+        let catch_all_substructure = EnumNonMatchingCollapsed(
+            self_arg_idents, &variants[..], &vi_idents[..]);
+
+        // These arms are of the form:
+        // (Variant1, Variant1, ...) => Body1
+        // (Variant2, Variant2, ...) => Body2
+        // ...
+        // where each tuple has length = self_args.len()
+        let mut match_arms: Vec<ast::Arm> = variants.iter().enumerate()
+            .map(|(index, variant)| {
+                let mk_self_pat = |cx: &mut ExtCtxt, self_arg_name: &str| {
+                    let (p, idents) = trait_.create_enum_variant_pattern(cx, type_ident,
+                                                                         &**variant,
+                                                                         self_arg_name,
+                                                                         ast::MutImmutable);
+                    (cx.pat(sp, ast::PatRegion(p, ast::MutImmutable)), idents)
+                };
+
+                // A single arm has form (&VariantK, &VariantK, ...) => BodyK
+                // (see "Final wrinkle" note below for why.)
+                let mut subpats = Vec::with_capacity(self_arg_names.len());
+                let mut self_pats_idents = Vec::with_capacity(self_arg_names.len() - 1);
+                let first_self_pat_idents = {
+                    let (p, idents) = mk_self_pat(cx, &self_arg_names[0]);
+                    subpats.push(p);
+                    idents
+                };
+                for self_arg_name in &self_arg_names[1..] {
+                    let (p, idents) = mk_self_pat(cx, &self_arg_name[..]);
+                    subpats.push(p);
+                    self_pats_idents.push(idents);
+                }
+
+                // Here is the pat = `(&VariantK, &VariantK, ...)`
+                let single_pat = cx.pat_tuple(sp, subpats);
+
+                // For the BodyK, we need to delegate to our caller,
+                // passing it an EnumMatching to indicate which case
+                // we are in.
+
+                // All of the Self args have the same variant in these
+                // cases.  So we transpose the info in self_pats_idents
+                // to gather the getter expressions together, in the
+                // form that EnumMatching expects.
+
+                // The transposition is driven by walking across the
+                // arg fields of the variant for the first self pat.
+                let field_tuples = first_self_pat_idents.into_iter().enumerate()
+                    // For each arg field of self, pull out its getter expr ...
+                    .map(|(field_index, (sp, opt_ident, self_getter_expr, attrs))| {
+                        // ... but FieldInfo also wants getter expr
+                        // for matching other arguments of Self type;
+                        // so walk across the *other* self_pats_idents
+                        // and pull out getter for same field in each
+                        // of them (using `field_index` tracked above).
+                        // That is the heart of the transposition.
+                        let others = self_pats_idents.iter().map(|fields| {
+                            let (_, _opt_ident, ref other_getter_expr, _) =
+                                fields[field_index];
+
+                            // All Self args have same variant, so
+                            // opt_idents are the same.  (Assert
+                            // here to make it self-evident that
+                            // it is okay to ignore `_opt_ident`.)
+                            assert!(opt_ident == _opt_ident);
+
+                            other_getter_expr.clone()
+                        }).collect::<Vec<P<Expr>>>();
+
+                        FieldInfo { span: sp,
+                                    name: opt_ident,
+                                    self_: self_getter_expr,
+                                    other: others,
+                                    attrs: attrs,
+                        }
+                    }).collect::<Vec<FieldInfo>>();
+
+                // Now, for some given VariantK, we have built up
+                // expressions for referencing every field of every
+                // Self arg, assuming all are instances of VariantK.
+                // Build up code associated with such a case.
+                let substructure = EnumMatching(index,
+                                                &**variant,
+                                                field_tuples);
+                let arm_expr = self.call_substructure_method(
+                    cx, trait_, type_ident, &self_args[..], nonself_args,
+                    &substructure);
+
+                cx.arm(sp, vec![single_pat], arm_expr)
+            }).collect();
+        // We will usually need the catch-all after matching the
+        // tuples `(VariantK, VariantK, ...)` for each VariantK of the
+        // enum.  But:
+        //
+        // * when there is only one Self arg, the arms above suffice
+        // (and the deriving we call back into may not be prepared to
+        // handle EnumNonMatchCollapsed), and,
+        //
+        // * when the enum has only one variant, the single arm that
+        // is already present always suffices.
+        //
+        // * In either of the two cases above, if we *did* add a
+        //   catch-all `_` match, it would trigger the
+        //   unreachable-pattern error.
+        //
+        if variants.len() > 1 && self_args.len() > 1 {
+            // Build a series of let statements mapping each self_arg
+            // to its discriminant value. If this is a C-style enum
+            // with a specific repr type, then casts the values to
+            // that type.  Otherwise casts to `i32` (the default repr
+            // type).
+            //
+            // i.e. for `enum E<T> { A, B(1), C(T, T) }`, and a deriving
+            // with three Self args, builds three statements:
+            //
+            // ```
+            // let __self0_vi = unsafe {
+            //     std::intrinsics::discriminant_value(&self) } as i32;
+            // let __self1_vi = unsafe {
+            //     std::intrinsics::discriminant_value(&__arg1) } as i32;
+            // let __self2_vi = unsafe {
+            //     std::intrinsics::discriminant_value(&__arg2) } as i32;
+            // ```
+            let mut index_let_stmts: Vec<P<ast::Stmt>> = Vec::new();
+
+            //We also build an expression which checks whether all discriminants are equal
+            // discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ...
+            let mut discriminant_test = cx.expr_bool(sp, true);
+
+            let target_type_name =
+                find_repr_type_name(&cx.parse_sess.span_diagnostic, type_attrs);
+
+            let mut first_ident = None;
+            for (&ident, self_arg) in vi_idents.iter().zip(&self_args) {
+                let path = cx.std_path(&["intrinsics", "discriminant_value"]);
+                let call = cx.expr_call_global(
+                    sp, path, vec![cx.expr_addr_of(sp, self_arg.clone())]);
+                let variant_value = cx.expr_block(P(ast::Block {
+                    stmts: vec![],
+                    expr: Some(call),
+                    id: ast::DUMMY_NODE_ID,
+                    rules: ast::UnsafeBlock(ast::CompilerGenerated),
+                    span: sp }));
+
+                let target_ty = cx.ty_ident(sp, cx.ident_of(target_type_name));
+                let variant_disr = cx.expr_cast(sp, variant_value, target_ty);
+                let let_stmt = cx.stmt_let(sp, false, ident, variant_disr);
+                index_let_stmts.push(let_stmt);
+
+                match first_ident {
+                    Some(first) => {
+                        let first_expr = cx.expr_ident(sp, first);
+                        let id = cx.expr_ident(sp, ident);
+                        let test = cx.expr_binary(sp, ast::BiEq, first_expr, id);
+                        discriminant_test = cx.expr_binary(sp, ast::BiAnd, discriminant_test, test)
+                    }
+                    None => {
+                        first_ident = Some(ident);
+                    }
+                }
+            }
+
+            let arm_expr = self.call_substructure_method(
+                cx, trait_, type_ident, &self_args[..], nonself_args,
+                &catch_all_substructure);
+
+            //Since we know that all the arguments will match if we reach the match expression we
+            //add the unreachable intrinsics as the result of the catch all which should help llvm
+            //in optimizing it
+            let path = cx.std_path(&["intrinsics", "unreachable"]);
+            let call = cx.expr_call_global(
+                sp, path, vec![]);
+            let unreachable = cx.expr_block(P(ast::Block {
+                stmts: vec![],
+                expr: Some(call),
+                id: ast::DUMMY_NODE_ID,
+                rules: ast::UnsafeBlock(ast::CompilerGenerated),
+                span: sp }));
+            match_arms.push(cx.arm(sp, vec![cx.pat_wild(sp)], unreachable));
+
+            // Final wrinkle: the self_args are expressions that deref
+            // down to desired l-values, but we cannot actually deref
+            // them when they are fed as r-values into a tuple
+            // expression; here add a layer of borrowing, turning
+            // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
+            let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg));
+            let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
+
+            //Lastly we create an expression which branches on all discriminants being equal
+            //  if discriminant_test {
+            //      match (...) {
+            //          (Variant1, Variant1, ...) => Body1
+            //          (Variant2, Variant2, ...) => Body2,
+            //          ...
+            //          _ => ::core::intrinsics::unreachable()
+            //      }
+            //  }
+            //  else {
+            //      <delegated expression referring to __self0_vi, et al.>
+            //  }
+            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)))
+        } else if variants.is_empty() {
+            // As an additional wrinkle, For a zero-variant enum A,
+            // currently the compiler
+            // will accept `fn (a: &Self) { match   *a   { } }`
+            // but rejects `fn (a: &Self) { match (&*a,) { } }`
+            // as well as  `fn (a: &Self) { match ( *a,) { } }`
+            //
+            // This means that the strategy of building up a tuple of
+            // all Self arguments fails when Self is a zero variant
+            // enum: rustc rejects the expanded program, even though
+            // the actual code tends to be impossible to execute (at
+            // least safely), according to the type system.
+            //
+            // The most expedient fix for this is to just let the
+            // code fall through to the catch-all.  But even this is
+            // error-prone, since the catch-all as defined above would
+            // generate code like this:
+            //
+            //     _ => { let __self0 = match *self { };
+            //            let __self1 = match *__arg_0 { };
+            //            <catch-all-expr> }
+            //
+            // Which is yields bindings for variables which type
+            // inference cannot resolve to unique types.
+            //
+            // One option to the above might be to add explicit type
+            // annotations.  But the *only* reason to go down that path
+            // would be to try to make the expanded output consistent
+            // with the case when the number of enum variants >= 1.
+            //
+            // That just isn't worth it.  In fact, trying to generate
+            // sensible code for *any* deriving on a zero-variant enum
+            // does not make sense.  But at the same time, for now, we
+            // do not want to cause a compile failure just because the
+            // user happened to attach a deriving to their
+            // zero-variant enum.
+            //
+            // Instead, just generate a failing expression for the
+            // zero variant case, skipping matches and also skipping
+            // delegating back to the end user code entirely.
+            //
+            // (See also #4499 and #12609; note that some of the
+            // discussions there influence what choice we make here;
+            // e.g. if we feature-gate `match x { ... }` when x refers
+            // to an uninhabited type (e.g. a zero-variant enum or a
+            // type holding such an enum), but do not feature-gate
+            // zero-variant enums themselves, then attempting to
+            // derive Debug on such a type could here generate code
+            // that needs the feature gate enabled.)
+
+            cx.expr_unreachable(sp)
+        }
+        else {
+
+            // Final wrinkle: the self_args are expressions that deref
+            // down to desired l-values, but we cannot actually deref
+            // them when they are fed as r-values into a tuple
+            // expression; here add a layer of borrowing, turning
+            // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
+            let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg));
+            let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
+            cx.expr_match(sp, match_arg, match_arms)
+        }
+    }
+
+    fn expand_static_enum_method_body(&self,
+                                      cx: &mut ExtCtxt,
+                                      trait_: &TraitDef,
+                                      enum_def: &EnumDef,
+                                      type_ident: Ident,
+                                      self_args: &[P<Expr>],
+                                      nonself_args: &[P<Expr>])
+        -> P<Expr> {
+        let summary = enum_def.variants.iter().map(|v| {
+            let ident = v.node.name;
+            let summary = trait_.summarise_struct(cx, &v.node.data);
+            (ident, v.span, summary)
+        }).collect();
+        self.call_substructure_method(cx, trait_, type_ident,
+                                      self_args, nonself_args,
+                                      &StaticEnum(enum_def, summary))
+    }
+}
+
+#[derive(PartialEq)] // dogfooding!
+enum StructType {
+    Unknown, Record, Tuple
+}
+
+// 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);
+            match field.node.kind {
+                ast::NamedField(ident, _) => named_idents.push((ident, sp)),
+                ast::UnnamedField(..) => just_spans.push(sp),
+            }
+        }
+
+        match (just_spans.is_empty(), named_idents.is_empty()) {
+            (false, false) => cx.span_bug(self.span,
+                                          "a struct with named and unnamed \
+                                          fields in generic `derive`"),
+            // named fields
+            (_, false) => Named(named_idents),
+            // tuple structs (includes empty structs)
+            (_, _)     => Unnamed(just_spans)
+        }
+    }
+
+    fn create_subpatterns(&self,
+                          cx: &mut ExtCtxt,
+                          field_paths: Vec<ast::SpannedIdent> ,
+                          mutbl: ast::Mutability)
+                          -> Vec<P<ast::Pat>> {
+        field_paths.iter().map(|path| {
+            cx.pat(path.span,
+                        ast::PatIdent(ast::BindingMode::ByRef(mutbl), (*path).clone(), None))
+        }).collect()
+    }
+
+    fn create_struct_pattern(&self,
+                             cx: &mut ExtCtxt,
+                             struct_path: ast::Path,
+                             struct_def: &'a VariantData,
+                             prefix: &str,
+                             mutbl: ast::Mutability)
+                             -> (P<ast::Pat>, Vec<(Span, Option<Ident>,
+                                                   P<Expr>,
+                                                   &'a [ast::Attribute])>) {
+        if struct_def.fields().is_empty() {
+            return (cx.pat_enum(self.span, struct_path, vec![]), vec![]);
+        }
+
+        let mut paths = Vec::new();
+        let mut ident_expr = Vec::new();
+        let mut struct_type = Unknown;
+
+        for (i, struct_field) in struct_def.fields().iter().enumerate() {
+            let sp = self.set_expn_info(cx, struct_field.span);
+            let opt_id = match struct_field.node.kind {
+                ast::NamedField(ident, _) if (struct_type == Unknown ||
+                                              struct_type == Record) => {
+                    struct_type = Record;
+                    Some(ident)
+                }
+                ast::UnnamedField(..) if (struct_type == Unknown ||
+                                          struct_type == Tuple) => {
+                    struct_type = Tuple;
+                    None
+                }
+                _ => {
+                    cx.span_bug(sp, "a struct with named and unnamed fields in `derive`");
+                }
+            };
+            let ident = cx.ident_of(&format!("{}_{}", prefix, i));
+            paths.push(codemap::Spanned{span: sp, node: ident});
+            let val = cx.expr(
+                sp, ast::ExprParen(cx.expr_deref(sp, cx.expr_path(cx.path_ident(sp,ident)))));
+            ident_expr.push((sp, opt_id, val, &struct_field.node.attrs[..]));
+        }
+
+        let subpats = self.create_subpatterns(cx, paths, mutbl);
+
+        // struct_type is definitely not Unknown, since struct_def.fields
+        // must be nonempty to reach here
+        let pattern = if struct_type == Record {
+            let field_pats = subpats.into_iter().zip(&ident_expr)
+                                    .map(|(pat, &(_, id, _, _))| {
+                // id is guaranteed to be Some
+                codemap::Spanned {
+                    span: pat.span,
+                    node: ast::FieldPat { ident: id.unwrap(), pat: pat, is_shorthand: false },
+                }
+            }).collect();
+            cx.pat_struct(self.span, struct_path, field_pats)
+        } else {
+            cx.pat_enum(self.span, struct_path, subpats)
+        };
+
+        (pattern, ident_expr)
+    }
+
+    fn create_enum_variant_pattern(&self,
+                                   cx: &mut ExtCtxt,
+                                   enum_ident: ast::Ident,
+                                   variant: &'a ast::Variant,
+                                   prefix: &str,
+                                   mutbl: ast::Mutability)
+        -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) {
+        let variant_ident = variant.node.name;
+        let variant_path = cx.path(variant.span, vec![enum_ident, variant_ident]);
+        self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl)
+    }
+}
+
+/* helpful premade recipes */
+
+/// Fold the fields. `use_foldl` controls whether this is done
+/// left-to-right (`true`) or right-to-left (`false`).
+pub fn cs_fold<F>(use_foldl: bool,
+                  mut f: F,
+                  base: P<Expr>,
+                  mut enum_nonmatch_f: EnumNonMatchCollapsedFunc,
+                  cx: &mut ExtCtxt,
+                  trait_span: Span,
+                  substructure: &Substructure)
+                  -> P<Expr> where
+    F: FnMut(&mut ExtCtxt, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>,
+{
+    match *substructure.fields {
+        EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
+            if use_foldl {
+                all_fields.iter().fold(base, |old, field| {
+                    f(cx,
+                      field.span,
+                      old,
+                      field.self_.clone(),
+                      &field.other)
+                })
+            } else {
+                all_fields.iter().rev().fold(base, |old, field| {
+                    f(cx,
+                      field.span,
+                      old,
+                      field.self_.clone(),
+                      &field.other)
+                })
+            }
+        },
+        EnumNonMatchingCollapsed(ref all_args, _, tuple) =>
+            enum_nonmatch_f(cx, trait_span, (&all_args[..], tuple),
+                            substructure.nonself_args),
+        StaticEnum(..) | StaticStruct(..) => {
+            cx.span_bug(trait_span, "static function in `derive`")
+        }
+    }
+}
+
+
+/// Call the method that is being derived on all the fields, and then
+/// process the collected results. i.e.
+///
+/// ```ignore
+/// f(cx, span, vec![self_1.method(__arg_1_1, __arg_2_1),
+///                  self_2.method(__arg_1_2, __arg_2_2)])
+/// ```
+#[inline]
+pub fn cs_same_method<F>(f: F,
+                         mut enum_nonmatch_f: EnumNonMatchCollapsedFunc,
+                         cx: &mut ExtCtxt,
+                         trait_span: Span,
+                         substructure: &Substructure)
+                         -> P<Expr> where
+    F: FnOnce(&mut ExtCtxt, Span, Vec<P<Expr>>) -> P<Expr>,
+{
+    match *substructure.fields {
+        EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
+            // call self_n.method(other_1_n, other_2_n, ...)
+            let called = all_fields.iter().map(|field| {
+                cx.expr_method_call(field.span,
+                                    field.self_.clone(),
+                                    substructure.method_ident,
+                                    field.other.iter()
+                                               .map(|e| cx.expr_addr_of(field.span, e.clone()))
+                                               .collect())
+            }).collect();
+
+            f(cx, trait_span, called)
+        },
+        EnumNonMatchingCollapsed(ref all_self_args, _, tuple) =>
+            enum_nonmatch_f(cx, trait_span, (&all_self_args[..], tuple),
+                            substructure.nonself_args),
+        StaticEnum(..) | StaticStruct(..) => {
+            cx.span_bug(trait_span, "static function in `derive`")
+        }
+    }
+}
diff --git a/src/libsyntax_ext/deriving/generic/ty.rs b/src/libsyntax_ext/deriving/generic/ty.rs
new file mode 100644 (file)
index 0000000..10564b5
--- /dev/null
@@ -0,0 +1,283 @@
+// 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.
+
+//! A mini version of ast::Ty, which is easier to use, and features an explicit `Self` type to use
+//! when specifying impls to be derived.
+
+pub use self::PtrTy::*;
+pub use self::Ty::*;
+
+use syntax::ast;
+use syntax::ast::{Expr,Generics,Ident};
+use syntax::ext::base::ExtCtxt;
+use syntax::ext::build::AstBuilder;
+use syntax::codemap::{Span,respan};
+use syntax::parse::token::special_idents;
+use syntax::ptr::P;
+
+/// The types of pointers
+#[derive(Clone, Eq, PartialEq)]
+#[allow(dead_code)]
+pub enum PtrTy<'a> {
+    /// &'lifetime mut
+    Borrowed(Option<&'a str>, ast::Mutability),
+    /// *mut
+    Raw(ast::Mutability),
+}
+
+/// A path, e.g. `::std::option::Option::<i32>` (global). Has support
+/// for type parameters and a lifetime.
+#[derive(Clone, Eq, PartialEq)]
+pub struct Path<'a> {
+    pub path: Vec<&'a str> ,
+    pub lifetime: Option<&'a str>,
+    pub params: Vec<Box<Ty<'a>>>,
+    pub global: bool,
+}
+
+impl<'a> Path<'a> {
+    pub fn new<'r>(path: Vec<&'r str> ) -> Path<'r> {
+        Path::new_(path, None, Vec::new(), true)
+    }
+    pub fn new_local<'r>(path: &'r str) -> Path<'r> {
+        Path::new_(vec!( path ), None, Vec::new(), false)
+    }
+    pub fn new_<'r>(path: Vec<&'r str> ,
+                    lifetime: Option<&'r str>,
+                    params: Vec<Box<Ty<'r>>>,
+                    global: bool)
+                    -> Path<'r> {
+        Path {
+            path: path,
+            lifetime: lifetime,
+            params: params,
+            global: global
+        }
+    }
+
+    pub fn to_ty(&self,
+                 cx: &ExtCtxt,
+                 span: Span,
+                 self_ty: Ident,
+                 self_generics: &Generics)
+                 -> P<ast::Ty> {
+        cx.ty_path(self.to_path(cx, span, self_ty, self_generics))
+    }
+    pub fn to_path(&self,
+                   cx: &ExtCtxt,
+                   span: Span,
+                   self_ty: Ident,
+                   self_generics: &Generics)
+                   -> ast::Path {
+        let idents = self.path.iter().map(|s| cx.ident_of(*s)).collect();
+        let lt = mk_lifetimes(cx, span, &self.lifetime);
+        let tys = self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics)).collect();
+
+        cx.path_all(span, self.global, idents, lt, tys, Vec::new())
+    }
+}
+
+/// A type. Supports pointers, Self, and literals
+#[derive(Clone, Eq, PartialEq)]
+pub enum Ty<'a> {
+    Self_,
+    /// &/Box/ Ty
+    Ptr(Box<Ty<'a>>, PtrTy<'a>),
+    /// mod::mod::Type<[lifetime], [Params...]>, including a plain type
+    /// parameter, and things like `i32`
+    Literal(Path<'a>),
+    /// includes unit
+    Tuple(Vec<Ty<'a>> )
+}
+
+pub fn borrowed_ptrty<'r>() -> PtrTy<'r> {
+    Borrowed(None, ast::MutImmutable)
+}
+pub fn borrowed<'r>(ty: Box<Ty<'r>>) -> Ty<'r> {
+    Ptr(ty, borrowed_ptrty())
+}
+
+pub fn borrowed_explicit_self<'r>() -> Option<Option<PtrTy<'r>>> {
+    Some(Some(borrowed_ptrty()))
+}
+
+pub fn borrowed_self<'r>() -> Ty<'r> {
+    borrowed(Box::new(Self_))
+}
+
+pub fn nil_ty<'r>() -> Ty<'r> {
+    Tuple(Vec::new())
+}
+
+fn mk_lifetime(cx: &ExtCtxt, span: Span, lt: &Option<&str>) -> Option<ast::Lifetime> {
+    match *lt {
+        Some(ref s) => Some(cx.lifetime(span, cx.ident_of(*s).name)),
+        None => None
+    }
+}
+
+fn mk_lifetimes(cx: &ExtCtxt, span: Span, lt: &Option<&str>) -> Vec<ast::Lifetime> {
+    match *lt {
+        Some(ref s) => vec!(cx.lifetime(span, cx.ident_of(*s).name)),
+        None => vec!()
+    }
+}
+
+impl<'a> Ty<'a> {
+    pub fn to_ty(&self,
+                 cx: &ExtCtxt,
+                 span: Span,
+                 self_ty: Ident,
+                 self_generics: &Generics)
+                 -> P<ast::Ty> {
+        match *self {
+            Ptr(ref ty, ref ptr) => {
+                let raw_ty = ty.to_ty(cx, span, self_ty, self_generics);
+                match *ptr {
+                    Borrowed(ref lt, mutbl) => {
+                        let lt = mk_lifetime(cx, span, lt);
+                        cx.ty_rptr(span, raw_ty, lt, mutbl)
+                    }
+                    Raw(mutbl) => cx.ty_ptr(span, raw_ty, mutbl)
+                }
+            }
+            Literal(ref p) => { p.to_ty(cx, span, self_ty, self_generics) }
+            Self_  => {
+                cx.ty_path(self.to_path(cx, span, self_ty, self_generics))
+            }
+            Tuple(ref fields) => {
+                let ty = ast::TyTup(fields.iter()
+                    .map(|f| f.to_ty(cx, span, self_ty, self_generics))
+                    .collect());
+                cx.ty(span, ty)
+            }
+        }
+    }
+
+    pub fn to_path(&self,
+                   cx: &ExtCtxt,
+                   span: Span,
+                   self_ty: Ident,
+                   self_generics: &Generics)
+                   -> ast::Path {
+        match *self {
+            Self_ => {
+                let self_params = self_generics.ty_params.map(|ty_param| {
+                    cx.ty_ident(span, ty_param.ident)
+                });
+                let lifetimes = self_generics.lifetimes.iter()
+                                                       .map(|d| d.lifetime)
+                                                       .collect();
+
+                cx.path_all(span, false, vec!(self_ty), lifetimes,
+                            self_params.into_vec(), Vec::new())
+            }
+            Literal(ref p) => {
+                p.to_path(cx, span, self_ty, self_generics)
+            }
+            Ptr(..) => { cx.span_bug(span, "pointer in a path in generic `derive`") }
+            Tuple(..) => { cx.span_bug(span, "tuple in a path in generic `derive`") }
+        }
+    }
+}
+
+
+fn mk_ty_param(cx: &ExtCtxt,
+               span: Span,
+               name: &str,
+               bounds: &[Path],
+               self_ident: Ident,
+               self_generics: &Generics)
+               -> ast::TyParam {
+    let bounds =
+        bounds.iter().map(|b| {
+            let path = b.to_path(cx, span, self_ident, self_generics);
+            cx.typarambound(path)
+        }).collect();
+    cx.typaram(span, cx.ident_of(name), bounds, None)
+}
+
+fn mk_generics(lifetimes: Vec<ast::LifetimeDef>, ty_params: Vec<ast::TyParam>)
+               -> Generics {
+    Generics {
+        lifetimes: lifetimes,
+        ty_params: P::from_vec(ty_params),
+        where_clause: ast::WhereClause {
+            id: ast::DUMMY_NODE_ID,
+            predicates: Vec::new(),
+        },
+    }
+}
+
+/// Lifetimes and bounds on type parameters
+#[derive(Clone)]
+pub struct LifetimeBounds<'a> {
+    pub lifetimes: Vec<(&'a str, Vec<&'a str>)>,
+    pub bounds: Vec<(&'a str, Vec<Path<'a>>)>,
+}
+
+impl<'a> LifetimeBounds<'a> {
+    pub fn empty() -> LifetimeBounds<'a> {
+        LifetimeBounds {
+            lifetimes: Vec::new(), bounds: Vec::new()
+        }
+    }
+    pub fn to_generics(&self,
+                       cx: &ExtCtxt,
+                       span: Span,
+                       self_ty: Ident,
+                       self_generics: &Generics)
+                       -> Generics {
+        let lifetimes = self.lifetimes.iter().map(|&(ref lt, ref bounds)| {
+            let bounds =
+                bounds.iter().map(
+                    |b| cx.lifetime(span, cx.ident_of(*b).name)).collect();
+            cx.lifetime_def(span, cx.ident_of(*lt).name, bounds)
+        }).collect();
+        let ty_params = self.bounds.iter().map(|t| {
+            match *t {
+                (ref name, ref bounds) => {
+                    mk_ty_param(cx,
+                                span,
+                                *name,
+                                bounds,
+                                self_ty,
+                                self_generics)
+                }
+            }
+        }).collect();
+        mk_generics(lifetimes, ty_params)
+    }
+}
+
+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.
+    let self_path = cx.expr_self(span);
+    match *self_ptr {
+        None => {
+            (self_path, respan(span, ast::SelfValue(special_idents::self_)))
+        }
+        Some(ref ptr) => {
+            let self_ty = respan(
+                span,
+                match *ptr {
+                    Borrowed(ref lt, mutbl) => {
+                        let lt = lt.map(|s| cx.lifetime(span, cx.ident_of(s).name));
+                        ast::SelfRegion(lt, mutbl, special_idents::self_)
+                    }
+                    Raw(_) => cx.span_bug(span, "attempted to use *self in deriving definition")
+                });
+            let self_expr = cx.expr_deref(span, self_path);
+            (self_expr, self_ty)
+        }
+    }
+}
diff --git a/src/libsyntax_ext/deriving/hash.rs b/src/libsyntax_ext/deriving/hash.rs
new file mode 100644 (file)
index 0000000..6bd21f7
--- /dev/null
@@ -0,0 +1,100 @@
+// 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 deriving::generic::*;
+use deriving::generic::ty::*;
+
+use syntax::ast::{MetaItem, Expr, MutMutable};
+use syntax::codemap::Span;
+use syntax::ext::base::{ExtCtxt, Annotatable};
+use syntax::ext::build::AstBuilder;
+use syntax::ptr::P;
+
+pub fn expand_deriving_hash(cx: &mut ExtCtxt,
+                            span: Span,
+                            mitem: &MetaItem,
+                            item: &Annotatable,
+                            push: &mut FnMut(Annotatable))
+{
+
+    let path = Path::new_(pathvec_std!(cx, core::hash::Hash), None,
+                          vec!(), true);
+    let arg = Path::new_local("__H");
+    let hash_trait_def = TraitDef {
+        span: span,
+        attributes: Vec::new(),
+        path: path,
+        additional_bounds: Vec::new(),
+        generics: LifetimeBounds::empty(),
+        is_unsafe: false,
+        methods: vec!(
+            MethodDef {
+                name: "hash",
+                generics: LifetimeBounds {
+                    lifetimes: Vec::new(),
+                    bounds: vec![("__H",
+                                  vec![path_std!(cx, core::hash::Hasher)])],
+                },
+                explicit_self: borrowed_explicit_self(),
+                args: vec!(Ptr(Box::new(Literal(arg)), Borrowed(None, MutMutable))),
+                ret_ty: nil_ty(),
+                attributes: vec![],
+                is_unsafe: false,
+                combine_substructure: combine_substructure(Box::new(|a, b, c| {
+                    hash_substructure(a, b, c)
+                }))
+            }
+        ),
+        associated_types: Vec::new(),
+    };
+
+    hash_trait_def.expand(cx, mitem, item, push);
+}
+
+fn hash_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
+    let state_expr = match (substr.nonself_args.len(), substr.nonself_args.get(0)) {
+        (1, Some(o_f)) => o_f,
+        _ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`")
+    };
+    let call_hash = |span, thing_expr| {
+        let hash_path = {
+            let strs = cx.std_path(&["hash", "Hash", "hash"]);
+
+            cx.expr_path(cx.path_global(span, strs))
+        };
+        let ref_thing = cx.expr_addr_of(span, thing_expr);
+        let expr = cx.expr_call(span, hash_path, vec!(ref_thing, state_expr.clone()));
+        cx.stmt_expr(expr)
+    };
+    let mut stmts = Vec::new();
+
+    let fields = match *substr.fields {
+        Struct(ref fs) => fs,
+        EnumMatching(index, variant, ref fs) => {
+            // Determine the discriminant. We will feed this value to the byte
+            // iteration function.
+            let discriminant = match variant.node.disr_expr {
+                Some(ref d) => d.clone(),
+                None => cx.expr_usize(trait_span, index)
+            };
+
+            stmts.push(call_hash(trait_span, discriminant));
+
+            fs
+        }
+        _ => cx.span_bug(trait_span, "impossible substructure in `derive(Hash)`")
+    };
+
+    for &FieldInfo { ref self_, span, .. } in fields {
+        stmts.push(call_hash(span, self_.clone()));
+    }
+
+    cx.expr_block(cx.block(trait_span, stmts, None))
+}
diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs
new file mode 100644 (file)
index 0000000..9195407
--- /dev/null
@@ -0,0 +1,202 @@
+// 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.
+
+//! The compiler code necessary to implement the `#[derive]` extensions.
+//!
+//! FIXME (#2810): hygiene. Search for "__" strings (in other files too). We also assume "extra" is
+//! the standard library, and "std" is the core library.
+
+use syntax::ast::{MetaItem, MetaWord};
+use syntax::attr::AttrMetaMethods;
+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::parse::token::{intern, intern_and_get_ident};
+
+macro_rules! pathvec {
+    ($($x:ident)::+) => (
+        vec![ $( stringify!($x) ),+ ]
+    )
+}
+
+macro_rules! path {
+    ($($x:tt)*) => (
+        ::ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) )
+    )
+}
+
+macro_rules! path_local {
+    ($x:ident) => (
+        ::deriving::generic::ty::Path::new_local(stringify!($x))
+    )
+}
+
+macro_rules! pathvec_std {
+    ($cx:expr, $first:ident :: $($rest:ident)::+) => ({
+        let mut v = pathvec!($($rest)::+);
+        if let Some(s) = $cx.crate_root {
+            v.insert(0, s);
+        }
+        v
+    })
+}
+
+macro_rules! path_std {
+    ($($x:tt)*) => (
+        ::deriving::generic::ty::Path::new( pathvec_std!( $($x)* ) )
+    )
+}
+
+pub mod bounds;
+pub mod clone;
+pub mod encodable;
+pub mod decodable;
+pub mod hash;
+pub mod debug;
+pub mod default;
+pub mod primitive;
+
+#[path="cmp/partial_eq.rs"]
+pub mod partial_eq;
+#[path="cmp/eq.rs"]
+pub mod eq;
+#[path="cmp/partial_ord.rs"]
+pub mod partial_ord;
+#[path="cmp/ord.rs"]
+pub mod ord;
+
+
+pub mod generic;
+
+fn expand_derive(cx: &mut ExtCtxt,
+                 span: Span,
+                 mitem: &MetaItem,
+                 annotatable: Annotatable)
+                 -> Annotatable {
+    annotatable.map_item_or(|item| {
+        item.map(|mut item| {
+            if mitem.value_str().is_some() {
+                cx.span_err(mitem.span, "unexpected value in `derive`");
+            }
+
+            let traits = mitem.meta_item_list().unwrap_or(&[]);
+            if traits.is_empty() {
+                cx.span_warn(mitem.span, "empty trait list in `derive`");
+            }
+
+            for titem in traits.iter().rev() {
+                let tname = match titem.node {
+                    MetaWord(ref tname) => tname,
+                    _ => {
+                        cx.span_err(titem.span, "malformed `derive` entry");
+                        continue;
+                    }
+                };
+
+                if !(is_builtin_trait(tname) || cx.ecfg.enable_custom_derive()) {
+                    feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
+                                                   "custom_derive",
+                                                   titem.span,
+                                                   feature_gate::GateIssue::Language,
+                                                   feature_gate::EXPLAIN_CUSTOM_DERIVE);
+                    continue;
+                }
+
+                // #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar]
+                item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span,
+                    intern_and_get_ident(&format!("derive_{}", tname)))));
+            }
+
+            item
+        })
+    }, |a| {
+        cx.span_err(span, "`derive` can only be applied to items");
+        a
+    })
+}
+
+macro_rules! derive_traits {
+    ($( $name:expr => $func:path, )+) => {
+        pub fn register_all(env: &mut SyntaxEnv) {
+            // Define the #[derive_*] extensions.
+            $({
+                struct DeriveExtension;
+
+                impl MultiItemDecorator for DeriveExtension {
+                    fn expand(&self,
+                              ecx: &mut ExtCtxt,
+                              sp: Span,
+                              mitem: &MetaItem,
+                              annotatable: &Annotatable,
+                              push: &mut FnMut(Annotatable)) {
+                        warn_if_deprecated(ecx, sp, $name);
+                        $func(ecx, sp, mitem, annotatable, push);
+                    }
+                }
+
+                env.insert(intern(concat!("derive_", $name)),
+                           MultiDecorator(Box::new(DeriveExtension)));
+            })+
+
+            env.insert(intern("derive"),
+                       MultiModifier(Box::new(expand_derive)));
+        }
+
+        fn is_builtin_trait(name: &str) -> bool {
+            match name {
+                $( $name )|+ => true,
+                _ => false,
+            }
+        }
+    }
+}
+
+derive_traits! {
+    "Clone" => clone::expand_deriving_clone,
+
+    "Hash" => hash::expand_deriving_hash,
+
+    "RustcEncodable" => encodable::expand_deriving_rustc_encodable,
+
+    "RustcDecodable" => decodable::expand_deriving_rustc_decodable,
+
+    "PartialEq" => partial_eq::expand_deriving_partial_eq,
+    "Eq" => eq::expand_deriving_eq,
+    "PartialOrd" => partial_ord::expand_deriving_partial_ord,
+    "Ord" => ord::expand_deriving_ord,
+
+    "Debug" => debug::expand_deriving_debug,
+
+    "Default" => default::expand_deriving_default,
+
+    "FromPrimitive" => primitive::expand_deriving_from_primitive,
+
+    "Send" => bounds::expand_deriving_unsafe_bound,
+    "Sync" => bounds::expand_deriving_unsafe_bound,
+    "Copy" => bounds::expand_deriving_copy,
+
+    // deprecated
+    "Encodable" => encodable::expand_deriving_encodable,
+    "Decodable" => decodable::expand_deriving_decodable,
+}
+
+#[inline] // because `name` is a compile-time constant
+fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) {
+    if let Some(replacement) = match name {
+        "Encodable" => Some("RustcEncodable"),
+        "Decodable" => Some("RustcDecodable"),
+        _ => None,
+    } {
+        ecx.span_warn(sp, &format!("derive({}) is deprecated in favor of derive({})",
+                                   name, replacement));
+    }
+}
diff --git a/src/libsyntax_ext/deriving/primitive.rs b/src/libsyntax_ext/deriving/primitive.rs
new file mode 100644 (file)
index 0000000..121fe01
--- /dev/null
@@ -0,0 +1,142 @@
+// 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.
+
+use deriving::generic::*;
+use deriving::generic::ty::*;
+
+use syntax::ast::{MetaItem, Expr};
+use syntax::ast;
+use syntax::codemap::Span;
+use syntax::ext::base::{ExtCtxt, Annotatable};
+use syntax::ext::build::AstBuilder;
+use syntax::parse::token::InternedString;
+use syntax::ptr::P;
+
+pub fn expand_deriving_from_primitive(cx: &mut ExtCtxt,
+                                      span: Span,
+                                      mitem: &MetaItem,
+                                      item: &Annotatable,
+                                      push: &mut FnMut(Annotatable))
+{
+    let inline = cx.meta_word(span, InternedString::new("inline"));
+    let attrs = vec!(cx.attribute(span, inline));
+    let trait_def = TraitDef {
+        span: span,
+        attributes: Vec::new(),
+        path: path_std!(cx, core::num::FromPrimitive),
+        additional_bounds: Vec::new(),
+        generics: LifetimeBounds::empty(),
+        is_unsafe: false,
+        methods: vec!(
+            MethodDef {
+                name: "from_i64",
+                generics: LifetimeBounds::empty(),
+                explicit_self: None,
+                args: vec!(Literal(path_local!(i64))),
+                ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option),
+                                           None,
+                                           vec!(Box::new(Self_)),
+                                           true)),
+                // #[inline] liable to cause code-bloat
+                attributes: attrs.clone(),
+                is_unsafe: false,
+                combine_substructure: combine_substructure(Box::new(|c, s, sub| {
+                    cs_from("i64", c, s, sub)
+                })),
+            },
+            MethodDef {
+                name: "from_u64",
+                generics: LifetimeBounds::empty(),
+                explicit_self: None,
+                args: vec!(Literal(path_local!(u64))),
+                ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option),
+                                           None,
+                                           vec!(Box::new(Self_)),
+                                           true)),
+                // #[inline] liable to cause code-bloat
+                attributes: attrs,
+                is_unsafe: false,
+                combine_substructure: combine_substructure(Box::new(|c, s, sub| {
+                    cs_from("u64", c, s, sub)
+                })),
+            }
+        ),
+        associated_types: Vec::new(),
+    };
+
+    trait_def.expand(cx, mitem, item, push)
+}
+
+fn cs_from(name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
+    let n = match (substr.nonself_args.len(), substr.nonself_args.get(0)) {
+        (1, Some(o_f)) => o_f,
+        _ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(FromPrimitive)`")
+    };
+
+    match *substr.fields {
+        StaticStruct(..) => {
+            cx.span_err(trait_span, "`FromPrimitive` cannot be derived for structs");
+            return cx.expr_fail(trait_span, InternedString::new(""));
+        }
+        StaticEnum(enum_def, _) => {
+            if enum_def.variants.is_empty() {
+                cx.span_err(trait_span,
+                            "`FromPrimitive` cannot be derived for enums with no variants");
+                return cx.expr_fail(trait_span, InternedString::new(""));
+            }
+
+            let mut arms = Vec::new();
+
+            for variant in &enum_def.variants {
+                let def = &variant.node.data;
+                if !def.is_unit() {
+                    cx.span_err(trait_span, "`FromPrimitive` cannot be derived \
+                                             for enums with non-unit variants");
+                    return cx.expr_fail(trait_span,
+                                        InternedString::new(""));
+                }
+
+                let span = variant.span;
+
+                // expr for `$n == $variant as $name`
+                let path = cx.path(span, vec![substr.type_ident, variant.node.name]);
+                let variant = cx.expr_path(path);
+                let ty = cx.ty_ident(span, cx.ident_of(name));
+                let cast = cx.expr_cast(span, variant.clone(), ty);
+                let guard = cx.expr_binary(span, ast::BiEq, n.clone(), cast);
+
+                // expr for `Some($variant)`
+                let body = cx.expr_some(span, variant);
+
+                // arm for `_ if $guard => $body`
+                let arm = ast::Arm {
+                    attrs: vec!(),
+                    pats: vec!(cx.pat_wild(span)),
+                    guard: Some(guard),
+                    body: body,
+                };
+
+                arms.push(arm);
+            }
+
+            // arm for `_ => None`
+            let arm = ast::Arm {
+                attrs: vec!(),
+                pats: vec!(cx.pat_wild(trait_span)),
+                guard: None,
+                body: cx.expr_none(trait_span),
+            };
+            arms.push(arm);
+
+            cx.expr_match(trait_span, n.clone(), arms)
+        }
+        _ => cx.span_bug(trait_span, "expected StaticEnum in derive(FromPrimitive)")
+    }
+}
diff --git a/src/libsyntax_ext/env.rs b/src/libsyntax_ext/env.rs
new file mode 100644 (file)
index 0000000..f1dd685
--- /dev/null
@@ -0,0 +1,106 @@
+// 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 compiler code necessary to support the env! extension.  Eventually this
+ * should all get sucked into either the compiler syntax extension plugin
+ * interface.
+ */
+
+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 std::env;
+
+pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+                              -> Box<base::MacResult+'cx> {
+    let var = match get_single_str_from_tts(cx, sp, tts, "option_env!") {
+        None => return DummyResult::expr(sp),
+        Some(v) => v
+    };
+
+    let e = match env::var(&var[..]) {
+      Err(..) => {
+          cx.expr_path(cx.path_all(sp,
+                                   true,
+                                   cx.std_path(&["option", "Option", "None"]),
+                                   Vec::new(),
+                                   vec!(cx.ty_rptr(sp,
+                                                   cx.ty_ident(sp,
+                                                        cx.ident_of("str")),
+                                                   Some(cx.lifetime(sp,
+                                                        cx.ident_of(
+                                                            "'static").name)),
+                                                   ast::MutImmutable)),
+                                   Vec::new()))
+      }
+      Ok(s) => {
+          cx.expr_call_global(sp,
+                              cx.std_path(&["option", "Option", "Some"]),
+                              vec!(cx.expr_str(sp,
+                                               token::intern_and_get_ident(
+                                          &s[..]))))
+      }
+    };
+    MacEager::expr(e)
+}
+
+pub fn expand_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+                       -> Box<base::MacResult+'cx> {
+    let mut exprs = match get_exprs_from_tts(cx, sp, tts) {
+        Some(ref exprs) if exprs.is_empty() => {
+            cx.span_err(sp, "env! takes 1 or 2 arguments");
+            return DummyResult::expr(sp);
+        }
+        None => return DummyResult::expr(sp),
+        Some(exprs) => exprs.into_iter()
+    };
+
+    let var = match expr_to_string(cx,
+                                exprs.next().unwrap(),
+                                "expected string literal") {
+        None => return DummyResult::expr(sp),
+        Some((v, _style)) => v
+    };
+    let msg = match exprs.next() {
+        None => {
+            token::intern_and_get_ident(&format!("environment variable `{}` \
+                                                 not defined",
+                                                var))
+        }
+        Some(second) => {
+            match expr_to_string(cx, second, "expected string literal") {
+                None => return DummyResult::expr(sp),
+                Some((s, _style)) => s
+            }
+        }
+    };
+
+    match exprs.next() {
+        None => {}
+        Some(_) => {
+            cx.span_err(sp, "env! takes 1 or 2 arguments");
+            return DummyResult::expr(sp);
+        }
+    }
+
+    let e = match env::var(&var[..]) {
+        Err(_) => {
+            cx.span_err(sp, &msg);
+            cx.expr_usize(sp, 0)
+        }
+        Ok(s) => cx.expr_str(sp, token::intern_and_get_ident(&s))
+    };
+    MacEager::expr(e)
+}
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs
new file mode 100644 (file)
index 0000000..1fb2b55
--- /dev/null
@@ -0,0 +1,717 @@
+// 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.
+
+use self::ArgumentType::*;
+use self::Position::*;
+
+use fmt_macros as parse;
+
+use syntax::ast;
+use syntax::codemap::{Span, respan};
+use syntax::ext::base::*;
+use syntax::ext::base;
+use syntax::ext::build::AstBuilder;
+use syntax::fold::Folder;
+use syntax::parse::token::special_idents;
+use syntax::parse::token;
+use syntax::ptr::P;
+
+use std::collections::HashMap;
+
+#[derive(PartialEq)]
+enum ArgumentType {
+    Known(String),
+    Unsigned
+}
+
+enum Position {
+    Exact(usize),
+    Named(String),
+}
+
+struct Context<'a, 'b:'a> {
+    ecx: &'a mut ExtCtxt<'b>,
+    /// The macro's call site. References to unstable formatting internals must
+    /// use this span to pass the stability checker.
+    macsp: Span,
+    /// The span of the format string literal.
+    fmtsp: Span,
+
+    /// Parsed argument expressions and the types that we've found so far for
+    /// them.
+    args: Vec<P<ast::Expr>>,
+    arg_types: Vec<Option<ArgumentType>>,
+    /// Parsed named expressions and the types that we've found for them so far.
+    /// Note that we keep a side-array of the ordering of the named arguments
+    /// found to be sure that we can translate them in the same order that they
+    /// were declared in.
+    names: HashMap<String, P<ast::Expr>>,
+    name_types: HashMap<String, ArgumentType>,
+    name_ordering: Vec<String>,
+
+    /// The latest consecutive literal strings, or empty if there weren't any.
+    literal: String,
+
+    /// Collection of the compiled `rt::Argument` structures
+    pieces: Vec<P<ast::Expr>>,
+    /// Collection of string literals
+    str_pieces: Vec<P<ast::Expr>>,
+    /// Stays `true` if all formatting parameters are default (as in "{}{}").
+    all_pieces_simple: bool,
+
+    name_positions: HashMap<String, usize>,
+
+    /// Updated as arguments are consumed or methods are entered
+    nest_level: usize,
+    next_arg: usize,
+}
+
+/// Parses the arguments from the given list of tokens, returning None
+/// if there's a parse error so we can continue parsing other format!
+/// expressions.
+///
+/// If parsing succeeds, the return value is:
+/// ```ignore
+/// Some((fmtstr, unnamed arguments, ordering of named arguments,
+///       named arguments))
+/// ```
+fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+              -> Option<(P<ast::Expr>, Vec<P<ast::Expr>>, Vec<String>,
+                         HashMap<String, P<ast::Expr>>)> {
+    let mut args = Vec::new();
+    let mut names = HashMap::<String, P<ast::Expr>>::new();
+    let mut order = Vec::new();
+
+    let mut p = ecx.new_parser_from_tts(tts);
+
+    if p.token == token::Eof {
+        ecx.span_err(sp, "requires at least a format string argument");
+        return None;
+    }
+    let fmtstr = panictry!(p.parse_expr());
+    let mut named = false;
+    while p.token != token::Eof {
+        if !p.eat(&token::Comma) {
+            ecx.span_err(sp, "expected token: `,`");
+            return None;
+        }
+        if p.token == token::Eof { break } // accept trailing commas
+        if named || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) {
+            named = true;
+            let ident = match p.token {
+                token::Ident(i, _) => {
+                    p.bump();
+                    i
+                }
+                _ if named => {
+                    ecx.span_err(p.span,
+                                 "expected ident, positional arguments \
+                                 cannot follow named arguments");
+                    return None;
+                }
+                _ => {
+                    ecx.span_err(p.span,
+                                 &format!("expected ident for named argument, found `{}`",
+                                         p.this_token_to_string()));
+                    return None;
+                }
+            };
+            let name: &str = &ident.name.as_str();
+
+            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
+                }
+            }
+            order.push(name.to_string());
+            names.insert(name.to_string(), e);
+        } else {
+            args.push(panictry!(p.parse_expr()));
+        }
+    }
+    Some((fmtstr, args, order, names))
+}
+
+impl<'a, 'b> Context<'a, 'b> {
+    /// Verifies one piece of a parse string. All errors are not emitted as
+    /// fatal so we can continue giving errors about this and possibly other
+    /// format strings.
+    fn verify_piece(&mut self, p: &parse::Piece) {
+        match *p {
+            parse::String(..) => {}
+            parse::NextArgument(ref arg) => {
+                // width/precision first, if they have implicit positional
+                // parameters it makes more sense to consume them first.
+                self.verify_count(arg.format.width);
+                self.verify_count(arg.format.precision);
+
+                // argument second, if it's an implicit positional parameter
+                // it's written second, so it should come after width/precision.
+                let pos = match arg.position {
+                    parse::ArgumentNext => {
+                        let i = self.next_arg;
+                        if self.check_positional_ok() {
+                            self.next_arg += 1;
+                        }
+                        Exact(i)
+                    }
+                    parse::ArgumentIs(i) => Exact(i),
+                    parse::ArgumentNamed(s) => Named(s.to_string()),
+                };
+
+                let ty = Known(arg.format.ty.to_string());
+                self.verify_arg_type(pos, ty);
+            }
+        }
+    }
+
+    fn verify_count(&mut self, c: parse::Count) {
+        match c {
+            parse::CountImplied | parse::CountIs(..) => {}
+            parse::CountIsParam(i) => {
+                self.verify_arg_type(Exact(i), Unsigned);
+            }
+            parse::CountIsName(s) => {
+                self.verify_arg_type(Named(s.to_string()), Unsigned);
+            }
+            parse::CountIsNextParam => {
+                if self.check_positional_ok() {
+                    let next_arg = self.next_arg;
+                    self.verify_arg_type(Exact(next_arg), Unsigned);
+                    self.next_arg += 1;
+                }
+            }
+        }
+    }
+
+    fn check_positional_ok(&mut self) -> bool {
+        if self.nest_level != 0 {
+            self.ecx.span_err(self.fmtsp, "cannot use implicit positional \
+                                           arguments nested inside methods");
+            false
+        } else {
+            true
+        }
+    }
+
+    fn describe_num_args(&self) -> String {
+        match self.args.len() {
+            0 => "no arguments given".to_string(),
+            1 => "there is 1 argument".to_string(),
+            x => format!("there are {} arguments", x),
+        }
+    }
+
+    fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
+        match arg {
+            Exact(arg) => {
+                if self.args.len() <= arg {
+                    let msg = format!("invalid reference to argument `{}` ({})",
+                                      arg, self.describe_num_args());
+
+                    self.ecx.span_err(self.fmtsp, &msg[..]);
+                    return;
+                }
+                {
+                    let arg_type = match self.arg_types[arg] {
+                        None => None,
+                        Some(ref x) => Some(x)
+                    };
+                    self.verify_same(self.args[arg].span, &ty, arg_type);
+                }
+                if self.arg_types[arg].is_none() {
+                    self.arg_types[arg] = Some(ty);
+                }
+            }
+
+            Named(name) => {
+                let span = match self.names.get(&name) {
+                    Some(e) => e.span,
+                    None => {
+                        let msg = format!("there is no argument named `{}`", name);
+                        self.ecx.span_err(self.fmtsp, &msg[..]);
+                        return;
+                    }
+                };
+                self.verify_same(span, &ty, self.name_types.get(&name));
+                if !self.name_types.contains_key(&name) {
+                    self.name_types.insert(name.clone(), ty);
+                }
+                // Assign this named argument a slot in the arguments array if
+                // it hasn't already been assigned a slot.
+                if !self.name_positions.contains_key(&name) {
+                    let slot = self.name_positions.len();
+                    self.name_positions.insert(name, slot);
+                }
+            }
+        }
+    }
+
+    /// When we're keeping track of the types that are declared for certain
+    /// arguments, we assume that `None` means we haven't seen this argument
+    /// yet, `Some(None)` means that we've seen the argument, but no format was
+    /// specified, and `Some(Some(x))` means that the argument was declared to
+    /// have type `x`.
+    ///
+    /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
+    /// that: `Some(None) == Some(Some(x))`
+    fn verify_same(&self,
+                   sp: Span,
+                   ty: &ArgumentType,
+                   before: Option<&ArgumentType>) {
+        let cur = match before {
+            None => return,
+            Some(t) => t,
+        };
+        if *ty == *cur {
+            return
+        }
+        match (cur, ty) {
+            (&Known(ref cur), &Known(ref ty)) => {
+                self.ecx.span_err(sp,
+                                  &format!("argument redeclared with type `{}` when \
+                                           it was previously `{}`",
+                                          *ty,
+                                          *cur));
+            }
+            (&Known(ref cur), _) => {
+                self.ecx.span_err(sp,
+                                  &format!("argument used to format with `{}` was \
+                                           attempted to not be used for formatting",
+                                           *cur));
+            }
+            (_, &Known(ref ty)) => {
+                self.ecx.span_err(sp,
+                                  &format!("argument previously used as a format \
+                                           argument attempted to be used as `{}`",
+                                           *ty));
+            }
+            (_, _) => {
+                self.ecx.span_err(sp, "argument declared with multiple formats");
+            }
+        }
+    }
+
+    fn rtpath(ecx: &ExtCtxt, s: &str) -> Vec<ast::Ident> {
+        ecx.std_path(&["fmt", "rt", "v1", s])
+    }
+
+    fn trans_count(&self, c: parse::Count) -> P<ast::Expr> {
+        let sp = self.macsp;
+        let count = |c, arg| {
+            let mut path = Context::rtpath(self.ecx, "Count");
+            path.push(self.ecx.ident_of(c));
+            match arg {
+                Some(arg) => self.ecx.expr_call_global(sp, path, vec![arg]),
+                None => self.ecx.expr_path(self.ecx.path_global(sp, path)),
+            }
+        };
+        match c {
+            parse::CountIs(i) => count("Is", Some(self.ecx.expr_usize(sp, i))),
+            parse::CountIsParam(i) => {
+                count("Param", Some(self.ecx.expr_usize(sp, i)))
+            }
+            parse::CountImplied => count("Implied", None),
+            parse::CountIsNextParam => count("NextParam", None),
+            parse::CountIsName(n) => {
+                let i = match self.name_positions.get(n) {
+                    Some(&i) => i,
+                    None => 0, // error already emitted elsewhere
+                };
+                let i = i + self.args.len();
+                count("Param", Some(self.ecx.expr_usize(sp, i)))
+            }
+        }
+    }
+
+    /// Translate the accumulated string literals to a literal expression
+    fn trans_literal_string(&mut self) -> P<ast::Expr> {
+        let sp = self.fmtsp;
+        let s = token::intern_and_get_ident(&self.literal);
+        self.literal.clear();
+        self.ecx.expr_str(sp, s)
+    }
+
+    /// Translate a `parse::Piece` to a static `rt::Argument` or append
+    /// to the `literal` string.
+    fn trans_piece(&mut self, piece: &parse::Piece) -> Option<P<ast::Expr>> {
+        let sp = self.macsp;
+        match *piece {
+            parse::String(s) => {
+                self.literal.push_str(s);
+                None
+            }
+            parse::NextArgument(ref arg) => {
+                // Translate the position
+                let pos = {
+                    let pos = |c, arg| {
+                        let mut path = Context::rtpath(self.ecx, "Position");
+                        path.push(self.ecx.ident_of(c));
+                        match arg {
+                            Some(i) => {
+                                let arg = self.ecx.expr_usize(sp, i);
+                                self.ecx.expr_call_global(sp, path, vec![arg])
+                            }
+                            None => {
+                                self.ecx.expr_path(self.ecx.path_global(sp, path))
+                            }
+                        }
+                    };
+                    match arg.position {
+                        // These two have a direct mapping
+                        parse::ArgumentNext => pos("Next", None),
+                        parse::ArgumentIs(i) => pos("At", Some(i)),
+
+                        // Named arguments are converted to positional arguments
+                        // at the end of the list of arguments
+                        parse::ArgumentNamed(n) => {
+                            let i = match self.name_positions.get(n) {
+                                Some(&i) => i,
+                                None => 0, // error already emitted elsewhere
+                            };
+                            let i = i + self.args.len();
+                            pos("At", Some(i))
+                        }
+                    }
+                };
+
+                let simple_arg = parse::Argument {
+                    position: parse::ArgumentNext,
+                    format: parse::FormatSpec {
+                        fill: arg.format.fill,
+                        align: parse::AlignUnknown,
+                        flags: 0,
+                        precision: parse::CountImplied,
+                        width: parse::CountImplied,
+                        ty: arg.format.ty
+                    }
+                };
+
+                let fill = match arg.format.fill { Some(c) => c, None => ' ' };
+
+                if *arg != simple_arg || fill != ' ' {
+                    self.all_pieces_simple = false;
+                }
+
+                // Translate the format
+                let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
+                let align = |name| {
+                    let mut p = Context::rtpath(self.ecx, "Alignment");
+                    p.push(self.ecx.ident_of(name));
+                    self.ecx.path_global(sp, p)
+                };
+                let align = match arg.format.align {
+                    parse::AlignLeft => align("Left"),
+                    parse::AlignRight => align("Right"),
+                    parse::AlignCenter => align("Center"),
+                    parse::AlignUnknown => align("Unknown"),
+                };
+                let align = self.ecx.expr_path(align);
+                let flags = self.ecx.expr_u32(sp, arg.format.flags);
+                let prec = self.trans_count(arg.format.precision);
+                let width = self.trans_count(arg.format.width);
+                let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec"));
+                let fmt = self.ecx.expr_struct(sp, path, vec!(
+                    self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
+                    self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
+                    self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags),
+                    self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec),
+                    self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
+
+                let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "Argument"));
+                Some(self.ecx.expr_struct(sp, path, vec!(
+                    self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
+                    self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt))))
+            }
+        }
+    }
+
+    fn static_array(ecx: &mut ExtCtxt,
+                    name: &str,
+                    piece_ty: P<ast::Ty>,
+                    pieces: Vec<P<ast::Expr>>)
+                    -> P<ast::Expr> {
+        let sp = piece_ty.span;
+        let ty = ecx.ty_rptr(sp,
+            ecx.ty(sp, ast::TyVec(piece_ty)),
+            Some(ecx.lifetime(sp, special_idents::static_lifetime.name)),
+            ast::MutImmutable);
+        let slice = ecx.expr_vec_slice(sp, pieces);
+        // static instead of const to speed up codegen by not requiring this to be inlined
+        let st = ast::ItemStatic(ty, ast::MutImmutable, slice);
+
+        let name = ecx.ident_of(name);
+        let item = ecx.item(sp, name, vec![], st);
+        let decl = respan(sp, ast::DeclItem(item));
+
+        // Wrap the declaration in a block so that it forms a single expression.
+        ecx.expr_block(ecx.block(sp,
+            vec![P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))],
+            Some(ecx.expr_ident(sp, name))))
+    }
+
+    /// Actually builds the expression which the iformat! block will be expanded
+    /// to
+    fn into_expr(mut self) -> P<ast::Expr> {
+        let mut locals = Vec::new();
+        let mut names = vec![None; self.name_positions.len()];
+        let mut pats = Vec::new();
+        let mut heads = Vec::new();
+
+        // First, build up the static array which will become our precompiled
+        // format "string"
+        let static_lifetime = self.ecx.lifetime(self.fmtsp, special_idents::static_lifetime.name);
+        let piece_ty = self.ecx.ty_rptr(
+                self.fmtsp,
+                self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
+                Some(static_lifetime),
+                ast::MutImmutable);
+        let pieces = Context::static_array(self.ecx,
+                                           "__STATIC_FMTSTR",
+                                           piece_ty,
+                                           self.str_pieces);
+
+
+        // Right now there is a bug such that for the expression:
+        //      foo(bar(&1))
+        // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
+        // valid for the call to `foo`. To work around this all arguments to the
+        // format! string are shoved into locals. Furthermore, we shove the address
+        // of each variable because we don't want to move out of the arguments
+        // passed to this function.
+        for (i, e) in self.args.into_iter().enumerate() {
+            let arg_ty = match self.arg_types[i].as_ref() {
+                Some(ty) => ty,
+                None => continue // error already generated
+            };
+
+            let name = self.ecx.ident_of(&format!("__arg{}", i));
+            pats.push(self.ecx.pat_ident(e.span, name));
+            locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty,
+                                            self.ecx.expr_ident(e.span, name)));
+            heads.push(self.ecx.expr_addr_of(e.span, e));
+        }
+        for name in &self.name_ordering {
+            let e = match self.names.remove(name) {
+                Some(e) => e,
+                None => continue
+            };
+            let arg_ty = match self.name_types.get(name) {
+                Some(ty) => ty,
+                None => continue
+            };
+
+            let lname = self.ecx.ident_of(&format!("__arg{}",
+                                                  *name));
+            pats.push(self.ecx.pat_ident(e.span, lname));
+            names[*self.name_positions.get(name).unwrap()] =
+                Some(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty,
+                                         self.ecx.expr_ident(e.span, lname)));
+            heads.push(self.ecx.expr_addr_of(e.span, e));
+        }
+
+        // Now create a vector containing all the arguments
+        let args = locals.into_iter().chain(names.into_iter().map(|a| a.unwrap()));
+
+        let args_array = self.ecx.expr_vec(self.fmtsp, args.collect());
+
+        // Constructs an AST equivalent to:
+        //
+        //      match (&arg0, &arg1) {
+        //          (tmp0, tmp1) => args_array
+        //      }
+        //
+        // It was:
+        //
+        //      let tmp0 = &arg0;
+        //      let tmp1 = &arg1;
+        //      args_array
+        //
+        // Because of #11585 the new temporary lifetime rule, the enclosing
+        // statements for these temporaries become the let's themselves.
+        // If one or more of them are RefCell's, RefCell borrow() will also
+        // end there; they don't last long enough for args_array to use them.
+        // The match expression solves the scope problem.
+        //
+        // Note, it may also very well be transformed to:
+        //
+        //      match arg0 {
+        //          ref tmp0 => {
+        //              match arg1 => {
+        //                  ref tmp1 => args_array } } }
+        //
+        // But the nested match expression is proved to perform not as well
+        // as series of let's; the first approach does.
+        let pat = self.ecx.pat_tuple(self.fmtsp, pats);
+        let arm = self.ecx.arm(self.fmtsp, vec!(pat), args_array);
+        let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads));
+        let result = self.ecx.expr_match(self.fmtsp, head, vec!(arm));
+
+        let args_slice = self.ecx.expr_addr_of(self.fmtsp, result);
+
+        // Now create the fmt::Arguments struct with all our locals we created.
+        let (fn_name, fn_args) = if self.all_pieces_simple {
+            ("new_v1", vec![pieces, args_slice])
+        } else {
+            // Build up the static array which will store our precompiled
+            // nonstandard placeholders, if there are any.
+            let piece_ty = self.ecx.ty_path(self.ecx.path_global(
+                    self.macsp,
+                    Context::rtpath(self.ecx, "Argument")));
+            let fmt = Context::static_array(self.ecx,
+                                            "__STATIC_FMTARGS",
+                                            piece_ty,
+                                            self.pieces);
+
+            ("new_v1_formatted", vec![pieces, args_slice, fmt])
+        };
+
+        let path = self.ecx.std_path(&["fmt", "Arguments", fn_name]);
+        self.ecx.expr_call_global(self.macsp, path, fn_args)
+    }
+
+    fn format_arg(ecx: &ExtCtxt, macsp: Span, sp: Span,
+                  ty: &ArgumentType, arg: P<ast::Expr>)
+                  -> P<ast::Expr> {
+        let trait_ = match *ty {
+            Known(ref tyname) => {
+                match &tyname[..] {
+                    ""  => "Display",
+                    "?" => "Debug",
+                    "e" => "LowerExp",
+                    "E" => "UpperExp",
+                    "o" => "Octal",
+                    "p" => "Pointer",
+                    "b" => "Binary",
+                    "x" => "LowerHex",
+                    "X" => "UpperHex",
+                    _ => {
+                        ecx.span_err(sp,
+                                     &format!("unknown format trait `{}`",
+                                             *tyname));
+                        "Dummy"
+                    }
+                }
+            }
+            Unsigned => {
+                let path = ecx.std_path(&["fmt", "ArgumentV1", "from_usize"]);
+                return ecx.expr_call_global(macsp, path, vec![arg])
+            }
+        };
+
+        let path = ecx.std_path(&["fmt", trait_, "fmt"]);
+        let format_fn = ecx.path_global(sp, path);
+        let path = ecx.std_path(&["fmt", "ArgumentV1", "new"]);
+        ecx.expr_call_global(macsp, path, vec![arg, ecx.expr_path(format_fn)])
+    }
+}
+
+pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
+                               tts: &[ast::TokenTree])
+                               -> Box<base::MacResult+'cx> {
+
+    match parse_args(ecx, sp, tts) {
+        Some((efmt, args, order, names)) => {
+            MacEager::expr(expand_preparsed_format_args(ecx, sp, efmt,
+                                                      args, order, names))
+        }
+        None => DummyResult::expr(sp)
+    }
+}
+
+/// Take the various parts of `format_args!(efmt, args..., name=names...)`
+/// and construct the appropriate formatting expression.
+pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
+                                    efmt: P<ast::Expr>,
+                                    args: Vec<P<ast::Expr>>,
+                                    name_ordering: Vec<String>,
+                                    names: HashMap<String, P<ast::Expr>>)
+                                    -> P<ast::Expr> {
+    let arg_types: Vec<_> = (0..args.len()).map(|_| None).collect();
+    let macsp = ecx.call_site();
+    // Expand the format literal so that efmt.span will have a backtrace. This
+    // is essential for locating a bug when the format literal is generated in
+    // a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")).
+    let efmt = ecx.expander().fold_expr(efmt);
+    let mut cx = Context {
+        ecx: ecx,
+        args: args,
+        arg_types: arg_types,
+        names: names,
+        name_positions: HashMap::new(),
+        name_types: HashMap::new(),
+        name_ordering: name_ordering,
+        nest_level: 0,
+        next_arg: 0,
+        literal: String::new(),
+        pieces: Vec::new(),
+        str_pieces: Vec::new(),
+        all_pieces_simple: true,
+        macsp: macsp,
+        fmtsp: efmt.span,
+    };
+    let fmt = match expr_to_string(cx.ecx,
+                                   efmt,
+                                   "format argument must be a string literal.") {
+        Some((fmt, _)) => fmt,
+        None => return DummyResult::raw_expr(sp)
+    };
+
+    let mut parser = parse::Parser::new(&fmt);
+
+    loop {
+        match parser.next() {
+            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 => {}
+                }
+            }
+            None => break
+        }
+    }
+    if !parser.errors.is_empty() {
+        cx.ecx.span_err(cx.fmtsp, &format!("invalid format string: {}",
+                                          parser.errors.remove(0)));
+        return DummyResult::raw_expr(sp);
+    }
+    if !cx.literal.is_empty() {
+        let s = cx.trans_literal_string();
+        cx.str_pieces.push(s);
+    }
+
+    // Make sure that all arguments were used and all arguments have types.
+    for (i, ty) in cx.arg_types.iter().enumerate() {
+        if ty.is_none() {
+            cx.ecx.span_err(cx.args[i].span, "argument never used");
+        }
+    }
+    for (name, e) in &cx.names {
+        if !cx.name_types.contains_key(name) {
+            cx.ecx.span_err(e.span, "named argument never used");
+        }
+    }
+
+    cx.into_expr()
+}
diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs
new file mode 100644 (file)
index 0000000..0f049fa
--- /dev/null
@@ -0,0 +1,74 @@
+// 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.
+
+//! Syntax extensions in the Rust compiler.
+
+#![crate_name = "syntax_ext"]
+#![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/")]
+
+#![unstable(feature = "rustc_private", issue = "27812")]
+
+#![feature(rustc_private)]
+#![feature(staged_api)]
+#![feature(str_char)]
+
+extern crate fmt_macros;
+#[macro_use]
+extern crate syntax;
+
+use syntax::ext::base::{MacroExpanderFn, NormalTT};
+use syntax::ext::base::{SyntaxEnv, SyntaxExtension};
+use syntax::parse::token::intern;
+
+
+mod asm;
+mod cfg;
+mod concat;
+mod concat_idents;
+mod env;
+mod format;
+mod log_syntax;
+mod trace_macros;
+
+// for custom_derive
+pub mod deriving;
+
+pub fn register_builtins(env: &mut SyntaxEnv) {
+    // utility function to simplify creating NormalTT syntax extensions
+    fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension {
+        NormalTT(Box::new(f), None, false)
+    }
+
+    env.insert(intern("asm"),
+               builtin_normal_expander(asm::expand_asm));
+    env.insert(intern("cfg"),
+               builtin_normal_expander(cfg::expand_cfg));
+    env.insert(intern("concat"),
+               builtin_normal_expander(concat::expand_syntax_ext));
+    env.insert(intern("concat_idents"),
+               builtin_normal_expander(concat_idents::expand_syntax_ext));
+    env.insert(intern("env"),
+               builtin_normal_expander(env::expand_env));
+    env.insert(intern("option_env"),
+               builtin_normal_expander(env::expand_option_env));
+    env.insert(intern("format_args"),
+               // format_args uses `unstable` things internally.
+               NormalTT(Box::new(format::expand_format_args), None, true));
+    env.insert(intern("log_syntax"),
+               builtin_normal_expander(log_syntax::expand_syntax_ext));
+    env.insert(intern("trace_macros"),
+               builtin_normal_expander(trace_macros::expand_trace_macros));
+
+    deriving::register_all(env);
+}
diff --git a/src/libsyntax_ext/log_syntax.rs b/src/libsyntax_ext/log_syntax.rs
new file mode 100644 (file)
index 0000000..ee944ab
--- /dev/null
@@ -0,0 +1,34 @@
+// 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.
+
+use syntax::ast;
+use syntax::codemap;
+use syntax::ext::base;
+use syntax::feature_gate;
+use syntax::print;
+
+pub fn expand_syntax_ext<'cx>(cx: &'cx mut base::ExtCtxt,
+                              sp: codemap::Span,
+                              tts: &[ast::TokenTree])
+                              -> Box<base::MacResult+'cx> {
+    if !cx.ecfg.enable_log_syntax() {
+        feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
+                                       "log_syntax",
+                                       sp,
+                                       feature_gate::GateIssue::Language,
+                                       feature_gate::EXPLAIN_LOG_SYNTAX);
+        return base::DummyResult::any(sp);
+    }
+
+    println!("{}", print::pprust::tts_to_string(tts));
+
+    // any so that `log_syntax` can be invoked as an expression and item.
+    base::DummyResult::any(sp)
+}
diff --git a/src/libsyntax_ext/trace_macros.rs b/src/libsyntax_ext/trace_macros.rs
new file mode 100644 (file)
index 0000000..7b1e985
--- /dev/null
@@ -0,0 +1,43 @@
+// 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.
+
+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;
+
+
+pub fn expand_trace_macros(cx: &mut ExtCtxt,
+                           sp: Span,
+                           tt: &[TokenTree])
+                           -> Box<base::MacResult+'static> {
+    if !cx.ecfg.enable_trace_macros() {
+        feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
+                                       "trace_macros",
+                                       sp,
+                                       feature_gate::GateIssue::Language,
+                                       feature_gate::EXPLAIN_TRACE_MACROS);
+        return base::DummyResult::any(sp);
+    }
+
+    match (tt.len(), tt.first()) {
+        (1, Some(&TokenTree::Token(_, ref tok))) if tok.is_keyword(keywords::True) => {
+            cx.set_trace_macros(true);
+        }
+        (1, Some(&TokenTree::Token(_, ref tok))) if tok.is_keyword(keywords::False) => {
+            cx.set_trace_macros(false);
+        }
+        _ => cx.span_err(sp, "trace_macros! accepts only `true` or `false`"),
+    }
+
+    base::DummyResult::any(sp)
+}
index 69ad55d79089931d4302dd92fd7fa5623ad7873d..df1fedf3d4e6610c23ee2a5fe341f28e9c44aca1 100644 (file)
 //! [win]: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682010%28v=vs.85%29.aspx
 //! [ti]: https://en.wikipedia.org/wiki/Terminfo
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "term"]
 #![unstable(feature = "rustc_private",
             reason = "use the crates.io `term` library instead",
             issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -77,9 +74,9 @@ pub mod terminfo;
 mod win;
 
 /// Alias for stdout terminals.
-pub type StdoutTerminal = Terminal<Output=Stdout> + Send;
+pub type StdoutTerminal = Terminal<Output = Stdout> + Send;
 /// Alias for stderr terminals.
-pub type StderrTerminal = Terminal<Output=Stderr> + Send;
+pub type StderrTerminal = Terminal<Output = Stderr> + Send;
 
 #[cfg(not(windows))]
 /// Return a Terminal wrapping stdout, or None if a terminal couldn't be
index a4e5d00ee7a77646e68082cde6c28a5e1e8e7413..04486132c84de27c720e8d720ab6cdc217b46b3e 100644 (file)
@@ -151,7 +151,7 @@ pub struct TerminfoTerminal<T> {
     ti: TermInfo,
 }
 
-impl<T: Write+Send> Terminal for TerminfoTerminal<T> {
+impl<T: Write + Send> Terminal for TerminfoTerminal<T> {
     type Output = T;
     fn fg(&mut self, color: color::Color) -> io::Result<bool> {
         let color = self.dim_if_necessary(color);
@@ -220,7 +220,7 @@ impl<T: Write+Send> Terminal for TerminfoTerminal<T> {
     }
 }
 
-impl<T: Write+Send> TerminfoTerminal<T> {
+impl<T: Write + Send> TerminfoTerminal<T> {
     /// Create a new TerminfoTerminal with the given TermInfo and Write.
     pub fn new_with_terminfo(out: T, terminfo: TermInfo) -> TerminfoTerminal<T> {
         let nc = if terminfo.strings.contains_key("setaf") &&
index 2cb7018669d3daacdfd212f2efc70d32cc3e004f..d36b182710b9790655d60910c3241b4249f9f22a 100644 (file)
@@ -91,7 +91,7 @@ fn bits_to_color(bits: u16) -> color::Color {
     color | (bits & 0x8) // copy the hi-intensity bit
 }
 
-impl<T: Write+Send+'static> WinConsole<T> {
+impl<T: Write + Send + 'static> WinConsole<T> {
     fn apply(&mut self) {
         let _unused = self.buf.flush();
         let mut accum: WORD = 0;
@@ -148,7 +148,7 @@ impl<T: Write> Write for WinConsole<T> {
     }
 }
 
-impl<T: Write+Send+'static> Terminal for WinConsole<T> {
+impl<T: Write + Send + 'static> Terminal for WinConsole<T> {
     type Output = T;
 
     fn fg(&mut self, color: color::Color) -> io::Result<bool> {
index 7ffe567ac83c135a82c54beefb5d1a2424dde1c6..ac5b235854a04cc54fb174cd94ea2fdc1f99026c 100644 (file)
 // running tests while providing a base that other test frameworks may
 // build off of.
 
-// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
-#![cfg_attr(stage0, feature(custom_attribute))]
 #![crate_name = "test"]
 #![unstable(feature = "test", issue = "27812")]
-#![cfg_attr(stage0, staged_api)]
 #![crate_type = "rlib"]
 #![crate_type = "dylib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -59,11 +56,9 @@ use self::NamePadding::*;
 use self::OutputLocation::*;
 
 use stats::Stats;
-use getopts::{OptGroup, optflag, optopt};
 use serialize::Encodable;
 use std::boxed::FnBox;
 use term::Terminal;
-use term::color::{Color, RED, YELLOW, GREEN, CYAN};
 
 use std::any::Any;
 use std::cmp;
@@ -82,12 +77,10 @@ use std::time::{Instant, Duration};
 
 // to be used by rustc to compile tests in libtest
 pub mod test {
-    pub use {Bencher, TestName, TestResult, TestDesc,
-             TestDescAndFn, TestOpts, TrFailed, TrIgnored, TrOk,
-             Metric, MetricMap,
-             StaticTestFn, StaticTestName, DynTestName, DynTestFn,
-             run_test, test_main, test_main_static, filter_tests,
-             parse_opts, StaticBenchFn, ShouldPanic};
+    pub use {Bencher, TestName, TestResult, TestDesc, TestDescAndFn, TestOpts, TrFailed,
+             TrIgnored, TrOk, Metric, MetricMap, StaticTestFn, StaticTestName, DynTestName,
+             DynTestFn, run_test, test_main, test_main_static, filter_tests, parse_opts,
+             StaticBenchFn, ShouldPanic};
 }
 
 pub mod stats;
@@ -100,13 +93,13 @@ pub mod stats;
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub enum TestName {
     StaticTestName(&'static str),
-    DynTestName(String)
+    DynTestName(String),
 }
 impl TestName {
     fn as_slice(&self) -> &str {
         match *self {
             StaticTestName(s) => s,
-            DynTestName(ref s) => s
+            DynTestName(ref s) => s,
         }
     }
 }
@@ -151,19 +144,19 @@ pub enum TestFn {
     StaticBenchFn(fn(&mut Bencher)),
     StaticMetricFn(fn(&mut MetricMap)),
     DynTestFn(Box<FnBox() + Send>),
-    DynMetricFn(Box<FnBox(&mut MetricMap)+Send>),
-    DynBenchFn(Box<TDynBenchFn+'static>)
+    DynMetricFn(Box<FnBox(&mut MetricMap) + Send>),
+    DynBenchFn(Box<TDynBenchFn + 'static>),
 }
 
 impl TestFn {
     fn padding(&self) -> NamePadding {
         match *self {
-            StaticTestFn(..)   => PadNone,
-            StaticBenchFn(..)  => PadOnRight,
+            StaticTestFn(..) => PadNone,
+            StaticBenchFn(..) => PadOnRight,
             StaticMetricFn(..) => PadOnRight,
-            DynTestFn(..)      => PadNone,
-            DynMetricFn(..)    => PadOnRight,
-            DynBenchFn(..)     => PadOnRight,
+            DynTestFn(..) => PadNone,
+            DynMetricFn(..) => PadOnRight,
+            DynBenchFn(..) => PadOnRight,
         }
     }
 }
@@ -176,7 +169,7 @@ impl fmt::Debug for TestFn {
             StaticMetricFn(..) => "StaticMetricFn(..)",
             DynTestFn(..) => "DynTestFn(..)",
             DynMetricFn(..) => "DynMetricFn(..)",
-            DynBenchFn(..) => "DynBenchFn(..)"
+            DynBenchFn(..) => "DynBenchFn(..)",
         })
     }
 }
@@ -197,7 +190,7 @@ pub struct Bencher {
 pub enum ShouldPanic {
     No,
     Yes,
-    YesWithMessage(&'static str)
+    YesWithMessage(&'static str),
 }
 
 // The definition of a single test. A test runner will run a list of
@@ -220,17 +213,20 @@ pub struct TestDescAndFn {
 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug, Copy)]
 pub struct Metric {
     value: f64,
-    noise: f64
+    noise: f64,
 }
 
 impl Metric {
     pub fn new(value: f64, noise: f64) -> Metric {
-        Metric {value: value, noise: noise}
+        Metric {
+            value: value,
+            noise: noise,
+        }
     }
 }
 
 #[derive(PartialEq)]
-pub struct MetricMap(BTreeMap<String,Metric>);
+pub struct MetricMap(BTreeMap<String, Metric>);
 
 impl Clone for MetricMap {
     fn clone(&self) -> MetricMap {
@@ -241,13 +237,12 @@ impl Clone for MetricMap {
 
 // The default console test runner. It accepts the command line
 // arguments and a vector of test_descs.
-pub fn test_main(args: &[String], tests: Vec<TestDescAndFn> ) {
-    let opts =
-        match parse_opts(args) {
-            Some(Ok(o)) => o,
-            Some(Err(msg)) => panic!("{:?}", msg),
-            None => return
-        };
+pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>) {
+    let opts = match parse_opts(args) {
+        Some(Ok(o)) => o,
+        Some(Err(msg)) => panic!("{:?}", msg),
+        None => return,
+    };
     match run_tests_console(&opts, tests) {
         Ok(true) => {}
         Ok(false) => std::process::exit(101),
@@ -264,13 +259,25 @@ pub fn test_main(args: &[String], tests: Vec<TestDescAndFn> ) {
 // rather than a &[].
 pub fn test_main_static(tests: &[TestDescAndFn]) {
     let args = env::args().collect::<Vec<_>>();
-    let owned_tests = tests.iter().map(|t| {
-        match t.testfn {
-            StaticTestFn(f) => TestDescAndFn { testfn: StaticTestFn(f), desc: t.desc.clone() },
-            StaticBenchFn(f) => TestDescAndFn { testfn: StaticBenchFn(f), desc: t.desc.clone() },
-            _ => panic!("non-static tests passed to test::test_main_static")
-        }
-    }).collect();
+    let owned_tests = tests.iter()
+                           .map(|t| {
+                               match t.testfn {
+                                   StaticTestFn(f) => {
+                                       TestDescAndFn {
+                                           testfn: StaticTestFn(f),
+                                           desc: t.desc.clone(),
+                                       }
+                                   }
+                                   StaticBenchFn(f) => {
+                                       TestDescAndFn {
+                                           testfn: StaticBenchFn(f),
+                                           desc: t.desc.clone(),
+                                       }
+                                   }
+                                   _ => panic!("non-static tests passed to test::test_main_static"),
+                               }
+                           })
+                           .collect();
     test_main(&args, owned_tests)
 }
 
@@ -309,6 +316,7 @@ impl TestOpts {
 /// Result of parsing the options.
 pub type OptRes = Result<TestOpts, String>;
 
+#[cfg_attr(rustfmt, rustfmt_skip)]
 fn optgroups() -> Vec<getopts::OptGroup> {
     vec!(getopts::optflag("", "ignored", "Run ignored tests"),
       getopts::optflag("", "test", "Run tests and not benchmarks"),
@@ -328,8 +336,8 @@ fn usage(binary: &str) {
     let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
     println!(r#"{usage}
 
-The FILTER regex is tested against the name of all tests to run, and
-only those tests that match are run.
+The FILTER string is tested against the name of all tests, and only those
+tests whose names contain the filter are run.
 
 By default, all tests are run in parallel. This can be altered with the
 RUST_TEST_THREADS environment variable when running tests (set it to 1).
@@ -358,13 +366,15 @@ Test Attributes:
 // Parses command line arguments into test options
 pub fn parse_opts(args: &[String]) -> Option<OptRes> {
     let args_ = &args[1..];
-    let matches =
-        match getopts::getopts(args_, &optgroups()) {
-          Ok(m) => m,
-          Err(f) => return Some(Err(f.to_string()))
-        };
+    let matches = match getopts::getopts(args_, &optgroups()) {
+        Ok(m) => m,
+        Err(f) => return Some(Err(f.to_string())),
+    };
 
-    if matches.opt_present("h") { usage(&args[0]); return None; }
+    if matches.opt_present("h") {
+        usage(&args[0]);
+        return None;
+    }
 
     let filter = if !matches.free.is_empty() {
         Some(matches.free[0].clone())
@@ -378,8 +388,7 @@ pub fn parse_opts(args: &[String]) -> Option<OptRes> {
     let logfile = logfile.map(|s| PathBuf::from(&s));
 
     let bench_benchmarks = matches.opt_present("bench");
-    let run_tests = ! bench_benchmarks ||
-        matches.opt_present("test");
+    let run_tests = !bench_benchmarks || matches.opt_present("test");
 
     let mut nocapture = matches.opt_present("nocapture");
     if !nocapture {
@@ -391,9 +400,11 @@ pub fn parse_opts(args: &[String]) -> Option<OptRes> {
         Some("always") => AlwaysColor,
         Some("never") => NeverColor,
 
-        Some(v) => return Some(Err(format!("argument for --color must be \
-                                            auto, always, or never (was {})",
-                                            v))),
+        Some(v) => {
+            return Some(Err(format!("argument for --color must be auto, always, or never (was \
+                                     {})",
+                                    v)))
+        }
     };
 
     let test_opts = TestOpts {
@@ -441,20 +452,19 @@ struct ConsoleTestState<T> {
     ignored: usize,
     measured: usize,
     metrics: MetricMap,
-    failures: Vec<(TestDesc, Vec<u8> )> ,
+    failures: Vec<(TestDesc, Vec<u8>)>,
     max_name_len: usize, // number of columns to fill when aligning names
 }
 
 impl<T: Write> ConsoleTestState<T> {
-    pub fn new(opts: &TestOpts,
-               _: Option<T>) -> io::Result<ConsoleTestState<io::Stdout>> {
+    pub fn new(opts: &TestOpts, _: Option<T>) -> io::Result<ConsoleTestState<io::Stdout>> {
         let log_out = match opts.logfile {
             Some(ref path) => Some(try!(File::create(path))),
-            None => None
+            None => None,
         };
         let out = match term::stdout() {
             None => Raw(io::stdout()),
-            Some(t) => Pretty(t)
+            Some(t) => Pretty(t),
         };
 
         Ok(ConsoleTestState {
@@ -492,9 +502,7 @@ impl<T: Write> ConsoleTestState<T> {
         self.write_pretty("bench", term::color::CYAN)
     }
 
-    pub fn write_pretty(&mut self,
-                        word: &str,
-                        color: term::color::Color) -> io::Result<()> {
+    pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> {
         match self.out {
             Pretty(ref mut term) => {
                 if self.use_color {
@@ -518,22 +526,25 @@ impl<T: Write> ConsoleTestState<T> {
             Pretty(ref mut term) => {
                 try!(term.write_all(s.as_bytes()));
                 term.flush()
-            },
+            }
             Raw(ref mut stdout) => {
                 try!(stdout.write_all(s.as_bytes()));
                 stdout.flush()
-            },
+            }
         }
     }
 
     pub fn write_run_start(&mut self, len: usize) -> io::Result<()> {
         self.total = len;
-        let noun = if len != 1 { "tests" } else { "test" };
+        let noun = if len != 1 {
+            "tests"
+        } else {
+            "test"
+        };
         self.write_plain(&format!("\nrunning {} {}\n", len, noun))
     }
 
-    pub fn write_test_start(&mut self, test: &TestDesc,
-                            align: NamePadding) -> io::Result<()> {
+    pub fn write_test_start(&mut self, test: &TestDesc, align: NamePadding) -> io::Result<()> {
         let name = test.padded_name(self.max_name_len, align);
         self.write_plain(&format!("test {} ... ", name))
     }
@@ -558,18 +569,19 @@ impl<T: Write> ConsoleTestState<T> {
         self.write_plain("\n")
     }
 
-    pub fn write_log(&mut self, test: &TestDesc,
-                     result: &TestResult) -> io::Result<()> {
+    pub fn write_log(&mut self, test: &TestDesc, result: &TestResult) -> io::Result<()> {
         match self.log_out {
             None => Ok(()),
             Some(ref mut o) => {
-                let s = format!("{} {}\n", match *result {
-                        TrOk => "ok".to_owned(),
-                        TrFailed => "failed".to_owned(),
-                        TrIgnored => "ignored".to_owned(),
-                        TrMetrics(ref mm) => mm.fmt_metrics(),
-                        TrBench(ref bs) => fmt_bench_samples(bs)
-                    }, test.name);
+                let s = format!("{} {}\n",
+                                match *result {
+                                    TrOk => "ok".to_owned(),
+                                    TrFailed => "failed".to_owned(),
+                                    TrIgnored => "ignored".to_owned(),
+                                    TrMetrics(ref mm) => mm.fmt_metrics(),
+                                    TrBench(ref bs) => fmt_bench_samples(bs),
+                                },
+                                test.name);
                 o.write_all(s.as_bytes())
             }
         }
@@ -617,7 +629,10 @@ impl<T: Write> ConsoleTestState<T> {
             try!(self.write_failed());
         }
         let s = format!(". {} passed; {} failed; {} ignored; {} measured\n\n",
-                        self.passed, self.failed, self.ignored, self.measured);
+                        self.passed,
+                        self.failed,
+                        self.ignored,
+                        self.measured);
         try!(self.write_plain(&s));
         return Ok(success);
     }
@@ -655,8 +670,9 @@ pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
     let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize;
 
     output.write_fmt(format_args!("{:>11} ns/iter (+/- {})",
-                     fmt_thousands_sep(median, ','),
-                     fmt_thousands_sep(deviation, ','))).unwrap();
+                                  fmt_thousands_sep(median, ','),
+                                  fmt_thousands_sep(deviation, ',')))
+          .unwrap();
     if bs.mb_s != 0 {
         output.write_fmt(format_args!(" = {} MB/s", bs.mb_s)).unwrap();
     }
@@ -664,10 +680,9 @@ pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
 }
 
 // A simple console test runner
-pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn> ) -> io::Result<bool> {
+pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<bool> {
 
-    fn callback<T: Write>(event: &TestEvent,
-                          st: &mut ConsoleTestState<T>) -> io::Result<()> {
+    fn callback<T: Write>(event: &TestEvent, st: &mut ConsoleTestState<T>) -> io::Result<()> {
         match (*event).clone() {
             TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
             TeWait(ref test, padding) => st.write_test_start(test, padding),
@@ -680,13 +695,9 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn> ) -> io::Res
                     TrMetrics(mm) => {
                         let tname = test.name;
                         let MetricMap(mm) = mm;
-                        for (k,v) in &mm {
+                        for (k, v) in &mm {
                             st.metrics
-                              .insert_metric(&format!("{}.{}",
-                                                      tname,
-                                                      k),
-                                             v.value,
-                                             v.noise);
+                              .insert_metric(&format!("{}.{}", tname, k), v.value, v.noise);
                         }
                         st.measured += 1
                     }
@@ -713,11 +724,11 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn> ) -> io::Res
             PadOnRight => t.desc.name.as_slice().len(),
         }
     }
-    match tests.iter().max_by_key(|t|len_if_padded(*t)) {
+    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 => {}
     }
     try!(run_tests(opts, tests, |x| callback(&x, &mut st)));
@@ -729,13 +740,13 @@ fn should_sort_failures_before_printing_them() {
     let test_a = TestDesc {
         name: StaticTestName("a"),
         ignore: false,
-        should_panic: ShouldPanic::No
+        should_panic: ShouldPanic::No,
     };
 
     let test_b = TestDesc {
         name: StaticTestName("b"),
         ignore: false,
-        should_panic: ShouldPanic::No
+        should_panic: ShouldPanic::No,
     };
 
     let mut st = ConsoleTestState {
@@ -749,13 +760,13 @@ fn should_sort_failures_before_printing_them() {
         measured: 0,
         max_name_len: 10,
         metrics: MetricMap::new(),
-        failures: vec!((test_b, Vec::new()), (test_a, Vec::new()))
+        failures: vec![(test_b, Vec::new()), (test_a, Vec::new())],
     };
 
     st.write_failures().unwrap();
     let s = match st.out {
         Raw(ref m) => String::from_utf8_lossy(&m[..]),
-        Pretty(_) => unreachable!()
+        Pretty(_) => unreachable!(),
     };
 
     let apos = s.find("a").unwrap();
@@ -795,18 +806,16 @@ fn stdout_isatty() -> bool {
 
 #[derive(Clone)]
 enum TestEvent {
-    TeFiltered(Vec<TestDesc> ),
+    TeFiltered(Vec<TestDesc>),
     TeWait(TestDesc, NamePadding),
-    TeResult(TestDesc, TestResult, Vec<u8> ),
+    TeResult(TestDesc, TestResult, Vec<u8>),
 }
 
-pub type MonitorMsg = (TestDesc, TestResult, Vec<u8> );
+pub type MonitorMsg = (TestDesc, TestResult, Vec<u8>);
 
 
-fn run_tests<F>(opts: &TestOpts,
-                tests: Vec<TestDescAndFn> ,
-                mut callback: F) -> io::Result<()> where
-    F: FnMut(TestEvent) -> io::Result<()>,
+fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) -> io::Result<()>
+    where F: FnMut(TestEvent) -> io::Result<()>
 {
     let mut filtered_tests = filter_tests(opts, tests);
     if !opts.bench_benchmarks {
@@ -823,7 +832,7 @@ fn run_tests<F>(opts: &TestOpts,
         filtered_tests.into_iter().partition(|e| {
             match e.testfn {
                 StaticTestFn(_) | DynTestFn(_) => true,
-                _ => false
+                _ => false,
             }
         });
 
@@ -878,7 +887,10 @@ fn get_concurrency() -> usize {
             let opt_n: Option<usize> = s.parse().ok();
             match opt_n {
                 Some(n) if n > 0 => n,
-                _ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", s)
+                _ => {
+                    panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.",
+                           s)
+                }
             }
         }
         Err(..) => num_cpus(),
@@ -911,10 +923,66 @@ fn get_concurrency() -> usize {
         }
     }
 
-    #[cfg(unix)]
+    #[cfg(any(target_os = "linux",
+              target_os = "macos",
+              target_os = "ios",
+              target_os = "android"))]
+    fn num_cpus() -> usize {
+        unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize }
+    }
+
+    #[cfg(any(target_os = "freebsd",
+              target_os = "dragonfly",
+              target_os = "bitrig",
+              target_os = "netbsd"))]
     fn num_cpus() -> usize {
-        extern { fn rust_get_num_cpus() -> libc::uintptr_t; }
-        unsafe { rust_get_num_cpus() as usize }
+        let mut cpus: libc::c_uint = 0;
+        let mut cpus_size = std::mem::size_of_val(&cpus);
+        let mut mib = [libc::CTL_HW, libc::HW_AVAILCPU, 0, 0];
+
+        unsafe {
+            libc::sysctl(mib.as_mut_ptr(),
+                         2,
+                         &mut cpus as *mut _ as *mut _,
+                         &mut cpus_size as *mut _ as *mut _,
+                         0 as *mut _,
+                         0);
+        }
+        if cpus < 1 {
+            mib[1] = libc::HW_NCPU;
+            unsafe {
+                libc::sysctl(mib.as_mut_ptr(),
+                             2,
+                             &mut cpus as *mut _ as *mut _,
+                             &mut cpus_size as *mut _ as *mut _,
+                             0 as *mut _,
+                             0);
+            }
+            if cpus < 1 {
+                cpus = 1;
+            }
+        }
+        cpus as usize
+    }
+
+    #[cfg(target_os = "openbsd")]
+    fn num_cpus() -> usize {
+        let mut cpus: libc::c_uint = 0;
+        let mut cpus_size = std::mem::size_of_val(&cpus);
+        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
+
+        unsafe {
+            libc::sysctl(mib.as_mut_ptr(),
+                         2,
+                         &mut cpus as *mut _ as *mut _,
+                         &mut cpus_size as *mut _ as *mut _,
+                         0 as *mut _,
+                         0);
+        }
+        if cpus < 1 {
+            cpus = 1;
+        }
+        cpus as usize
     }
 }
 
@@ -925,9 +993,9 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescA
     filtered = match opts.filter {
         None => filtered,
         Some(ref filter) => {
-            filtered.into_iter().filter(|test| {
-                test.desc.name.as_slice().contains(&filter[..])
-            }).collect()
+            filtered.into_iter()
+                    .filter(|test| test.desc.name.as_slice().contains(&filter[..]))
+                    .collect()
         }
     };
 
@@ -939,8 +1007,8 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescA
             if test.desc.ignore {
                 let TestDescAndFn {desc, testfn} = test;
                 Some(TestDescAndFn {
-                    desc: TestDesc {ignore: false, ..desc},
-                    testfn: testfn
+                    desc: TestDesc { ignore: false, ..desc },
+                    testfn: testfn,
                 })
             } else {
                 None
@@ -957,18 +1025,23 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescA
 
 pub fn convert_benchmarks_to_tests(tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
     // convert benchmarks to tests, if we're not benchmarking them
-    tests.into_iter().map(|x| {
-        let testfn = match x.testfn {
-            DynBenchFn(bench) => {
-                DynTestFn(Box::new(move || bench::run_once(|b| bench.run(b))))
-            }
-            StaticBenchFn(benchfn) => {
-                DynTestFn(Box::new(move || bench::run_once(|b| benchfn(b))))
-            }
-            f => f
-        };
-        TestDescAndFn { desc: x.desc, testfn: testfn }
-    }).collect()
+    tests.into_iter()
+         .map(|x| {
+             let testfn = match x.testfn {
+                 DynBenchFn(bench) => {
+                     DynTestFn(Box::new(move || bench::run_once(|b| bench.run(b))))
+                 }
+                 StaticBenchFn(benchfn) => {
+                     DynTestFn(Box::new(move || bench::run_once(|b| benchfn(b))))
+                 }
+                 f => f,
+             };
+             TestDescAndFn {
+                 desc: x.desc,
+                 testfn: testfn,
+             }
+         })
+         .collect()
 }
 
 pub fn run_test(opts: &TestOpts,
@@ -992,7 +1065,9 @@ pub fn run_test(opts: &TestOpts,
             fn write(&mut self, data: &[u8]) -> io::Result<usize> {
                 Write::write(&mut *self.0.lock().unwrap(), data)
             }
-            fn flush(&mut self) -> io::Result<()> { Ok(()) }
+            fn flush(&mut self) -> io::Result<()> {
+                Ok(())
+            }
         }
 
         thread::spawn(move || {
@@ -1004,12 +1079,13 @@ pub fn run_test(opts: &TestOpts,
             });
 
             let result_guard = cfg.spawn(move || {
-                if !nocapture {
-                    io::set_print(box Sink(data2.clone()));
-                    io::set_panic(box Sink(data2));
-                }
-                testfn()
-            }).unwrap();
+                                      if !nocapture {
+                                          io::set_print(box Sink(data2.clone()));
+                                          io::set_panic(box Sink(data2));
+                                      }
+                                      testfn()
+                                  })
+                                  .unwrap();
             let test_result = calc_result(&desc, result_guard.join());
             let stdout = data.lock().unwrap().to_vec();
             monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
@@ -1040,27 +1116,25 @@ pub fn run_test(opts: &TestOpts,
             return;
         }
         DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f),
-        StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture,
-                                          Box::new(f))
+        StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, Box::new(f)),
     }
 }
 
-fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any+Send>>) -> TestResult {
+fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any + Send>>) -> TestResult {
     match (&desc.should_panic, task_result) {
         (&ShouldPanic::No, Ok(())) |
         (&ShouldPanic::Yes, Err(_)) => TrOk,
         (&ShouldPanic::YesWithMessage(msg), Err(ref err))
             if err.downcast_ref::<String>()
-                .map(|e| &**e)
-                .or_else(|| err.downcast_ref::<&'static str>().map(|e| *e))
-                .map(|e| e.contains(msg))
-                .unwrap_or(false) => TrOk,
+               .map(|e| &**e)
+               .or_else(|| err.downcast_ref::<&'static str>().map(|e| *e))
+               .map(|e| e.contains(msg))
+               .unwrap_or(false) => TrOk,
         _ => TrFailed,
     }
 }
 
 impl MetricMap {
-
     pub fn new() -> MetricMap {
         MetricMap(BTreeMap::new())
     }
@@ -1081,7 +1155,7 @@ impl MetricMap {
     pub fn insert_metric(&mut self, name: &str, value: f64, noise: f64) {
         let m = Metric {
             value: value,
-            noise: noise
+            noise: noise,
         };
         let MetricMap(ref mut map) = *self;
         map.insert(name.to_owned(), m);
@@ -1089,10 +1163,9 @@ impl MetricMap {
 
     pub fn fmt_metrics(&self) -> String {
         let MetricMap(ref mm) = *self;
-        let v : Vec<String> = mm.iter()
-            .map(|(k,v)| format!("{}: {} (+/- {})", *k,
-                                 v.value, v.noise))
-            .collect();
+        let v: Vec<String> = mm.iter()
+                               .map(|(k, v)| format!("{}: {} (+/- {})", *k, v.value, v.noise))
+                               .collect();
         v.join(", ")
     }
 }
@@ -1109,17 +1182,21 @@ impl MetricMap {
 pub fn black_box<T>(dummy: T) -> T {
     // we need to "use" the argument in some way LLVM can't
     // introspect.
-    unsafe {asm!("" : : "r"(&dummy))}
+    unsafe { asm!("" : : "r"(&dummy)) }
     dummy
 }
 #[cfg(all(target_os = "nacl", target_arch = "le32"))]
 #[inline(never)]
-pub fn black_box<T>(dummy: T) -> T { dummy }
+pub fn black_box<T>(dummy: T) -> T {
+    dummy
+}
 
 
 impl Bencher {
     /// Callback for benchmark functions to run in their body.
-    pub fn iter<T, F>(&mut self, mut inner: F) where F: FnMut() -> T {
+    pub fn iter<T, F>(&mut self, mut inner: F)
+        where F: FnMut() -> T
+    {
         let start = Instant::now();
         let k = self.iterations;
         for _ in 0..k {
@@ -1140,13 +1217,17 @@ impl Bencher {
         }
     }
 
-    pub fn bench_n<F>(&mut self, n: u64, f: F) where F: FnOnce(&mut Bencher) {
+    pub fn bench_n<F>(&mut self, n: u64, f: F)
+        where F: FnOnce(&mut Bencher)
+    {
         self.iterations = n;
         f(self);
     }
 
     // This is a more statistics-driven benchmark algorithm
-    pub fn auto_bench<F>(&mut self, mut f: F) -> stats::Summary where F: FnMut(&mut Bencher) {
+    pub fn auto_bench<F>(&mut self, mut f: F) -> stats::Summary
+        where F: FnMut(&mut Bencher)
+    {
         // Initial bench run to get ballpark figure.
         let mut n = 1;
         self.bench_n(n, |x| f(x));
@@ -1163,17 +1244,19 @@ impl Bencher {
         // side effect of not being able to do as many runs is
         // automatically handled by the statistical analysis below
         // (i.e. larger error bars).
-        if n == 0 { n = 1; }
+        if n == 0 {
+            n = 1;
+        }
 
         let mut total_run = Duration::new(0, 0);
-        let samples : &mut [f64] = &mut [0.0_f64; 50];
+        let samples: &mut [f64] = &mut [0.0_f64; 50];
         loop {
             let loop_start = Instant::now();
 
             for p in &mut *samples {
                 self.bench_n(n, |x| f(x));
                 *p = self.ns_per_iter() as f64;
-            };
+            }
 
             stats::winsorize(samples, 5.0);
             let summ = stats::Summary::new(samples);
@@ -1181,7 +1264,7 @@ impl Bencher {
             for p in &mut *samples {
                 self.bench_n(5 * n, |x| f(x));
                 *p = self.ns_per_iter() as f64;
-            };
+            }
 
             stats::winsorize(samples, 5.0);
             let summ5 = stats::Summary::new(samples);
@@ -1189,9 +1272,8 @@ impl Bencher {
 
             // If we've run for 100ms and seem to have converged to a
             // stable median.
-            if loop_run > Duration::from_millis(100) &&
-                summ.median_abs_dev_pct < 1.0 &&
-                summ.median - summ5.median < summ5.median_abs_dev {
+            if loop_run > Duration::from_millis(100) && summ.median_abs_dev_pct < 1.0 &&
+               summ.median - summ5.median < summ5.median_abs_dev {
                 return summ5;
             }
 
@@ -1218,30 +1300,33 @@ pub mod bench {
     use std::time::Duration;
     use super::{Bencher, BenchSamples};
 
-    pub fn benchmark<F>(f: F) -> BenchSamples where F: FnMut(&mut Bencher) {
+    pub fn benchmark<F>(f: F) -> BenchSamples
+        where F: FnMut(&mut Bencher)
+    {
         let mut bs = Bencher {
             iterations: 0,
             dur: Duration::new(0, 0),
-            bytes: 0
+            bytes: 0,
         };
 
         let ns_iter_summ = bs.auto_bench(f);
 
         let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
-        let iter_s = 1_000_000_000 / ns_iter;
-        let mb_s = (bs.bytes * iter_s) / 1_000_000;
+        let mb_s = bs.bytes * 1000 / ns_iter;
 
         BenchSamples {
             ns_iter_summ: ns_iter_summ,
-            mb_s: mb_s as usize
+            mb_s: mb_s as usize,
         }
     }
 
-    pub fn run_once<F>(f: F) where F: FnOnce(&mut Bencher) {
+    pub fn run_once<F>(f: F)
+        where F: FnOnce(&mut Bencher)
+    {
         let mut bs = Bencher {
             iterations: 0,
             dur: Duration::new(0, 0),
-            bytes: 0
+            bytes: 0,
         };
         bs.bench_n(1, f);
     }
@@ -1249,22 +1334,22 @@ pub mod bench {
 
 #[cfg(test)]
 mod tests {
-    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts,
-               TestDesc, TestDescAndFn, TestOpts, run_test,
-               MetricMap,
-               StaticTestName, DynTestName, DynTestFn, ShouldPanic};
+    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts, TestDesc, TestDescAndFn,
+               TestOpts, run_test, MetricMap, StaticTestName, DynTestName, DynTestFn, ShouldPanic};
     use std::sync::mpsc::channel;
 
     #[test]
     pub fn do_not_run_ignored_tests() {
-        fn f() { panic!(); }
+        fn f() {
+            panic!();
+        }
         let desc = TestDescAndFn {
             desc: TestDesc {
                 name: StaticTestName("whatever"),
                 ignore: true,
                 should_panic: ShouldPanic::No,
             },
-            testfn: DynTestFn(Box::new(move|| f())),
+            testfn: DynTestFn(Box::new(move || f())),
         };
         let (tx, rx) = channel();
         run_test(&TestOpts::new(), false, desc, tx);
@@ -1274,14 +1359,14 @@ mod tests {
 
     #[test]
     pub fn ignored_tests_result_in_ignored() {
-        fn f() { }
+        fn f() {}
         let desc = TestDescAndFn {
             desc: TestDesc {
                 name: StaticTestName("whatever"),
                 ignore: true,
                 should_panic: ShouldPanic::No,
             },
-            testfn: DynTestFn(Box::new(move|| f())),
+            testfn: DynTestFn(Box::new(move || f())),
         };
         let (tx, rx) = channel();
         run_test(&TestOpts::new(), false, desc, tx);
@@ -1291,14 +1376,16 @@ mod tests {
 
     #[test]
     fn test_should_panic() {
-        fn f() { panic!(); }
+        fn f() {
+            panic!();
+        }
         let desc = TestDescAndFn {
             desc: TestDesc {
                 name: StaticTestName("whatever"),
                 ignore: false,
                 should_panic: ShouldPanic::Yes,
             },
-            testfn: DynTestFn(Box::new(move|| f())),
+            testfn: DynTestFn(Box::new(move || f())),
         };
         let (tx, rx) = channel();
         run_test(&TestOpts::new(), false, desc, tx);
@@ -1308,14 +1395,16 @@ mod tests {
 
     #[test]
     fn test_should_panic_good_message() {
-        fn f() { panic!("an error message"); }
+        fn f() {
+            panic!("an error message");
+        }
         let desc = TestDescAndFn {
             desc: TestDesc {
                 name: StaticTestName("whatever"),
                 ignore: false,
                 should_panic: ShouldPanic::YesWithMessage("error message"),
             },
-            testfn: DynTestFn(Box::new(move|| f())),
+            testfn: DynTestFn(Box::new(move || f())),
         };
         let (tx, rx) = channel();
         run_test(&TestOpts::new(), false, desc, tx);
@@ -1325,14 +1414,16 @@ mod tests {
 
     #[test]
     fn test_should_panic_bad_message() {
-        fn f() { panic!("an error message"); }
+        fn f() {
+            panic!("an error message");
+        }
         let desc = TestDescAndFn {
             desc: TestDesc {
                 name: StaticTestName("whatever"),
                 ignore: false,
                 should_panic: ShouldPanic::YesWithMessage("foobar"),
             },
-            testfn: DynTestFn(Box::new(move|| f())),
+            testfn: DynTestFn(Box::new(move || f())),
         };
         let (tx, rx) = channel();
         run_test(&TestOpts::new(), false, desc, tx);
@@ -1342,14 +1433,14 @@ mod tests {
 
     #[test]
     fn test_should_panic_but_succeeds() {
-        fn f() { }
+        fn f() {}
         let desc = TestDescAndFn {
             desc: TestDesc {
                 name: StaticTestName("whatever"),
                 ignore: false,
                 should_panic: ShouldPanic::Yes,
             },
-            testfn: DynTestFn(Box::new(move|| f())),
+            testfn: DynTestFn(Box::new(move || f())),
         };
         let (tx, rx) = channel();
         run_test(&TestOpts::new(), false, desc, tx);
@@ -1359,12 +1450,10 @@ mod tests {
 
     #[test]
     fn parse_ignored_flag() {
-        let args = vec!("progname".to_string(),
-                        "filter".to_string(),
-                        "--ignored".to_string());
+        let args = vec!["progname".to_string(), "filter".to_string(), "--ignored".to_string()];
         let opts = match parse_opts(&args) {
             Some(Ok(o)) => o,
-            _ => panic!("Malformed arg in parse_ignored_flag")
+            _ => panic!("Malformed arg in parse_ignored_flag"),
         };
         assert!((opts.run_ignored));
     }
@@ -1378,28 +1467,26 @@ mod tests {
         opts.run_tests = true;
         opts.run_ignored = true;
 
-        let tests = vec!(
-            TestDescAndFn {
-                desc: TestDesc {
-                    name: StaticTestName("1"),
-                    ignore: true,
-                    should_panic: ShouldPanic::No,
-                },
-                testfn: DynTestFn(Box::new(move|| {})),
-            },
-            TestDescAndFn {
-                desc: TestDesc {
-                    name: StaticTestName("2"),
-                    ignore: false,
-                    should_panic: ShouldPanic::No,
-                },
-                testfn: DynTestFn(Box::new(move|| {})),
-            });
+        let tests = vec![TestDescAndFn {
+                             desc: TestDesc {
+                                 name: StaticTestName("1"),
+                                 ignore: true,
+                                 should_panic: ShouldPanic::No,
+                             },
+                             testfn: DynTestFn(Box::new(move || {})),
+                         },
+                         TestDescAndFn {
+                             desc: TestDesc {
+                                 name: StaticTestName("2"),
+                                 ignore: false,
+                                 should_panic: ShouldPanic::No,
+                             },
+                             testfn: DynTestFn(Box::new(move || {})),
+                         }];
         let filtered = filter_tests(&opts, tests);
 
         assert_eq!(filtered.len(), 1);
-        assert_eq!(filtered[0].desc.name.to_string(),
-                   "1");
+        assert_eq!(filtered[0].desc.name.to_string(), "1");
         assert!(filtered[0].desc.ignore == false);
     }
 
@@ -1408,19 +1495,17 @@ mod tests {
         let mut opts = TestOpts::new();
         opts.run_tests = true;
 
-        let names =
-            vec!("sha1::test".to_string(),
-                 "isize::test_to_str".to_string(),
-                 "isize::test_pow".to_string(),
-                 "test::do_not_run_ignored_tests".to_string(),
-                 "test::ignored_tests_result_in_ignored".to_string(),
-                 "test::first_free_arg_should_be_a_filter".to_string(),
-                 "test::parse_ignored_flag".to_string(),
-                 "test::filter_for_ignored_option".to_string(),
-                 "test::sort_tests".to_string());
-        let tests =
-        {
-            fn testfn() { }
+        let names = vec!["sha1::test".to_string(),
+                         "isize::test_to_str".to_string(),
+                         "isize::test_pow".to_string(),
+                         "test::do_not_run_ignored_tests".to_string(),
+                         "test::ignored_tests_result_in_ignored".to_string(),
+                         "test::first_free_arg_should_be_a_filter".to_string(),
+                         "test::parse_ignored_flag".to_string(),
+                         "test::filter_for_ignored_option".to_string(),
+                         "test::sort_tests".to_string()];
+        let tests = {
+            fn testfn() {}
             let mut tests = Vec::new();
             for name in &names {
                 let test = TestDescAndFn {
@@ -1437,16 +1522,15 @@ mod tests {
         };
         let filtered = filter_tests(&opts, tests);
 
-        let expected =
-            vec!("isize::test_pow".to_string(),
-                 "isize::test_to_str".to_string(),
-                 "sha1::test".to_string(),
-                 "test::do_not_run_ignored_tests".to_string(),
-                 "test::filter_for_ignored_option".to_string(),
-                 "test::first_free_arg_should_be_a_filter".to_string(),
-                 "test::ignored_tests_result_in_ignored".to_string(),
-                 "test::parse_ignored_flag".to_string(),
-                 "test::sort_tests".to_string());
+        let expected = vec!["isize::test_pow".to_string(),
+                            "isize::test_to_str".to_string(),
+                            "sha1::test".to_string(),
+                            "test::do_not_run_ignored_tests".to_string(),
+                            "test::filter_for_ignored_option".to_string(),
+                            "test::first_free_arg_should_be_a_filter".to_string(),
+                            "test::ignored_tests_result_in_ignored".to_string(),
+                            "test::parse_ignored_flag".to_string(),
+                            "test::sort_tests".to_string()];
 
         for (a, b) in expected.iter().zip(filtered) {
             assert!(*a == b.desc.name.to_string());
index c1ba1260f67e120fdd058c04f6fbf68fc0f13b4a..335b6d67209dedcde22a3ecbcaf0a1cc0298094d 100644 (file)
@@ -111,7 +111,7 @@ pub trait Stats {
     /// is otherwise equivalent.
     ///
     /// See also: https://en.wikipedia.org/wiki/Quartile
-    fn quartiles(&self) -> (f64,f64,f64);
+    fn quartiles(&self) -> (f64, f64, f64);
 
     /// Inter-quartile range: the difference between the 25th percentile (1st quartile) and the 75th
     /// percentile (3rd quartile). See `quartiles`.
@@ -134,7 +134,7 @@ pub struct Summary {
     pub std_dev_pct: f64,
     pub median_abs_dev: f64,
     pub median_abs_dev_pct: f64,
-    pub quartiles: (f64,f64,f64),
+    pub quartiles: (f64, f64, f64),
     pub iqr: f64,
 }
 
@@ -153,7 +153,7 @@ impl Summary {
             median_abs_dev: samples.median_abs_dev(),
             median_abs_dev_pct: samples.median_abs_dev_pct(),
             quartiles: samples.quartiles(),
-            iqr: samples.iqr()
+            iqr: samples.iqr(),
         }
     }
 }
@@ -187,7 +187,7 @@ impl Stats for [f64] {
                 partials.push(x);
             } else {
                 partials[j] = x;
-                partials.truncate(j+1);
+                partials.truncate(j + 1);
             }
         }
         let zero: f64 = 0.0;
@@ -221,13 +221,13 @@ impl Stats for [f64] {
             let mut v: f64 = 0.0;
             for s in self {
                 let x = *s - mean;
-                v = v + x*x;
+                v = v + x * x;
             }
             // NB: this is _supposed to be_ len-1, not len. If you
             // change it back to len, you will be calculating a
             // population variance, not a sample variance.
             let denom = (self.len() - 1) as f64;
-            v/denom
+            v / denom
         }
     }
 
@@ -260,7 +260,7 @@ impl Stats for [f64] {
         percentile_of_sorted(&tmp, pct)
     }
 
-    fn quartiles(&self) -> (f64,f64,f64) {
+    fn quartiles(&self) -> (f64, f64, f64) {
         let mut tmp = self.to_vec();
         local_sort(&mut tmp);
         let first = 25f64;
@@ -269,11 +269,11 @@ impl Stats for [f64] {
         let b = percentile_of_sorted(&tmp, secound);
         let third = 75f64;
         let c = percentile_of_sorted(&tmp, third);
-        (a,b,c)
+        (a, b, c)
     }
 
     fn iqr(&self) -> f64 {
-        let (a,_,c) = self.quartiles();
+        let (a, _, c) = self.quartiles();
         c - a
     }
 }
@@ -299,7 +299,7 @@ fn percentile_of_sorted(sorted_samples: &[f64], pct: f64) -> f64 {
     let d = rank - lrank;
     let n = lrank as usize;
     let lo = sorted_samples[n];
-    let hi = sorted_samples[n+1];
+    let hi = sorted_samples[n + 1];
     lo + (hi - lo) * d
 }
 
@@ -316,7 +316,7 @@ pub fn winsorize(samples: &mut [f64], pct: f64) {
     local_sort(&mut tmp);
     let lo = percentile_of_sorted(&tmp, pct);
     let hundred = 100 as f64;
-    let hi = percentile_of_sorted(&tmp, hundred-pct);
+    let hi = percentile_of_sorted(&tmp, hundred - pct);
     for samp in samples {
         if *samp > hi {
             *samp = hi
@@ -380,10 +380,7 @@ mod tests {
 
     #[test]
     fn test_norm2() {
-        let val = &[
-            958.0000000000,
-            924.0000000000,
-        ];
+        let val = &[958.0000000000, 924.0000000000];
         let summ = &Summary {
             sum: 1882.0000000000,
             min: 924.0000000000,
@@ -395,25 +392,23 @@ mod tests {
             std_dev_pct: 2.5549022912,
             median_abs_dev: 25.2042000000,
             median_abs_dev_pct: 2.6784484591,
-            quartiles: (932.5000000000,941.0000000000,949.5000000000),
+            quartiles: (932.5000000000, 941.0000000000, 949.5000000000),
             iqr: 17.0000000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_norm10narrow() {
-        let val = &[
-            966.0000000000,
-            985.0000000000,
-            1110.0000000000,
-            848.0000000000,
-            821.0000000000,
-            975.0000000000,
-            962.0000000000,
-            1157.0000000000,
-            1217.0000000000,
-            955.0000000000,
-        ];
+        let val = &[966.0000000000,
+                    985.0000000000,
+                    1110.0000000000,
+                    848.0000000000,
+                    821.0000000000,
+                    975.0000000000,
+                    962.0000000000,
+                    1157.0000000000,
+                    1217.0000000000,
+                    955.0000000000];
         let summ = &Summary {
             sum: 9996.0000000000,
             min: 821.0000000000,
@@ -425,25 +420,23 @@ mod tests {
             std_dev_pct: 12.6742097933,
             median_abs_dev: 102.2994000000,
             median_abs_dev_pct: 10.5408964451,
-            quartiles: (956.7500000000,970.5000000000,1078.7500000000),
+            quartiles: (956.7500000000, 970.5000000000, 1078.7500000000),
             iqr: 122.0000000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_norm10medium() {
-        let val = &[
-            954.0000000000,
-            1064.0000000000,
-            855.0000000000,
-            1000.0000000000,
-            743.0000000000,
-            1084.0000000000,
-            704.0000000000,
-            1023.0000000000,
-            357.0000000000,
-            869.0000000000,
-        ];
+        let val = &[954.0000000000,
+                    1064.0000000000,
+                    855.0000000000,
+                    1000.0000000000,
+                    743.0000000000,
+                    1084.0000000000,
+                    704.0000000000,
+                    1023.0000000000,
+                    357.0000000000,
+                    869.0000000000];
         let summ = &Summary {
             sum: 8653.0000000000,
             min: 357.0000000000,
@@ -455,25 +448,23 @@ mod tests {
             std_dev_pct: 25.4846418487,
             median_abs_dev: 195.7032000000,
             median_abs_dev_pct: 21.4704552935,
-            quartiles: (771.0000000000,911.5000000000,1017.2500000000),
+            quartiles: (771.0000000000, 911.5000000000, 1017.2500000000),
             iqr: 246.2500000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_norm10wide() {
-        let val = &[
-            505.0000000000,
-            497.0000000000,
-            1591.0000000000,
-            887.0000000000,
-            1026.0000000000,
-            136.0000000000,
-            1580.0000000000,
-            940.0000000000,
-            754.0000000000,
-            1433.0000000000,
-        ];
+        let val = &[505.0000000000,
+                    497.0000000000,
+                    1591.0000000000,
+                    887.0000000000,
+                    1026.0000000000,
+                    136.0000000000,
+                    1580.0000000000,
+                    940.0000000000,
+                    754.0000000000,
+                    1433.0000000000];
         let summ = &Summary {
             sum: 9349.0000000000,
             min: 136.0000000000,
@@ -485,40 +476,38 @@ mod tests {
             std_dev_pct: 52.3146817750,
             median_abs_dev: 611.5725000000,
             median_abs_dev_pct: 66.9482758621,
-            quartiles: (567.2500000000,913.5000000000,1331.2500000000),
+            quartiles: (567.2500000000, 913.5000000000, 1331.2500000000),
             iqr: 764.0000000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_norm25verynarrow() {
-        let val = &[
-            991.0000000000,
-            1018.0000000000,
-            998.0000000000,
-            1013.0000000000,
-            974.0000000000,
-            1007.0000000000,
-            1014.0000000000,
-            999.0000000000,
-            1011.0000000000,
-            978.0000000000,
-            985.0000000000,
-            999.0000000000,
-            983.0000000000,
-            982.0000000000,
-            1015.0000000000,
-            1002.0000000000,
-            977.0000000000,
-            948.0000000000,
-            1040.0000000000,
-            974.0000000000,
-            996.0000000000,
-            989.0000000000,
-            1015.0000000000,
-            994.0000000000,
-            1024.0000000000,
-        ];
+        let val = &[991.0000000000,
+                    1018.0000000000,
+                    998.0000000000,
+                    1013.0000000000,
+                    974.0000000000,
+                    1007.0000000000,
+                    1014.0000000000,
+                    999.0000000000,
+                    1011.0000000000,
+                    978.0000000000,
+                    985.0000000000,
+                    999.0000000000,
+                    983.0000000000,
+                    982.0000000000,
+                    1015.0000000000,
+                    1002.0000000000,
+                    977.0000000000,
+                    948.0000000000,
+                    1040.0000000000,
+                    974.0000000000,
+                    996.0000000000,
+                    989.0000000000,
+                    1015.0000000000,
+                    994.0000000000,
+                    1024.0000000000];
         let summ = &Summary {
             sum: 24926.0000000000,
             min: 948.0000000000,
@@ -530,25 +519,23 @@ mod tests {
             std_dev_pct: 1.9888308788,
             median_abs_dev: 22.2390000000,
             median_abs_dev_pct: 2.2283567134,
-            quartiles: (983.0000000000,998.0000000000,1013.0000000000),
+            quartiles: (983.0000000000, 998.0000000000, 1013.0000000000),
             iqr: 30.0000000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_exp10a() {
-        let val = &[
-            23.0000000000,
-            11.0000000000,
-            2.0000000000,
-            57.0000000000,
-            4.0000000000,
-            12.0000000000,
-            5.0000000000,
-            29.0000000000,
-            3.0000000000,
-            21.0000000000,
-        ];
+        let val = &[23.0000000000,
+                    11.0000000000,
+                    2.0000000000,
+                    57.0000000000,
+                    4.0000000000,
+                    12.0000000000,
+                    5.0000000000,
+                    29.0000000000,
+                    3.0000000000,
+                    21.0000000000];
         let summ = &Summary {
             sum: 167.0000000000,
             min: 2.0000000000,
@@ -560,25 +547,23 @@ mod tests {
             std_dev_pct: 101.5828843560,
             median_abs_dev: 13.3434000000,
             median_abs_dev_pct: 116.0295652174,
-            quartiles: (4.2500000000,11.5000000000,22.5000000000),
+            quartiles: (4.2500000000, 11.5000000000, 22.5000000000),
             iqr: 18.2500000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_exp10b() {
-        let val = &[
-            24.0000000000,
-            17.0000000000,
-            6.0000000000,
-            38.0000000000,
-            25.0000000000,
-            7.0000000000,
-            51.0000000000,
-            2.0000000000,
-            61.0000000000,
-            32.0000000000,
-        ];
+        let val = &[24.0000000000,
+                    17.0000000000,
+                    6.0000000000,
+                    38.0000000000,
+                    25.0000000000,
+                    7.0000000000,
+                    51.0000000000,
+                    2.0000000000,
+                    61.0000000000,
+                    32.0000000000];
         let summ = &Summary {
             sum: 263.0000000000,
             min: 2.0000000000,
@@ -590,25 +575,23 @@ mod tests {
             std_dev_pct: 74.4671410520,
             median_abs_dev: 22.9803000000,
             median_abs_dev_pct: 93.7971428571,
-            quartiles: (9.5000000000,24.5000000000,36.5000000000),
+            quartiles: (9.5000000000, 24.5000000000, 36.5000000000),
             iqr: 27.0000000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_exp10c() {
-        let val = &[
-            71.0000000000,
-            2.0000000000,
-            32.0000000000,
-            1.0000000000,
-            6.0000000000,
-            28.0000000000,
-            13.0000000000,
-            37.0000000000,
-            16.0000000000,
-            36.0000000000,
-        ];
+        let val = &[71.0000000000,
+                    2.0000000000,
+                    32.0000000000,
+                    1.0000000000,
+                    6.0000000000,
+                    28.0000000000,
+                    13.0000000000,
+                    37.0000000000,
+                    16.0000000000,
+                    36.0000000000];
         let summ = &Summary {
             sum: 242.0000000000,
             min: 1.0000000000,
@@ -620,40 +603,38 @@ mod tests {
             std_dev_pct: 88.4507754589,
             median_abs_dev: 21.4977000000,
             median_abs_dev_pct: 97.7168181818,
-            quartiles: (7.7500000000,22.0000000000,35.0000000000),
+            quartiles: (7.7500000000, 22.0000000000, 35.0000000000),
             iqr: 27.2500000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_exp25() {
-        let val = &[
-            3.0000000000,
-            24.0000000000,
-            1.0000000000,
-            19.0000000000,
-            7.0000000000,
-            5.0000000000,
-            30.0000000000,
-            39.0000000000,
-            31.0000000000,
-            13.0000000000,
-            25.0000000000,
-            48.0000000000,
-            1.0000000000,
-            6.0000000000,
-            42.0000000000,
-            63.0000000000,
-            2.0000000000,
-            12.0000000000,
-            108.0000000000,
-            26.0000000000,
-            1.0000000000,
-            7.0000000000,
-            44.0000000000,
-            25.0000000000,
-            11.0000000000,
-        ];
+        let val = &[3.0000000000,
+                    24.0000000000,
+                    1.0000000000,
+                    19.0000000000,
+                    7.0000000000,
+                    5.0000000000,
+                    30.0000000000,
+                    39.0000000000,
+                    31.0000000000,
+                    13.0000000000,
+                    25.0000000000,
+                    48.0000000000,
+                    1.0000000000,
+                    6.0000000000,
+                    42.0000000000,
+                    63.0000000000,
+                    2.0000000000,
+                    12.0000000000,
+                    108.0000000000,
+                    26.0000000000,
+                    1.0000000000,
+                    7.0000000000,
+                    44.0000000000,
+                    25.0000000000,
+                    11.0000000000];
         let summ = &Summary {
             sum: 593.0000000000,
             min: 1.0000000000,
@@ -665,40 +646,38 @@ mod tests {
             std_dev_pct: 103.3565983562,
             median_abs_dev: 19.2738000000,
             median_abs_dev_pct: 101.4410526316,
-            quartiles: (6.0000000000,19.0000000000,31.0000000000),
+            quartiles: (6.0000000000, 19.0000000000, 31.0000000000),
             iqr: 25.0000000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_binom25() {
-        let val = &[
-            18.0000000000,
-            17.0000000000,
-            27.0000000000,
-            15.0000000000,
-            21.0000000000,
-            25.0000000000,
-            17.0000000000,
-            24.0000000000,
-            25.0000000000,
-            24.0000000000,
-            26.0000000000,
-            26.0000000000,
-            23.0000000000,
-            15.0000000000,
-            23.0000000000,
-            17.0000000000,
-            18.0000000000,
-            18.0000000000,
-            21.0000000000,
-            16.0000000000,
-            15.0000000000,
-            31.0000000000,
-            20.0000000000,
-            17.0000000000,
-            15.0000000000,
-        ];
+        let val = &[18.0000000000,
+                    17.0000000000,
+                    27.0000000000,
+                    15.0000000000,
+                    21.0000000000,
+                    25.0000000000,
+                    17.0000000000,
+                    24.0000000000,
+                    25.0000000000,
+                    24.0000000000,
+                    26.0000000000,
+                    26.0000000000,
+                    23.0000000000,
+                    15.0000000000,
+                    23.0000000000,
+                    17.0000000000,
+                    18.0000000000,
+                    18.0000000000,
+                    21.0000000000,
+                    16.0000000000,
+                    15.0000000000,
+                    31.0000000000,
+                    20.0000000000,
+                    17.0000000000,
+                    15.0000000000];
         let summ = &Summary {
             sum: 514.0000000000,
             min: 15.0000000000,
@@ -710,40 +689,38 @@ mod tests {
             std_dev_pct: 22.2037202539,
             median_abs_dev: 5.9304000000,
             median_abs_dev_pct: 29.6520000000,
-            quartiles: (17.0000000000,20.0000000000,24.0000000000),
+            quartiles: (17.0000000000, 20.0000000000, 24.0000000000),
             iqr: 7.0000000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_pois25lambda30() {
-        let val = &[
-            27.0000000000,
-            33.0000000000,
-            34.0000000000,
-            34.0000000000,
-            24.0000000000,
-            39.0000000000,
-            28.0000000000,
-            27.0000000000,
-            31.0000000000,
-            28.0000000000,
-            38.0000000000,
-            21.0000000000,
-            33.0000000000,
-            36.0000000000,
-            29.0000000000,
-            37.0000000000,
-            32.0000000000,
-            34.0000000000,
-            31.0000000000,
-            39.0000000000,
-            25.0000000000,
-            31.0000000000,
-            32.0000000000,
-            40.0000000000,
-            24.0000000000,
-        ];
+        let val = &[27.0000000000,
+                    33.0000000000,
+                    34.0000000000,
+                    34.0000000000,
+                    24.0000000000,
+                    39.0000000000,
+                    28.0000000000,
+                    27.0000000000,
+                    31.0000000000,
+                    28.0000000000,
+                    38.0000000000,
+                    21.0000000000,
+                    33.0000000000,
+                    36.0000000000,
+                    29.0000000000,
+                    37.0000000000,
+                    32.0000000000,
+                    34.0000000000,
+                    31.0000000000,
+                    39.0000000000,
+                    25.0000000000,
+                    31.0000000000,
+                    32.0000000000,
+                    40.0000000000,
+                    24.0000000000];
         let summ = &Summary {
             sum: 787.0000000000,
             min: 21.0000000000,
@@ -755,40 +732,38 @@ mod tests {
             std_dev_pct: 16.3814245145,
             median_abs_dev: 5.9304000000,
             median_abs_dev_pct: 18.5325000000,
-            quartiles: (28.0000000000,32.0000000000,34.0000000000),
+            quartiles: (28.0000000000, 32.0000000000, 34.0000000000),
             iqr: 6.0000000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_pois25lambda40() {
-        let val = &[
-            42.0000000000,
-            50.0000000000,
-            42.0000000000,
-            46.0000000000,
-            34.0000000000,
-            45.0000000000,
-            34.0000000000,
-            49.0000000000,
-            39.0000000000,
-            28.0000000000,
-            40.0000000000,
-            35.0000000000,
-            37.0000000000,
-            39.0000000000,
-            46.0000000000,
-            44.0000000000,
-            32.0000000000,
-            45.0000000000,
-            42.0000000000,
-            37.0000000000,
-            48.0000000000,
-            42.0000000000,
-            33.0000000000,
-            42.0000000000,
-            48.0000000000,
-        ];
+        let val = &[42.0000000000,
+                    50.0000000000,
+                    42.0000000000,
+                    46.0000000000,
+                    34.0000000000,
+                    45.0000000000,
+                    34.0000000000,
+                    49.0000000000,
+                    39.0000000000,
+                    28.0000000000,
+                    40.0000000000,
+                    35.0000000000,
+                    37.0000000000,
+                    39.0000000000,
+                    46.0000000000,
+                    44.0000000000,
+                    32.0000000000,
+                    45.0000000000,
+                    42.0000000000,
+                    37.0000000000,
+                    48.0000000000,
+                    42.0000000000,
+                    33.0000000000,
+                    42.0000000000,
+                    48.0000000000];
         let summ = &Summary {
             sum: 1019.0000000000,
             min: 28.0000000000,
@@ -800,40 +775,38 @@ mod tests {
             std_dev_pct: 14.3978417577,
             median_abs_dev: 5.9304000000,
             median_abs_dev_pct: 14.1200000000,
-            quartiles: (37.0000000000,42.0000000000,45.0000000000),
+            quartiles: (37.0000000000, 42.0000000000, 45.0000000000),
             iqr: 8.0000000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_pois25lambda50() {
-        let val = &[
-            45.0000000000,
-            43.0000000000,
-            44.0000000000,
-            61.0000000000,
-            51.0000000000,
-            53.0000000000,
-            59.0000000000,
-            52.0000000000,
-            49.0000000000,
-            51.0000000000,
-            51.0000000000,
-            50.0000000000,
-            49.0000000000,
-            56.0000000000,
-            42.0000000000,
-            52.0000000000,
-            51.0000000000,
-            43.0000000000,
-            48.0000000000,
-            48.0000000000,
-            50.0000000000,
-            42.0000000000,
-            43.0000000000,
-            42.0000000000,
-            60.0000000000,
-        ];
+        let val = &[45.0000000000,
+                    43.0000000000,
+                    44.0000000000,
+                    61.0000000000,
+                    51.0000000000,
+                    53.0000000000,
+                    59.0000000000,
+                    52.0000000000,
+                    49.0000000000,
+                    51.0000000000,
+                    51.0000000000,
+                    50.0000000000,
+                    49.0000000000,
+                    56.0000000000,
+                    42.0000000000,
+                    52.0000000000,
+                    51.0000000000,
+                    43.0000000000,
+                    48.0000000000,
+                    48.0000000000,
+                    50.0000000000,
+                    42.0000000000,
+                    43.0000000000,
+                    42.0000000000,
+                    60.0000000000];
         let summ = &Summary {
             sum: 1235.0000000000,
             min: 42.0000000000,
@@ -845,40 +818,38 @@ mod tests {
             std_dev_pct: 11.3913245723,
             median_abs_dev: 4.4478000000,
             median_abs_dev_pct: 8.8956000000,
-            quartiles: (44.0000000000,50.0000000000,52.0000000000),
+            quartiles: (44.0000000000, 50.0000000000, 52.0000000000),
             iqr: 8.0000000000,
         };
         check(val, summ);
     }
     #[test]
     fn test_unif25() {
-        let val = &[
-            99.0000000000,
-            55.0000000000,
-            92.0000000000,
-            79.0000000000,
-            14.0000000000,
-            2.0000000000,
-            33.0000000000,
-            49.0000000000,
-            3.0000000000,
-            32.0000000000,
-            84.0000000000,
-            59.0000000000,
-            22.0000000000,
-            86.0000000000,
-            76.0000000000,
-            31.0000000000,
-            29.0000000000,
-            11.0000000000,
-            41.0000000000,
-            53.0000000000,
-            45.0000000000,
-            44.0000000000,
-            98.0000000000,
-            98.0000000000,
-            7.0000000000,
-        ];
+        let val = &[99.0000000000,
+                    55.0000000000,
+                    92.0000000000,
+                    79.0000000000,
+                    14.0000000000,
+                    2.0000000000,
+                    33.0000000000,
+                    49.0000000000,
+                    3.0000000000,
+                    32.0000000000,
+                    84.0000000000,
+                    59.0000000000,
+                    22.0000000000,
+                    86.0000000000,
+                    76.0000000000,
+                    31.0000000000,
+                    29.0000000000,
+                    11.0000000000,
+                    41.0000000000,
+                    53.0000000000,
+                    45.0000000000,
+                    44.0000000000,
+                    98.0000000000,
+                    98.0000000000,
+                    7.0000000000];
         let summ = &Summary {
             sum: 1242.0000000000,
             min: 2.0000000000,
@@ -890,7 +861,7 @@ mod tests {
             std_dev_pct: 64.1488719719,
             median_abs_dev: 45.9606000000,
             median_abs_dev_pct: 102.1346666667,
-            quartiles: (29.0000000000,45.0000000000,79.0000000000),
+            quartiles: (29.0000000000, 45.0000000000, 79.0000000000),
             iqr: 50.0000000000,
         };
         check(val, summ);
@@ -920,7 +891,7 @@ mod bench {
     #[bench]
     pub fn sum_many_f64(b: &mut Bencher) {
         let nums = [-1e30f64, 1e60, 1e30, 1.0, -1e60];
-        let v = (0..500).map(|i| nums[i%5]).collect::<Vec<_>>();
+        let v = (0..500).map(|i| nums[i % 5]).collect::<Vec<_>>();
 
         b.iter(|| {
             v.sum();
diff --git a/src/rt/rust_android_dummy.c b/src/rt/rust_android_dummy.c
deleted file mode 100644 (file)
index c322dc6..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifdef __ANDROID__
-
-#include "rust_android_dummy.h"
-#include <math.h>
-#include <errno.h>
-
-int backtrace(void **array, int size) { return 0; }
-
-char **backtrace_symbols(void *const *array, int size) { return 0; }
-
-void backtrace_symbols_fd (void *const *array, int size, int fd) {}
-
-volatile int* __errno_location() {
-    return &errno;
-}
-
-float log2f(float f)
-{
-    return logf( f ) / logf( 2 );
-}
-
-double log2( double n )
-{
-    return log( n ) / log( 2 );
-}
-
-double exp10( double x )
-{
-    return pow( 10, x );
-}
-
-void telldir()
-{
-}
-
-void seekdir()
-{
-}
-
-void mkfifo()
-{
-}
-
-void abs()
-{
-}
-
-void labs()
-{
-}
-
-void rand()
-{
-}
-
-void srand()
-{
-}
-
-void atof()
-{
-}
-
-int glob(const char *pattern,
-                    int flags,
-                    int (*errfunc) (const char *epath, int eerrno),
-                    glob_t *pglob)
-{
-    return 0;
-}
-
-void globfree(glob_t *pglob)
-{
-}
-
-int pthread_atfork(void (*prefork)(void),
-                              void (*postfork_parent)(void),
-                              void (*postfork_child)(void))
-{
-    return 0;
-}
-
-int mlockall(int flags)
-{
-    return 0;
-}
-
-int munlockall(void)
-{
-    return 0;
-}
-
-int shm_open(const char *name, int oflag, mode_t mode)
-{
-    return 0;
-}
-
-int shm_unlink(const char *name)
-{
-    return 0;
-}
-
-int posix_madvise(void *addr, size_t len, int advice)
-{
-    return 0;
-}
-
-#endif
diff --git a/src/rt/rust_android_dummy.h b/src/rt/rust_android_dummy.h
deleted file mode 100644 (file)
index d2329a4..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef _RUST_ANDROID_DUMMY_H
-#define _RUST_ANDROID_DUMMY_H
-
-int backtrace (void **__array, int __size);
-
-char **backtrace_symbols (void *__const *__array, int __size);
-
-void backtrace_symbols_fd (void *__const *__array, int __size, int __fd);
-
-#include <sys/types.h>
-
-struct stat;
-typedef struct {
-    size_t gl_pathc;    /* Count of total paths so far. */
-    size_t gl_matchc;   /* Count of paths matching pattern. */
-    size_t gl_offs;     /* Reserved at beginning of gl_pathv. */
-    int gl_flags;       /* Copy of flags parameter to glob. */
-    char **gl_pathv;    /* List of paths matching pattern. */
-                /* Copy of errfunc parameter to glob. */
-    int (*gl_errfunc)(const char *, int);
-
-    /*
-     * Alternate filesystem access methods for glob; replacement
-     * versions of closedir(3), readdir(3), opendir(3), stat(2)
-     * and lstat(2).
-     */
-    void (*gl_closedir)(void *);
-    struct dirent *(*gl_readdir)(void *);
-    void *(*gl_opendir)(const char *);
-    int (*gl_lstat)(const char *, struct stat *);
-} glob_t;
-
-#endif
diff --git a/src/rt/rust_builtin.c b/src/rt/rust_builtin.c
deleted file mode 100644 (file)
index af15bda..0000000
+++ /dev/null
@@ -1,470 +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.
-
-#if !defined(_WIN32)
-
-#include <stdint.h>
-#include <time.h>
-#include <string.h>
-#include <assert.h>
-#include <stdlib.h>
-
-
-#include <dirent.h>
-#include <pthread.h>
-#include <signal.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#ifdef __APPLE__
-#include <TargetConditionals.h>
-#include <mach/mach_time.h>
-
-#if !(TARGET_OS_IPHONE)
-#include <crt_externs.h>
-#endif
-#endif
-
-char*
-rust_list_dir_val(struct dirent* entry_ptr) {
-    return entry_ptr->d_name;
-}
-
-// Android's struct dirent does have d_type from the very beginning
-// (android-3). _DIRENT_HAVE_D_TYPE is not defined all the way to android-21
-// though...
-#if defined(__ANDROID__)
-# define _DIRENT_HAVE_D_TYPE
-#endif
-
-int
-rust_dir_get_mode(struct dirent* entry_ptr) {
-#if defined(_DIRENT_HAVE_D_TYPE) || defined(__APPLE__)
-    switch (entry_ptr->d_type) {
-        case DT_BLK: return S_IFBLK;
-        case DT_CHR: return S_IFCHR;
-        case DT_FIFO: return S_IFIFO;
-        case DT_LNK: return S_IFLNK;
-        case DT_REG: return S_IFREG;
-        case DT_SOCK: return S_IFSOCK;
-        case DT_DIR: return S_IFDIR;
-    }
-#endif
-    return -1;
-}
-
-ino_t
-rust_dir_get_ino(struct dirent* entry_ptr) {
-    return entry_ptr->d_ino;
-}
-
-DIR*
-rust_opendir(char *dirname) {
-    return opendir(dirname);
-}
-
-int
-rust_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) {
-    return readdir_r(dirp, entry, result);
-}
-
-int
-rust_dirent_t_size() {
-    return sizeof(struct dirent);
-}
-
-#if defined(__BSD__)
-static int
-get_num_cpus() {
-    /* swiped from http://stackoverflow.com/questions/150355/
-       programmatically-find-the-number-of-cores-on-a-machine */
-
-    unsigned int numCPU;
-    int mib[4];
-    size_t len = sizeof(numCPU);
-
-    /* set the mib for hw.ncpu */
-    mib[0] = CTL_HW;
-    mib[1] = HW_AVAILCPU;  // alternatively, try HW_NCPU;
-
-    /* get the number of CPUs from the system */
-    sysctl(mib, 2, &numCPU, &len, NULL, 0);
-
-    if( numCPU < 1 ) {
-        mib[1] = HW_NCPU;
-        sysctl( mib, 2, &numCPU, &len, NULL, 0 );
-
-        if( numCPU < 1 ) {
-            numCPU = 1;
-        }
-    }
-    return numCPU;
-}
-#elif defined(__GNUC__)
-static int
-get_num_cpus() {
-    return sysconf(_SC_NPROCESSORS_ONLN);
-}
-#endif
-
-uintptr_t
-rust_get_num_cpus() {
-    return get_num_cpus();
-}
-
-#if defined(__DragonFly__)
-#include <errno.h>
-// In DragonFly __error() is an inline function and as such
-// no symbol exists for it.
-int *__dfly_error(void) { return __error(); }
-#endif
-
-#if defined(__Bitrig__)
-#include <stdio.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <limits.h>
-
-int rust_get_path(void *p, size_t* sz)
-{
-  int mib[4];
-  char *eq = NULL;
-  char *key = NULL;
-  char *val = NULL;
-  char **menv = NULL;
-  size_t maxlen, len;
-  int nenv = 0;
-  int i;
-
-  if ((p == NULL) && (sz == NULL))
-    return -1;
-
-  /* get the argv array */
-  mib[0] = CTL_KERN;
-  mib[1] = KERN_PROC_ARGS;
-  mib[2] = getpid();
-  mib[3] = KERN_PROC_ENV;
-
-  /* get the number of bytes needed to get the env */
-  maxlen = 0;
-  if (sysctl(mib, 4, NULL, &maxlen, NULL, 0) == -1)
-    return -1;
-
-  /* allocate the buffer */
-  if ((menv = calloc(maxlen, sizeof(char))) == NULL)
-    return -1;
-
-  /* get the env array */
-  if (sysctl(mib, 4, menv, &maxlen, NULL, 0) == -1)
-  {
-    free(menv);
-    return -1;
-  }
-
-  mib[3] = KERN_PROC_NENV;
-  len = sizeof(int);
-  /* get the length of env array */
-  if (sysctl(mib, 4, &nenv, &len, NULL, 0) == -1)
-  {
-    free(menv);
-    return -1;
-  }
-
-  /* find _ key and resolve the value */
-  for (i = 0; i < nenv; i++)
-  {
-    if ((eq = strstr(menv[i], "=")) == NULL)
-      continue;
-
-    key = menv[i];
-    val = eq + 1;
-    *eq = '\0';
-
-    if (strncmp(key, "PATH", maxlen) != 0)
-      continue;
-
-    if (p == NULL)
-    {
-      /* return the length of the value + NUL */
-      *sz = strnlen(val, maxlen) + 1;
-      free(menv);
-      return 0;
-    }
-    else
-    {
-      /* copy *sz bytes to the output buffer */
-      memcpy(p, val, *sz);
-      free(menv);
-      return 0;
-    }
-  }
-
-  free(menv);
-  return -1;
-}
-
-int rust_get_path_array(void * p, size_t * sz)
-{
-  char *path, *str;
-  char **buf;
-  int i, num;
-  size_t len;
-
-  if ((p == NULL) && (sz == NULL))
-    return -1;
-
-  /* get the length of the PATH value */
-  if (rust_get_path(NULL, &len) == -1)
-    return -1;
-
-  if (len == 0)
-    return -1;
-
-  /* allocate the buffer */
-  if ((path = calloc(len, sizeof(char))) == NULL)
-    return -1;
-
-  /* get the PATH value */
-  if (rust_get_path(path, &len) == -1)
-  {
-    free(path);
-    return -1;
-  }
-
-  /* count the number of parts in the PATH */
-  num = 1;
-  for(str = path; *str != '\0'; str++)
-  {
-    if (*str == ':')
-      num++;
-  }
-
-  /* calculate the size of the buffer for the 2D array */
-  len = (num * sizeof(char*) + 1) + strlen(path) + 1;
-
-  if (p == NULL)
-  {
-    free(path);
-    *sz = len;
-    return 0;
-  }
-
-  /* make sure we have enough buffer space */
-  if (*sz < len)
-  {
-    free(path);
-    return -1;
-  }
-
-  /* zero out the buffer */
-  buf = (char**)p;
-  memset(buf, 0, *sz);
-
-  /* copy the data into the right place */
-  str = p + ((num+1) * sizeof(char*));
-  memcpy(str, path, strlen(path));
-
-  /* parse the path into it's parts */
-  for (i = 0; i < num && (buf[i] = strsep(&str, ":")) != NULL; i++) {;}
-  buf[num] = NULL;
-
-  free(path);
-  return 0;
-}
-
-int rust_get_argv_zero(void* p, size_t* sz)
-{
-  int mib[4];
-  char **argv = NULL;
-  size_t len;
-
-  if ((p == NULL) && (sz == NULL))
-    return -1;
-
-  /* get the argv array */
-  mib[0] = CTL_KERN;
-  mib[1] = KERN_PROC_ARGS;
-  mib[2] = getpid();
-  mib[3] = KERN_PROC_ARGV;
-
-  /* request KERN_PROC_ARGV size */
-  len = 0;
-  if (sysctl(mib, 4, NULL, &len, NULL, 0) == -1)
-    return -1;
-
-  /* allocate buffer to receive the values */
-  if ((argv = malloc(len)) == NULL)
-    return -1;
-
-  /* get the argv array */
-  if (sysctl(mib, 4, argv, &len, NULL, 0) == -1)
-  {
-    free(argv);
-    return -1;
-  }
-
-  /* get length of argv[0] */
-  len = strnlen(argv[0], len) + 1;
-
-  if (p == NULL)
-  {
-    *sz = len;
-    free(argv);
-    return 0;
-  }
-
-  if (*sz < len)
-  {
-    free(argv);
-    return -1;
-  }
-
-  memcpy(p, argv[0], len);
-  free(argv);
-  return 0;
-}
-
-const char * rust_current_exe()
-{
-  static char *self = NULL;
-  char *argv0;
-  char **paths;
-  size_t sz;
-  int i;
-  /* If `PATH_MAX` is defined on the platform, `realpath` will truncate the
-   * resolved path up to `PATH_MAX`. While this can make the resolution fail if
-   * the executable is placed in a deep path, the usage of a buffer whose
-   * length depends on `PATH_MAX` is still memory safe. */
-  char buf[2*PATH_MAX], exe[PATH_MAX];
-
-  if (self != NULL)
-    return self;
-
-  if (rust_get_argv_zero(NULL, &sz) == -1)
-    return NULL;
-  if ((argv0 = calloc(sz, sizeof(char))) == NULL)
-    return NULL;
-  if (rust_get_argv_zero(argv0, &sz) == -1)
-  {
-    free(argv0);
-    return NULL;
-  }
-
-  /* if argv0 is a relative or absolute path, resolve it with realpath */
-  if ((*argv0 == '.') || (*argv0 == '/') || (strstr(argv0, "/") != NULL))
-  {
-    self = realpath(argv0, NULL);
-    free(argv0);
-    return self;
-  }
-
-  /* get the path array */
-  if (rust_get_path_array(NULL, &sz) == -1)
-  {
-    free(argv0);
-    return NULL;
-  }
-  if ((paths = calloc(sz, sizeof(char))) == NULL)
-  {
-    free(argv0);
-    return NULL;
-  }
-  if (rust_get_path_array(paths, &sz) == -1)
-  {
-    free(argv0);
-    free(paths);
-    return NULL;
-  }
-
-  for(i = 0; paths[i] != NULL; i++)
-  {
-    snprintf(buf, 2*PATH_MAX, "%s/%s", paths[i], argv0);
-    if (realpath(buf, exe) == NULL)
-      continue;
-
-    if (access(exe, F_OK | X_OK) == -1)
-      continue;
-
-    self = strdup(exe);
-    free(argv0);
-    free(paths);
-    return self;
-  }
-
-  free(argv0);
-  free(paths);
-  return NULL;
-}
-
-#elif defined(__OpenBSD__)
-
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <limits.h>
-
-const char * rust_current_exe() {
-    static char *self = NULL;
-
-    if (self == NULL) {
-        int mib[4];
-        char **argv = NULL;
-        size_t argv_len;
-
-        /* initialize mib */
-        mib[0] = CTL_KERN;
-        mib[1] = KERN_PROC_ARGS;
-        mib[2] = getpid();
-        mib[3] = KERN_PROC_ARGV;
-
-        /* request KERN_PROC_ARGV size */
-        argv_len = 0;
-        if (sysctl(mib, 4, NULL, &argv_len, NULL, 0) == -1)
-            return (NULL);
-
-        /* allocate size */
-        if ((argv = malloc(argv_len)) == NULL)
-            return (NULL);
-
-        /* request KERN_PROC_ARGV */
-        if (sysctl(mib, 4, argv, &argv_len, NULL, 0) == -1) {
-            free(argv);
-            return (NULL);
-        }
-
-        /* get realpath if possible */
-        if ((argv[0] != NULL) && ((*argv[0] == '.') || (*argv[0] == '/')
-                                || (strstr(argv[0], "/") != NULL)))
-
-            self = realpath(argv[0], NULL);
-        else
-            self = NULL;
-
-        /* cleanup */
-        free(argv);
-    }
-
-    return (self);
-}
-
-#endif
-
-#endif // !defined(_WIN32)
-
-//
-// Local Variables:
-// mode: C++
-// fill-column: 78;
-// indent-tabs-mode: nil
-// c-basic-offset: 4
-// buffer-file-coding-system: utf-8-unix
-// End:
-//
index f7895d694c8246923e15c57a8839c0811ff62ff6..320cd3dbd851a4e638f5c008da30c4de74413c46 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <stdint.h>
 #include <assert.h>
+#include <stdarg.h>
 
 // These functions are used in the unit tests for C ABI calls.
 
@@ -222,3 +223,18 @@ uint64_t get_z(struct S s) {
 uint64_t get_c_many_params(void *a, void *b, void *c, void *d, struct quad f) {
     return f.c;
 }
+
+// Calculates the average of `(x + y) / n` where x: i64, y: f64. There must be exactly n pairs
+// passed as variadic arguments.
+double rust_interesting_average(uint64_t n, ...) {
+    va_list pairs;
+    double sum = 0.0;
+    int i;
+    va_start(pairs, n);
+    for(i = 0; i < n; i += 1) {
+        sum += (double)va_arg(pairs, int64_t);
+        sum += va_arg(pairs, double);
+    }
+    va_end(pairs);
+    return sum / n;
+}
index a81eac279d062bc576493c0d16fa73771c4e2e5a..d1b6fe6655ac6619851b9e1b2fad8bc5f5f9c597 100644 (file)
@@ -22,8 +22,6 @@
 // object (usually called `crtX.o), which then invokes initialization callbacks
 // of other runtime components (registered via yet another special image section).
 
-#![cfg_attr(stage0, feature(no_std))]
-
 #![crate_type="rlib"]
 #![no_std]
 #![allow(non_camel_case_types)]
index e3b691ce2f7dbbfd4be19ac0000e66cfbe4e36ab..5e4e13ebd05e4ea13c994a50f4e8275a2aee5843 100644 (file)
@@ -10,8 +10,6 @@
 
 // See rsbegin.rs for details.
 
-#![cfg_attr(stage0, feature(no_std))]
-
 #![crate_type="rlib"]
 #![no_std]
 
index e14c3346cc155f83a972e62d868e943983ba57b2..36a37dba1fa0f7cf4df405b9d520aa7057551805 100644 (file)
@@ -126,9 +126,9 @@ pub fn parse_summary(input: &mut Read, src: &Path) -> Result<Book, Vec<String>>
         let title = line[start_bracket + 1..end_bracket].to_string();
         let indent = &line[..star_idx];
 
-        let path_from_root = match src.join(given_path).relative_from(src) {
-            Some(p) => p.to_path_buf(),
-            None => {
+        let path_from_root = match src.join(given_path).strip_prefix(src) {
+            Ok(p) => p.to_path_buf(),
+            Err(..) => {
                 errors.push(format!("paths in SUMMARY.md must be relative, \
                                      but path '{}' for section '{}' is not.",
                                      given_path, title));
index d23e868eeadd192fc649763d79810881f10164d0..e8345dc9586d179f2ac740e5b72b5729787daac3 100644 (file)
@@ -11,7 +11,6 @@
 #![deny(warnings)]
 
 #![feature(iter_arith)]
-#![feature(path_relative_from)]
 #![feature(rustc_private)]
 #![feature(rustdoc)]
 
index c59170d523d7a33747816ea88bcf7a5d6fdca868..f64c9d36025a3f847d6bcb97285567e13c8c8576 100644 (file)
@@ -1,3 +1,16 @@
+S 2015-12-18 3391630
+  bitrig-x86_64 6476e1562df02389b55553b4c88b1f4fd121cd40
+  freebsd-i386 7e624c50494402e1feb14c743d659fbd71b448f5
+  freebsd-x86_64 91724d4e655807a2a2e940ac50992ebeaac16ea9
+  dragonfly-x86_64 e74d79488e88ac2de3bd03afd5959d2ae6e2b628
+  linux-i386 a09c4a4036151d0cb28e265101669731600e01f2
+  linux-x86_64 97e2a5eb8904962df8596e95d6e5d9b574d73bf4
+  macos-i386 ca52d2d3ba6497ed007705ee3401cf7efc136ca1
+  macos-x86_64 3c44ffa18f89567c2b81f8d695e711c86d81ffc7
+  openbsd-x86_64 6c8aab2c8a169274942f9a15e460069a3ff64be9
+  winnt-i386 f9056ebd3db9611d31c2dc6dc5f96c7208d5d227
+  winnt-x86_64 a85a40e535d828016181d3aa40afe34c3e36ab8c
+
 S 2015-08-11 1af31d4
   bitrig-x86_64 739e0635cd5a1b3635f1457aae3ef6390ea9a7a8
   freebsd-i386 3cd4a44fb97b3135be3d1b760bea604a381e85dc
index b3960c2707b4b81c71a928c867837e64396e6518..175e8730cbcd3f8ccbed893ace5b4d98dd8d1641 100644 (file)
@@ -13,7 +13,7 @@
 
 #![crate_type="lib"]
 
-pub trait Bar {
+pub trait Bar: Sized {
     type T;
 
     fn get(x: Option<Self>) -> <Self as Bar>::T;
index 542b10fd1c68c396b83af758252288e73efd3965..a3f42edbed2a88a761a86c72d35a5a7b75847815 100644 (file)
@@ -15,6 +15,7 @@
 #![feature(rustc_private)]
 
 extern crate syntax;
+extern crate syntax_ext;
 extern crate rustc;
 extern crate rustc_plugin;
 
@@ -22,10 +23,10 @@ use syntax::ast;
 use syntax::codemap::Span;
 use syntax::ext::base::{MultiDecorator, ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
-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::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 rustc_plugin::Registry;
 
 #[plugin_registrar]
index f44e77d563a9e8b018b6c1c12db9e6cb0de7f298..fe12d3b1f080f4450abb2e2f9b8c10a21ba3e519 100644 (file)
@@ -15,6 +15,7 @@
 #![feature(rustc_private)]
 
 extern crate syntax;
+extern crate syntax_ext;
 extern crate rustc;
 extern crate rustc_plugin;
 
@@ -23,11 +24,11 @@ use syntax::attr::AttrMetaMethods;
 use syntax::codemap::Span;
 use syntax::ext::base::{MultiDecorator, ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
-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::parse::token;
 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 rustc_plugin::Registry;
 
 #[plugin_registrar]
index 270cfdcb7f651cec9d893d5f9d22da99d97c8719..4bd8ecacb96b3ddcb5e86e42654dd18cfe6c0fa9 100644 (file)
@@ -10,6 +10,7 @@
 
 #![crate_type = "lib"]
 #![crate_name = "default_param_test"]
+#![feature(default_type_parameter_fallback)]
 
 use std::marker::PhantomData;
 
diff --git a/src/test/auxiliary/deprecation-lint.rs b/src/test/auxiliary/deprecation-lint.rs
new file mode 100644 (file)
index 0000000..ff872ef
--- /dev/null
@@ -0,0 +1,90 @@
+// 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.
+
+#![feature(deprecated)]
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub fn deprecated() {}
+#[deprecated(since = "1.0.0", note = "text")]
+pub fn deprecated_text() {}
+
+pub struct MethodTester;
+
+impl MethodTester {
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub fn method_deprecated(&self) {}
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub fn method_deprecated_text(&self) {}
+}
+
+pub trait Trait {
+    #[deprecated(since = "1.0.0", note = "text")]
+    fn trait_deprecated(&self) {}
+    #[deprecated(since = "1.0.0", note = "text")]
+    fn trait_deprecated_text(&self) {}
+}
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub trait DeprecatedTrait { fn dummy(&self) { } }
+
+impl Trait for MethodTester {}
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub struct DeprecatedStruct {
+    pub i: isize
+}
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub struct DeprecatedUnitStruct;
+
+pub enum Enum {
+    #[deprecated(since = "1.0.0", note = "text")]
+    DeprecatedVariant,
+}
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub struct DeprecatedTupleStruct(pub isize);
+
+pub struct Stable {
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub override2: u8,
+}
+
+pub struct Stable2(pub u8, pub u8, #[deprecated(since = "1.0.0", note = "text")] pub u8);
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub struct Deprecated {
+    pub inherit: u8,
+}
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub struct Deprecated2(pub u8,
+                       pub u8,
+                       pub u8);
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub mod deprecated_mod {
+    pub fn deprecated() {}
+}
+
+#[macro_export]
+macro_rules! macro_test {
+    () => (deprecated());
+}
+
+#[macro_export]
+macro_rules! macro_test_arg {
+    ($func:expr) => ($func);
+}
+
+#[macro_export]
+macro_rules! macro_test_arg_nested {
+    ($func:ident) => (macro_test_arg!($func()));
+}
diff --git a/src/test/auxiliary/empty-struct.rs b/src/test/auxiliary/empty-struct.rs
new file mode 100644 (file)
index 0000000..3b92bc3
--- /dev/null
@@ -0,0 +1,19 @@
+// 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.
+
+#![feature(braced_empty_structs)]
+
+pub struct XEmpty1 {}
+pub struct XEmpty2;
+
+pub enum XE {
+    XEmpty3 {},
+    XEmpty4,
+}
index 22cbc415eb4cef9849a1366e017b5cf9236122a4..55a4226c6632da8685dd5c58fd556c6469e79834 100644 (file)
@@ -24,7 +24,7 @@ pub extern "win64" fn foo(a: isize, b: isize, c: isize, d: isize) {
 }
 
 #[inline(never)]
-#[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "aarch64"))]
+#[cfg(not(target_arch = "x86_64"))]
 pub extern fn foo(a: isize, b: isize, c: isize, d: isize) {
     assert_eq!(a, 1);
     assert_eq!(b, 2);
index e57c6dc718492239192883f86bdf39186cabb0ed..3d777d01d502733a3c8bd539faff6d8d011a30a0 100644 (file)
@@ -13,7 +13,7 @@
 
 use std::marker;
 
-struct arc_destruct<T: Sync> {
+pub struct arc_destruct<T: Sync> {
     _data: isize,
     _marker: marker::PhantomData<T>
 }
@@ -37,7 +37,7 @@ fn init() -> arc_destruct<context_res> {
     arc(context_res())
 }
 
-struct context_res {
+pub struct context_res {
     ctx : isize,
 }
 
index 09d8302095f3d9bfcdd693d9cf98a1aed6750657..3100aba4b72be3cfa027c52d61feec6bb0f67470 100644 (file)
@@ -98,6 +98,12 @@ impl Trait for MethodTester {}
 #[unstable(feature = "test_feature", issue = "0")]
 pub trait UnstableTrait { fn dummy(&self) { } }
 
+#[stable(feature = "test_feature", since = "1.0.0")]
+#[rustc_deprecated(since = "1.0.0", reason = "text")]
+pub trait DeprecatedTrait {
+    #[stable(feature = "test_feature", since = "1.0.0")] fn dummy(&self) { }
+}
+
 #[stable(feature = "test_feature", since = "1.0.0")]
 #[rustc_deprecated(since = "1.0.0", reason = "text")]
 pub struct DeprecatedStruct {
diff --git a/src/test/auxiliary/mir_external_refs.rs b/src/test/auxiliary/mir_external_refs.rs
new file mode 100644 (file)
index 0000000..4cad980
--- /dev/null
@@ -0,0 +1,28 @@
+// 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.
+
+
+pub struct S(pub u8);
+
+impl S {
+    pub fn hey() -> u8 { 24 }
+}
+
+pub trait X {
+    fn hoy(&self) -> u8 { 25 }
+}
+
+impl X for S {}
+
+pub enum E {
+    U(u8)
+}
+
+pub fn regular_fn() -> u8 { 12 }
index e61fb49add5b1e7aa5f53082113d483488178d3c..b8fd59bf7037df002c7fd0131cfa3d51261933cd 100644 (file)
@@ -11,7 +11,7 @@
 #![crate_name="static_methods_crate"]
 #![crate_type = "lib"]
 
-pub trait read {
+pub trait read: Sized {
     fn readMaybe(s: String) -> Option<Self>;
 }
 
diff --git a/src/test/bench/shootout-mandelbrot.rs b/src/test/bench/shootout-mandelbrot.rs
deleted file mode 100644 (file)
index 21ac232..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-// The Computer Language Benchmarks Game
-// http://benchmarksgame.alioth.debian.org/
-//
-// contributed by the Rust Project Developers
-
-// Copyright (c) 2012-2014 The Rust Project Developers
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-//
-// - Redistributions of source code must retain the above copyright
-//   notice, this list of conditions and the following disclaimer.
-//
-// - Redistributions in binary form must reproduce the above copyright
-//   notice, this list of conditions and the following disclaimer in
-//   the documentation and/or other materials provided with the
-//   distribution.
-//
-// - Neither the name of "The Computer Language Benchmarks Game" nor
-//   the name of "The Computer Language Shootout Benchmarks" nor the
-//   names of its contributors may be used to endorse or promote
-//   products derived from this software without specific prior
-//   written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-// OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#![feature(core_simd, core)]
-
-// ignore-pretty very bad with line comments
-
-use std::env;
-use std::io::prelude::*;
-use std::io;
-use std::simd::f64x2;
-use std::sync::Arc;
-use std::thread;
-
-const ITER: usize = 50;
-const LIMIT: f64 = 2.0;
-const WORKERS: usize = 16;
-
-fn mandelbrot<W: Write>(w: usize, mut out: W) -> io::Result<()> {
-    assert_eq!(WORKERS % 2, 0);
-
-    // Ensure w and h are multiples of 8.
-    let w = (w + 7) / 8 * 8;
-    let h = w;
-
-    let chunk_size = h / WORKERS;
-
-    // Account for remainders in workload division, e.g. 1000 / 16 = 62.5
-    let last_chunk_size = if h % WORKERS != 0 {
-        chunk_size + h % WORKERS
-    } else {
-        chunk_size
-    };
-
-    // precalc values
-    let inverse_w_doubled = 2.0 / w as f64;
-    let inverse_h_doubled = 2.0 / h as f64;
-    let v_inverses = f64x2(inverse_w_doubled, inverse_h_doubled);
-    let v_consts = f64x2(1.5, 1.0);
-
-    // A lot of this code assumes this (so do other lang benchmarks)
-    assert_eq!(w, h);
-    let mut precalc_r = Vec::with_capacity(w);
-    let mut precalc_i = Vec::with_capacity(h);
-
-    let precalc_futures = (0..WORKERS).map(|i| {
-        thread::spawn(move|| {
-            let mut rs = Vec::with_capacity(w / WORKERS);
-            let mut is = Vec::with_capacity(w / WORKERS);
-
-            let start = i * chunk_size;
-            let end = if i == (WORKERS - 1) {
-                start + last_chunk_size
-            } else {
-                (i + 1) * chunk_size
-            };
-
-            // This assumes w == h
-            for x in start..end {
-                let xf = x as f64;
-                let xy = f64x2(xf, xf);
-
-                let f64x2(r, i) = xy * v_inverses - v_consts;
-                rs.push(r);
-                is.push(i);
-            }
-
-            (rs, is)
-        })
-    }).collect::<Vec<_>>();
-
-    for res in precalc_futures {
-        let (rs, is) = res.join().unwrap();
-        precalc_r.extend(rs);
-        precalc_i.extend(is);
-    }
-
-    assert_eq!(precalc_r.len(), w);
-    assert_eq!(precalc_i.len(), h);
-
-    let arc_init_r = Arc::new(precalc_r);
-    let arc_init_i = Arc::new(precalc_i);
-
-    let data = (0..WORKERS).map(|i| {
-        let vec_init_r = arc_init_r.clone();
-        let vec_init_i = arc_init_i.clone();
-
-        thread::spawn(move|| {
-            let mut res: Vec<u8> = Vec::with_capacity((chunk_size * w) / 8);
-            let init_r_slice = vec_init_r;
-
-            let start = i * chunk_size;
-            let end = if i == (WORKERS - 1) {
-                start + last_chunk_size
-            } else {
-                (i + 1) * chunk_size
-            };
-
-            for &init_i in &vec_init_i[start..end] {
-                write_line(init_i, &init_r_slice, &mut res);
-            }
-
-            res
-        })
-    }).collect::<Vec<_>>();
-
-    try!(writeln!(&mut out, "P4\n{} {}", w, h));
-    for res in data {
-        try!(out.write_all(&res.join().unwrap()));
-    }
-    out.flush()
-}
-
-fn write_line(init_i: f64, vec_init_r: &[f64], res: &mut Vec<u8>) {
-    let v_init_i : f64x2 = f64x2(init_i, init_i);
-    let v_2 : f64x2 = f64x2(2.0, 2.0);
-    const LIMIT_SQUARED: f64 = LIMIT * LIMIT;
-
-    for chunk_init_r in vec_init_r.chunks(8) {
-        let mut cur_byte = 0xff;
-        let mut i = 0;
-
-        while i < 8 {
-            let v_init_r = f64x2(chunk_init_r[i], chunk_init_r[i + 1]);
-            let mut cur_r = v_init_r;
-            let mut cur_i = v_init_i;
-            let mut r_sq = v_init_r * v_init_r;
-            let mut i_sq = v_init_i * v_init_i;
-
-            let mut b = 0;
-            for _ in 0..ITER {
-                let r = cur_r;
-                let i = cur_i;
-
-                cur_i = v_2 * r * i + v_init_i;
-                cur_r = r_sq - i_sq + v_init_r;
-
-                let f64x2(bit1, bit2) = r_sq + i_sq;
-
-                if bit1 > LIMIT_SQUARED {
-                    b |= 2;
-                    if b == 3 { break; }
-                }
-
-                if bit2 > LIMIT_SQUARED {
-                    b |= 1;
-                    if b == 3 { break; }
-                }
-
-                r_sq = cur_r * cur_r;
-                i_sq = cur_i * cur_i;
-            }
-
-            cur_byte = (cur_byte << 2) + b;
-            i += 2;
-        }
-
-        res.push(cur_byte^!0);
-    }
-}
-
-fn main() {
-    let mut args = env::args();
-    let res = if args.len() < 2 {
-        println!("Test mode: do not dump the image because it's not utf8, \
-                  which interferes with the test runner.");
-        mandelbrot(1000, io::sink())
-    } else {
-        mandelbrot(args.nth(1).unwrap().parse().unwrap(), io::stdout())
-    };
-    res.unwrap();
-}
diff --git a/src/test/bench/shootout-spectralnorm.rs b/src/test/bench/shootout-spectralnorm.rs
deleted file mode 100644 (file)
index a6c77ea..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-// The Computer Language Benchmarks Game
-// http://benchmarksgame.alioth.debian.org/
-//
-// contributed by the Rust Project Developers
-
-// Copyright (c) 2012-2014 The Rust Project Developers
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-//
-// - Redistributions of source code must retain the above copyright
-//   notice, this list of conditions and the following disclaimer.
-//
-// - Redistributions in binary form must reproduce the above copyright
-//   notice, this list of conditions and the following disclaimer in
-//   the documentation and/or other materials provided with the
-//   distribution.
-//
-// - Neither the name of "The Computer Language Benchmarks Game" nor
-//   the name of "The Computer Language Shootout Benchmarks" nor the
-//   names of its contributors may be used to endorse or promote
-//   products derived from this software without specific prior
-//   written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-// OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// no-pretty-expanded FIXME #15189
-
-#![allow(non_snake_case)]
-#![feature(unboxed_closures, iter_arith, core_simd, scoped)]
-
-use std::thread;
-use std::env;
-use std::simd::f64x2;
-
-fn main() {
-    let mut args = env::args();
-    let answer = spectralnorm(if env::var_os("RUST_BENCH").is_some() {
-        5500
-    } else if args.len() < 2 {
-        2000
-    } else {
-        args.nth(1).unwrap().parse().unwrap()
-    });
-    println!("{:.9}", answer);
-}
-
-fn spectralnorm(n: usize) -> f64 {
-    assert!(n % 2 == 0, "only even lengths are accepted");
-    let mut u = vec![1.0; n];
-    let mut v = u.clone();
-    let mut tmp = v.clone();
-    for _ in 0..10 {
-        mult_AtAv(&u, &mut v, &mut tmp);
-        mult_AtAv(&v, &mut u, &mut tmp);
-    }
-    (dot(&u, &v) / dot(&v, &v)).sqrt()
-}
-
-fn mult_AtAv(v: &[f64], out: &mut [f64], tmp: &mut [f64]) {
-    mult_Av(v, tmp);
-    mult_Atv(tmp, out);
-}
-
-fn mult_Av(v: &[f64], out: &mut [f64]) {
-    parallel(out, |start, out| mult(v, out, start, |i, j| A(i, j)));
-}
-
-fn mult_Atv(v: &[f64], out: &mut [f64]) {
-    parallel(out, |start, out| mult(v, out, start, |i, j| A(j, i)));
-}
-
-fn mult<F>(v: &[f64], out: &mut [f64], start: usize, a: F)
-           where F: Fn(usize, usize) -> f64 {
-    for (i, slot) in out.iter_mut().enumerate().map(|(i, s)| (i + start, s)) {
-        let mut sum = f64x2(0.0, 0.0);
-        for (j, chunk) in v.chunks(2).enumerate().map(|(j, s)| (2 * j, s)) {
-            let top = f64x2(chunk[0], chunk[1]);
-            let bot = f64x2(a(i, j), a(i, j + 1));
-            sum = sum + top / bot;
-        }
-        let f64x2(a, b) = sum;
-        *slot = a + b;
-    }
-}
-
-fn A(i: usize, j: usize) -> f64 {
-    ((i + j) * (i + j + 1) / 2 + i + 1) as f64
-}
-
-fn dot(v: &[f64], u: &[f64]) -> f64 {
-    v.iter().zip(u).map(|(a, b)| *a * *b).sum()
-}
-
-
-// Executes a closure in parallel over the given mutable slice. The closure `f`
-// is run in parallel and yielded the starting index within `v` as well as a
-// sub-slice of `v`.
-fn parallel<'a,T, F>(v: &mut [T], ref f: F)
-                  where T: Send + Sync + 'a,
-                        F: Fn(usize, &mut [T]) + Sync + 'a {
-    // FIXME: pick a more appropriate parallel factor
-    // FIXME: replace with thread::scoped when it exists again
-    let parallelism = 4;
-    let size = v.len() / parallelism + 1;
-    v.chunks_mut(size).enumerate().map(|(i, chunk)| {
-        f(i * size, chunk)
-    }).collect::<Vec<_>>();
-}
diff --git a/src/test/codegen/mir_zst_stores.rs b/src/test/codegen/mir_zst_stores.rs
new file mode 100644 (file)
index 0000000..c1acdaf
--- /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.
+
+// compile-flags: -C no-prepopulate-passes
+
+#![feature(rustc_attrs)]
+#![crate_type = "lib"]
+use std::marker::PhantomData;
+
+
+struct Zst { phantom: PhantomData<Zst> }
+
+// CHECK-LABEL: @mir
+#[no_mangle]
+#[rustc_mir]
+fn mir(){
+    // CHECK-NOT: getelementptr
+    // CHECK-NOT: store{{.*}}undef
+    let x = Zst { phantom: PhantomData };
+}
index 7ffbbe69c3d6bd503148d7d65a6cf5e2968d4922..3e153a21e5d38c8f0dfe86adcdb706a4420a7ea7 100644 (file)
@@ -23,7 +23,8 @@ fn main() {
     let ps = syntax::parse::ParseSess::new();
     let mut cx = syntax::ext::base::ExtCtxt::new(
         &ps, vec![],
-        syntax::ext::expand::ExpansionConfig::default("qquote".to_string()));
+        syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
+        &mut Vec::new());
     cx.bt_push(syntax::codemap::ExpnInfo {
         call_site: DUMMY_SP,
         callee: syntax::codemap::NameAndSpan {
diff --git a/src/test/compile-fail/associated-const-array-len.rs b/src/test/compile-fail/associated-const-array-len.rs
new file mode 100644 (file)
index 0000000..5d8007d
--- /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(associated_consts)]
+
+trait Foo {
+    const ID: usize;
+}
+
+const X: [i32; <i32 as Foo>::ID] = [0, 1, 2]; //~ ERROR E0250
+
+fn main() {
+    assert_eq!(1, X);
+}
diff --git a/src/test/compile-fail/associated-const-no-item.rs b/src/test/compile-fail/associated-const-no-item.rs
new file mode 100644 (file)
index 0000000..89d1ac1
--- /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(associated_consts)]
+
+trait Foo {
+    const ID: i32;
+}
+
+const X: i32 = <i32>::ID;
+//~^ ERROR no associated item named `ID` found for type `i32`
+
+fn main() {
+    assert_eq!(1, X);
+}
diff --git a/src/test/compile-fail/associated-const-type-parameter-arms.rs b/src/test/compile-fail/associated-const-type-parameter-arms.rs
new file mode 100644 (file)
index 0000000..f564157
--- /dev/null
@@ -0,0 +1,38 @@
+// 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.
+
+#![feature(associated_consts)]
+
+pub enum EFoo { A, B, C, D }
+
+pub trait Foo {
+    const X: EFoo;
+}
+
+struct Abc;
+impl Foo for Abc {
+    const X: EFoo = EFoo::B;
+}
+
+struct Def;
+impl Foo for Def {
+    const X: EFoo = EFoo::D;
+}
+
+pub fn test<A: Foo, B: Foo>(arg: EFoo) {
+    match arg {
+        A::X => println!("A::X"), //~ error: statics cannot be referenced in patterns [E0158]
+        B::X => println!("B::X"), //~ error: statics cannot be referenced in patterns [E0158]
+        _ => (),
+    }
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/associated-const-type-parameter-arrays-2.rs b/src/test/compile-fail/associated-const-type-parameter-arrays-2.rs
new file mode 100644 (file)
index 0000000..2f68735
--- /dev/null
@@ -0,0 +1,32 @@
+// 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.
+
+#![feature(associated_consts)]
+
+pub trait Foo {
+    const Y: usize;
+}
+
+struct Abc;
+impl Foo for Abc {
+    const Y: usize = 8;
+}
+
+struct Def;
+impl Foo for Def {
+    const Y: usize = 33;
+}
+
+pub fn test<A: Foo, B: Foo>() {
+    let _array = [4; <A as Foo>::Y]; //~ error: expected constant integer
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/associated-const-type-parameter-arrays.rs b/src/test/compile-fail/associated-const-type-parameter-arrays.rs
new file mode 100644 (file)
index 0000000..3d3b795
--- /dev/null
@@ -0,0 +1,32 @@
+// 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.
+
+#![feature(associated_consts)]
+
+pub trait Foo {
+    const Y: usize;
+}
+
+struct Abc;
+impl Foo for Abc {
+    const Y: usize = 8;
+}
+
+struct Def;
+impl Foo for Def {
+    const Y: usize = 33;
+}
+
+pub fn test<A: Foo, B: Foo>() {
+    let _array: [u32; <A as Foo>::Y]; //~ error: the parameter type
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/associated-const-type-parameters.rs b/src/test/compile-fail/associated-const-type-parameters.rs
deleted file mode 100644 (file)
index e48ff59..0000000
+++ /dev/null
@@ -1,26 +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.
-
-#![feature(associated_consts)]
-
-pub trait Foo {
-    const MIN: i32;
-
-    fn get_min() -> i32 {
-        Self::MIN //~ ERROR E0329
-    }
-}
-
-fn get_min<T: Foo>() -> i32 {
-    T::MIN; //~ ERROR E0329
-    <T as Foo>::MIN //~ ERROR E0329
-}
-
-fn main() {}
index 915cb077787ea41c05330ce1185ce497633afac0..6d68da54112f24da473b778e0780319813873246 100644 (file)
@@ -32,13 +32,13 @@ impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned {
 impl<'a, B: ?Sized> IntoCow<'a, B> for <B as ToOwned>::Owned where B: ToOwned {
 //~^ ERROR E0119
     fn into_cow(self) -> Cow<'a, B> {
-        Cow
+        Cow(PhantomData)
     }
 }
 
 impl<'a, B: ?Sized> IntoCow<'a, B> for &'a B where B: ToOwned {
     fn into_cow(self) -> Cow<'a, B> {
-        Cow
+        Cow(PhantomData)
     }
 }
 
index 233532a6085803400c5ae04f9edbf1d70b4f94a9..0b1d6a5b71ad20e0abb8a6bc1809ab559747fcb0 100644 (file)
@@ -25,10 +25,7 @@ trait Get {
 
 trait Other {
     fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
-    // (note that we no longer catch the error here, since the
-    //  error below aborts compilation.
-    //  See also associated-types-no-suitable-supertrait-2.rs
-    //  which checks that this error would be caught eventually.)
+    //~^ ERROR the trait `Get` is not implemented for the type `Self`
 }
 
 impl<T:Get> Other for T {
index c26d6cf4fe6d7c96a042ad1663bc13cfc4b4f2ec..d4a9830f220eae4609ac70e59ab71fa59beac8dd 100644 (file)
@@ -21,5 +21,5 @@ fn main() {
     let mut x = Int(0);
     x += 1;
     //~^ error: overloaded augmented assignments are not stable
-    // | help: add #![feature(augmented_assignments)] to the crate root to enable
+    //~| help: add #![feature(augmented_assignments)] to the crate root to enable
 }
index 0db8f9858f9dbc335b9437d58ea7111597305ee3..6f9e9cf945a5454475dbed1721c685b04a60b92f 100644 (file)
@@ -22,5 +22,5 @@ fn main() {
     let mut x = Int(0);
     x += 1;
     //~^ error: overloaded augmented assignments are not stable
-    // | help: add #![feature(augmented_assignments)] to the crate root to enable
+    //~| help: add #![feature(augmented_assignments)] to the crate root to enable
 }
index 704d856f106b280468522ff0377c02c0882c77a3..a1021500be3d7ed959b035c997a1b59e88492644 100644 (file)
@@ -9,14 +9,12 @@
 // except according to those terms.
 
 
-// error-pattern: unresolved
-
 enum color { rgb(isize, isize, isize), rgba(isize, isize, isize, isize), }
 
 fn main() {
-    let red: color = rgb(255, 0, 0);
+    let red: color = color::rgb(255, 0, 0);
     match red {
-      rgb(r, g, b) => { println!("rgb"); }
-      hsl(h, s, l) => { println!("hsl"); }
+      color::rgb(r, g, b) => { println!("rgb"); }
+      color::hsl(h, s, l) => { println!("hsl"); }  //~ ERROR no associated
     }
 }
index 037364c7a531c4ff64a64504fe02bfb61ec62e27..3065ecfaa302d3379a35ad350ac16d3301b80c6a 100644 (file)
@@ -13,7 +13,7 @@
 
 use std::sync::mpsc::{channel, Sender};
 
-trait Foo : Sync+'static {
+trait Foo : Sized+Sync+'static {
     fn foo(self, mut chan: Sender<Self>) { }
 }
 
diff --git a/src/test/compile-fail/coerce-expect-unsized-ascribed.rs b/src/test/compile-fail/coerce-expect-unsized-ascribed.rs
new file mode 100644 (file)
index 0000000..ef65927
--- /dev/null
@@ -0,0 +1,42 @@
+// 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.
+
+// A version of coerce-expect-unsized that uses type ascription.
+// Doesn't work so far, but supposed to work eventually
+
+#![feature(box_syntax, type_ascription)]
+
+use std::fmt::Debug;
+
+pub fn main() {
+    let _ = box { [1, 2, 3] }: Box<[i32]>; //~ ERROR mismatched types
+    let _ = box if true { [1, 2, 3] } else { [1, 3, 4] }: Box<[i32]>; //~ ERROR mismatched types
+    let _ = box match true { true => [1, 2, 3], false => [1, 3, 4] }: Box<[i32]>;
+    //~^ ERROR mismatched types
+    let _ = box { |x| (x as u8) }: Box<Fn(i32) -> _>; //~ ERROR mismatched types
+    let _ = box if true { false } else { true }: Box<Debug>; //~ ERROR mismatched types
+    let _ = box match true { true => 'a', false => 'b' }: Box<Debug>; //~ ERROR mismatched types
+
+    let _ = &{ [1, 2, 3] }: &[i32]; //~ ERROR mismatched types
+    let _ = &if true { [1, 2, 3] } else { [1, 3, 4] }: &[i32]; //~ ERROR mismatched types
+    let _ = &match true { true => [1, 2, 3], false => [1, 3, 4] }: &[i32];
+    //~^ ERROR mismatched types
+    let _ = &{ |x| (x as u8) }: &Fn(i32) -> _; //~ ERROR mismatched types
+    let _ = &if true { false } else { true }: &Debug; //~ ERROR mismatched types
+    let _ = &match true { true => 'a', false => 'b' }: &Debug; //~ ERROR mismatched types
+
+    let _ = Box::new([1, 2, 3]): Box<[i32]>; //~ ERROR mismatched types
+    let _ = Box::new(|x| (x as u8)): Box<Fn(i32) -> _>; //~ ERROR mismatched types
+
+    let _ = vec![
+        Box::new(|x| (x as u8)),
+        box |x| (x as i16 as u8),
+    ]: Vec<Box<Fn(i32) -> _>>;
+}
index 55c9ba2a0e89aeda6cfb43ea13f0bdd4e8ba049f..344ec89d25de915e58de8021481d9ce2a632df69 100644 (file)
@@ -15,14 +15,14 @@ trait MyTrait {}
 struct TestType<T>(::std::marker::PhantomData<T>);
 
 unsafe impl<T: MyTrait+'static> Send for TestType<T> {}
-//~^ ERROR conflicting implementations for trait `core::marker::Send`
-//~^^ ERROR conflicting implementations for trait `core::marker::Send`
+//~^ ERROR conflicting implementations of trait `core::marker::Send`
+//~^^ ERROR conflicting implementations of trait `core::marker::Send`
 
 impl<T: MyTrait> !Send for TestType<T> {}
-//~^ ERROR conflicting implementations for trait `core::marker::Send`
+//~^ ERROR conflicting implementations of trait `core::marker::Send`
 
 unsafe impl<T:'static> Send for TestType<T> {}
-//~^ ERROR error: conflicting implementations for trait `core::marker::Send`
+//~^ ERROR error: conflicting implementations of trait `core::marker::Send`
 
 impl !Send for TestType<i32> {}
 
index cccc8b05b3038b8ae719f192badcb5906edc43b5..0705702b031ee007b2731320f50b4d4885908fbe 100644 (file)
@@ -15,7 +15,7 @@ trait MyTrait {}
 impl MyTrait for .. {}
 
 impl MyTrait for .. {}
-//~^ ERROR conflicting implementations for trait `MyTrait`
+//~^ ERROR conflicting implementations of trait `MyTrait`
 
 trait MySafeTrait {}
 
index ce6baeb204c93b67c8203bf61e4c3759ceda90b9..8bb9556fcc075204f010c66cab853259cb3c1b11 100644 (file)
@@ -14,6 +14,6 @@
 // If the trait is not object-safe, we give a more tailored message
 // because we're such schnuckels:
 trait NotObjectSafe { fn eq(&self, other: Self); }
-impl NotObjectSafe for NotObjectSafe { } //~ ERROR E0372
+impl NotObjectSafe for NotObjectSafe { } //~ ERROR E0038
 
 fn main() { }
index 1be606c3546fd18168c58a3b5aee8882b7145dbc..9c210c132a3131084733aead1eabdf7b34870d8c 100644 (file)
@@ -28,8 +28,6 @@ impl Copy for MyType {}
 
 impl Copy for &'static mut MyType {}
 //~^ ERROR E0206
-//~| ERROR E0277
-//~| ERROR E0277
 impl Clone for MyType { fn clone(&self) -> Self { *self } }
 
 impl Copy for (MyType, MyType) {}
@@ -42,8 +40,6 @@ impl Copy for &'static NotSync {}
 impl Copy for [MyType] {}
 //~^ ERROR E0206
 //~| ERROR E0117
-//~| ERROR E0277
-//~| ERROR E0277
 
 impl Copy for &'static [NotSync] {}
 //~^ ERROR E0206
index 2ac4bb0492b1f6f4df24a1454829c785aaf66e0f..167067cb5fc0aec45ac48d9c282b1f5703875bbb 100644 (file)
@@ -30,7 +30,6 @@ impl Sized for (MyType, MyType) {} //~ ERROR E0117
 impl Sized for &'static NotSync {} //~ ERROR E0322
 
 impl Sized for [MyType] {} //~ ERROR E0117
-//~^ ERROR E0277
 
 impl Sized for &'static [NotSync] {} //~ ERROR E0117
 
diff --git a/src/test/compile-fail/coherence-overlap-messages.rs b/src/test/compile-fail/coherence-overlap-messages.rs
new file mode 100644 (file)
index 0000000..4f1092f
--- /dev/null
@@ -0,0 +1,42 @@
+// 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 Foo {}
+
+impl<T> Foo for T {} //~ ERROR conflicting implementations of trait `Foo`:
+impl<U> Foo for U {}
+
+trait Bar {}
+
+impl<T> Bar for T {} //~ ERROR conflicting implementations of trait `Bar` for type `u8`:
+impl Bar for u8 {}
+
+trait Baz<T> {}
+
+impl<T, U> Baz<U> for T {} //~ ERROR conflicting implementations of trait `Baz<_>` for type `u8`:
+impl<T> Baz<T> for u8 {}
+
+trait Quux<T> {}
+
+impl<T, U> Quux<U> for T {} //~ ERROR conflicting implementations of trait `Quux<_>`:
+impl<T> Quux<T> for T {}
+
+trait Qaar<T> {}
+
+impl<T, U> Qaar<U> for T {} //~ ERROR conflicting implementations of trait `Qaar<u8>`:
+impl<T> Qaar<u8> for T {}
+
+trait Qaax<T> {}
+
+impl<T, U> Qaax<U> for T {}
+//~^ ERROR conflicting implementations of trait `Qaax<u8>` for type `u32`:
+impl Qaax<u8> for u32 {}
+
+fn main() {}
diff --git a/src/test/compile-fail/coherence-projection-conflict-orphan.rs b/src/test/compile-fail/coherence-projection-conflict-orphan.rs
new file mode 100644 (file)
index 0000000..3de7945
--- /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(rustc_attrs)]
+
+// Here we expect a coherence conflict because, even though `i32` does
+// not implement `Iterator`, we cannot rely on that negative reasoning
+// due to the orphan rules. Therefore, `A::Item` may yet turn out to
+// be `i32`.
+
+pub trait Foo<P> {}
+
+pub trait Bar {
+    type Output: 'static;
+}
+
+impl Foo<i32> for i32 { } //~ ERROR E0119
+
+impl<A:Iterator> Foo<A::Item> for A { }
+
+fn main() {}
diff --git a/src/test/compile-fail/coherence-projection-conflict.rs b/src/test/compile-fail/coherence-projection-conflict.rs
new file mode 100644 (file)
index 0000000..2236e71
--- /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 std::marker::PhantomData;
+
+pub trait Foo<P> {}
+
+pub trait Bar {
+    type Output: 'static;
+}
+
+impl Foo<i32> for i32 { } //~ ERROR E0119
+
+impl<A:Bar> Foo<A::Output> for A { }
+
+impl Bar for i32 {
+    type Output = i32;
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/coherence-projection-ok-orphan.rs b/src/test/compile-fail/coherence-projection-ok-orphan.rs
new file mode 100644 (file)
index 0000000..a52af08
--- /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)]
+#![allow(dead_code)]
+
+// Here we do not get a coherence conflict because `Baz: Iterator`
+// does not hold and (due to the orphan rules), we can rely on that.
+
+pub trait Foo<P> {}
+
+pub trait Bar {
+    type Output: 'static;
+}
+
+struct Baz;
+impl Foo<i32> for Baz { }
+
+impl<A:Iterator> Foo<A::Item> for A { }
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
diff --git a/src/test/compile-fail/coherence-projection-ok.rs b/src/test/compile-fail/coherence-projection-ok.rs
new file mode 100644 (file)
index 0000000..af88f37
--- /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(rustc_attrs)]
+
+pub trait Foo<P> {}
+
+pub trait Bar {
+    type Output: 'static;
+}
+
+impl Foo<i32> for i32 { }
+
+impl<A:Bar> Foo<A::Output> for A { }
+
+impl Bar for i32 {
+    type Output = u32;
+}
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
index daa60955ad88decd4d037bc1fe3299d4d24ec018..2a2fc2ef080dbf30cacd440ff6d482e0c7eda65d 100644 (file)
@@ -9,7 +9,6 @@
 // except according to those terms.
 
 #![allow(unused_imports)]
-#![feature(negate_unsigned)]
 
 // Note: the relevant lint pass here runs before some of the constant
 // evaluation below (e.g. that performed by trans and llvm), so if you
@@ -65,7 +64,7 @@ const VALS_I64: (i64, i64, i64, i64) =
      );
 
 const VALS_U8: (u8, u8, u8, u8) =
-    (-u8::MIN,
+    (-(u8::MIN as i8) as u8,
      u8::MIN - 1,
      //~^ ERROR attempted to sub with overflow
      u8::MAX + 1,
@@ -75,7 +74,7 @@ const VALS_U8: (u8, u8, u8, u8) =
      );
 
 const VALS_U16: (u16, u16, u16, u16) =
-    (-u16::MIN,
+    (-(u16::MIN as i16) as u16,
      u16::MIN - 1,
      //~^ ERROR attempted to sub with overflow
      u16::MAX + 1,
@@ -85,7 +84,7 @@ const VALS_U16: (u16, u16, u16, u16) =
      );
 
 const VALS_U32: (u32, u32, u32, u32) =
-    (-u32::MIN,
+    (-(u32::MIN as i32) as u32,
      u32::MIN - 1,
      //~^ ERROR attempted to sub with overflow
      u32::MAX + 1,
@@ -95,7 +94,7 @@ const VALS_U32: (u32, u32, u32, u32) =
      );
 
 const VALS_U64: (u64, u64, u64, u64) =
-    (-u64::MIN,
+    (-(u64::MIN as i64) as u64,
      u64::MIN - 1,
      //~^ ERROR attempted to sub with overflow
      u64::MAX + 1,
diff --git a/src/test/compile-fail/const-eval-overflow0.rs b/src/test/compile-fail/const-eval-overflow0.rs
new file mode 100644 (file)
index 0000000..7db7de9
--- /dev/null
@@ -0,0 +1,100 @@
+// 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.
+
+#![allow(unused_imports)]
+
+// Note: the relevant lint pass here runs before some of the constant
+// evaluation below (e.g. that performed by trans and llvm), so if you
+// change this warn to a deny, then the compiler will exit before
+// those errors are detected.
+
+use std::fmt;
+use std::{i8, i16, i32, i64, isize};
+use std::{u8, u16, u32, u64, usize};
+
+const VALS_I8: (i8, i8, i8, i8) =
+    (-i8::MIN,
+     i8::MIN - 1,
+     i8::MAX + 1,
+     i8::MIN * 2,
+     );
+
+const VALS_I16: (i16, i16, i16, i16) =
+    (-i16::MIN,
+     i16::MIN - 1,
+     i16::MAX + 1,
+     i16::MIN * 2,
+     );
+
+const VALS_I32: (i32, i32, i32, i32) =
+    (-i32::MIN,
+     i32::MIN - 1,
+     i32::MAX + 1,
+     i32::MIN * 2,
+     );
+
+const VALS_I64: (i64, i64, i64, i64) =
+    (-i64::MIN,
+     i64::MIN - 1,
+     i64::MAX + 1,
+     i64::MAX * 2,
+     );
+
+const VALS_U8: (u8, u8, u8, u8) =
+    (-u8::MIN,
+     //~^ ERROR unary negation of unsigned integer
+     //~| HELP use a cast or the `!` operator
+     u8::MIN - 1,
+     u8::MAX + 1,
+     u8::MAX * 2,
+     );
+
+const VALS_U16: (u16, u16, u16, u16) =
+    (-u16::MIN,
+     //~^ ERROR unary negation of unsigned integer
+     //~| HELP use a cast or the `!` operator
+     u16::MIN - 1,
+     u16::MAX + 1,
+     u16::MAX * 2,
+     );
+
+const VALS_U32: (u32, u32, u32, u32) =
+    (-u32::MIN,
+     //~^ ERROR unary negation of unsigned integer
+     //~| HELP use a cast or the `!` operator
+     u32::MIN - 1,
+     u32::MAX + 1,
+     u32::MAX * 2,
+     );
+
+const VALS_U64: (u64, u64, u64, u64) =
+    (-u64::MIN,
+     //~^ ERROR unary negation of unsigned integer
+     //~| HELP use a cast or the `!` operator
+     u64::MIN - 1,
+     u64::MAX + 1,
+     u64::MAX * 2,
+     );
+
+fn main() {
+    foo(VALS_I8);
+    foo(VALS_I16);
+    foo(VALS_I32);
+    foo(VALS_I64);
+
+    foo(VALS_U8);
+    foo(VALS_U16);
+    foo(VALS_U32);
+    foo(VALS_U64);
+}
+
+fn foo<T:fmt::Debug>(x: T) {
+    println!("{:?}", x);
+}
diff --git a/src/test/compile-fail/const-fn-error.rs b/src/test/compile-fail/const-fn-error.rs
new file mode 100644 (file)
index 0000000..cb6f2d0
--- /dev/null
@@ -0,0 +1,29 @@
+// 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.
+
+// test that const fn signature and body errors are checked
+// even in array lengths, which are evaluated before check_const
+
+#![feature(const_fn)]
+
+const X : usize = 2;
+
+const fn f(x: usize) -> usize {
+    let mut sum = 0; //~ ERROR: E0016
+    for i in 0..x { //~ ERROR: E0016
+        sum += i;
+    }
+    sum
+}
+
+#[allow(unused_variables)]
+fn main() {
+    let a : [i32; f(X)];
+}
index baa3eba06805facb90af3a3a33d8956dce186b3b..f8381978dc7bd9ef0bbe737d0d1a8417e9606b5d 100644 (file)
@@ -37,11 +37,5 @@ const fn get_Y_addr() -> &'static u32 {
         //~^ ERROR E0013
 }
 
-const fn get() -> u32 {
-    let x = 22; //~ ERROR E0016
-    let y = 44; //~ ERROR E0016
-    x + y
-}
-
 fn main() {
 }
diff --git a/src/test/compile-fail/const-fn-not-safe-for-const2.rs b/src/test/compile-fail/const-fn-not-safe-for-const2.rs
new file mode 100644 (file)
index 0000000..a053847
--- /dev/null
@@ -0,0 +1,44 @@
+// 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.
+
+// Test that we can't call random fns in a const fn or do other bad things.
+
+#![feature(const_fn)]
+
+use std::mem::transmute;
+
+fn random() -> u32 { 0 }
+
+const fn sub(x: &u32) -> usize {
+    unsafe { transmute(x) }
+}
+
+const fn sub1() -> u32 {
+    random()
+}
+
+static Y: u32 = 0;
+
+const fn get_Y() -> u32 {
+    Y
+}
+
+const fn get_Y_addr() -> &'static u32 {
+    &Y
+}
+
+const fn get() -> u32 {
+    let x = 22; //~ ERROR E0016
+    let y = 44; //~ ERROR E0016
+    x + y
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/const-pattern-not-const-evaluable.rs b/src/test/compile-fail/const-pattern-not-const-evaluable.rs
new file mode 100644 (file)
index 0000000..ecc43d2
--- /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.
+
+#![feature(const_fn)]
+
+enum Cake {
+    BlackForest,
+    Marmor,
+}
+use Cake::*;
+
+const BOO: (Cake, Cake) = (Marmor, BlackForest);
+//~^ ERROR: constant evaluation error: non-constant path in constant expression [E0471]
+const FOO: Cake = BOO.1;
+
+const fn foo() -> Cake {
+    Marmor //~ ERROR: constant evaluation error: non-constant path in constant expression [E0471]
+    //~^ ERROR: non-constant path in constant expression
+}
+
+const WORKS: Cake = Marmor;
+
+const GOO: Cake = foo();
+
+fn main() {
+    match BlackForest {
+        FOO => println!("hi"), //~ NOTE: in pattern here
+        GOO => println!("meh"), //~ NOTE: in pattern here
+        WORKS => println!("möp"),
+        _ => println!("bye"),
+    }
+}
diff --git a/src/test/compile-fail/const-tup-index-span.rs b/src/test/compile-fail/const-tup-index-span.rs
new file mode 100644 (file)
index 0000000..8c607fc
--- /dev/null
@@ -0,0 +1,18 @@
+// 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.
+
+// Test spans of errors
+
+const TUP: (usize,) = 5 << 64;
+//~^ ERROR: attempted left shift with overflow [E0250]
+const ARR: [i32; TUP.0] = [];
+
+fn main() {
+}
index 0aefd0ae28845f1474dc87000d06af4886e1e2e5..7d4c618de665c622712d252d60417fade9974ef5 100644 (file)
@@ -21,14 +21,15 @@ trait Foo<X,Y>: Bar<X> {
 
 trait Bar<X> { }
 
-fn vacuous<A>()
+// We don't always check where clauses for sanity, but in this case
+// wfcheck does report an error here:
+fn vacuous<A>() //~ ERROR the trait `Bar<u32>` is not implemented for the type `i32`
     where i32: Foo<u32, A>
 {
-    // vacuous could never be called, because it requires that i32:
-    // Bar<u32>. But the code doesn't check that this could never be
-    // satisfied.
+    // ... the original intention was to check that we don't use that
+    // vacuous where clause (which could never be satisfied) to accept
+    // the following line and then mess up calls elsewhere.
     require::<i32, u32>();
-    //~^ ERROR the trait `Bar<u32>` is not implemented for the type `i32`
 }
 
 fn require<A,B>()
diff --git a/src/test/compile-fail/dep-graph-caller-callee.rs b/src/test/compile-fail/dep-graph-caller-callee.rs
new file mode 100644 (file)
index 0000000..acd6091
--- /dev/null
@@ -0,0 +1,47 @@
+// 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.
+
+// Test that immediate callers have to change when callee changes, but
+// not callers' callers.
+
+// compile-flags: -Z incr-comp
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+fn main() { }
+
+mod x {
+    #[rustc_if_this_changed]
+    pub fn x() { }
+}
+
+mod y {
+    use x;
+
+    // These dependencies SHOULD exist:
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR OK
+    pub fn y() {
+        x::x();
+    }
+}
+
+mod z {
+    use y;
+
+    // These are expected to yield errors, because changes to `x`
+    // affect the BODY of `y`, but not its signature.
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path
+    pub fn z() {
+        y::y();
+    }
+}
diff --git a/src/test/compile-fail/dep-graph-struct-signature.rs b/src/test/compile-fail/dep-graph-struct-signature.rs
new file mode 100644 (file)
index 0000000..5cfb748
--- /dev/null
@@ -0,0 +1,100 @@
+// 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.
+
+// Test cases where a changing struct appears in the signature of fns
+// and methods.
+
+// compile-flags: -Z incr-comp
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+#![allow(unused_variables)]
+
+fn main() { }
+
+#[rustc_if_this_changed]
+struct WillChange {
+    x: u32,
+    y: u32
+}
+
+struct WontChange {
+    x: u32,
+    y: u32
+}
+
+// these are valid dependencies
+mod signatures {
+    use WillChange;
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    trait Bar {
+        fn do_something(x: WillChange);
+    }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    fn some_fn(x: WillChange) { }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    fn new_foo(x: u32, y: u32) -> WillChange {
+        WillChange { x: x, y: y }
+    }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    impl WillChange {
+        fn new(x: u32, y: u32) -> WillChange { loop { } }
+    }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    impl WillChange {
+        fn method(&self, x: u32) { }
+    }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    struct WillChanges {
+        x: WillChange,
+        y: WillChange
+    }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    fn indirect(x: WillChanges) { }
+}
+
+// these are invalid dependencies, though sometimes we create edges
+// anyway.
+mod invalid_signatures {
+    use WontChange;
+
+    // FIXME due to the variance pass having overly conservative edges,
+    // we incorrectly think changes are needed here
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    trait A {
+        fn do_something_else_twice(x: WontChange);
+    }
+
+    // FIXME due to the variance pass having overly conservative edges,
+    // we incorrectly think changes are needed here
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
+    fn b(x: WontChange) { }
+
+    #[rustc_then_this_would_need(ItemSignature)] //~ ERROR no path from `WillChange`
+    #[rustc_then_this_would_need(CollectItem)] //~ ERROR no path from `WillChange`
+    fn c(x: u32) { }
+}
+
diff --git a/src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs b/src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs
new file mode 100644 (file)
index 0000000..57e8358
--- /dev/null
@@ -0,0 +1,54 @@
+// 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.
+
+// Test that adding an impl to a trait `Foo` DOES affect functions
+// that only use `Bar` if they have methods in common.
+
+// compile-flags: -Z incr-comp
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+fn main() { }
+
+pub trait Foo: Sized {
+    fn method(self) { }
+}
+
+pub trait Bar: Sized {
+    fn method(self) { }
+}
+
+mod x {
+    use {Foo, Bar};
+
+    #[rustc_if_this_changed]
+    impl Foo for u32 { }
+
+    impl Bar for char { }
+}
+
+mod y {
+    use {Foo, Bar};
+
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
+    pub fn with_char() {
+        char::method('a');
+    }
+}
+
+mod z {
+    use y;
+
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    pub fn z() {
+        y::with_char();
+    }
+}
diff --git a/src/test/compile-fail/dep-graph-trait-impl-two-traits.rs b/src/test/compile-fail/dep-graph-trait-impl-two-traits.rs
new file mode 100644 (file)
index 0000000..ba54a05
--- /dev/null
@@ -0,0 +1,54 @@
+// 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.
+
+// Test that adding an impl to a trait `Foo` does not affect functions
+// that only use `Bar`, so long as they do not have methods in common.
+
+// compile-flags: -Z incr-comp
+
+#![feature(rustc_attrs)]
+#![allow(warnings)]
+
+fn main() { }
+
+pub trait Foo: Sized {
+    fn foo(self) { }
+}
+
+pub trait Bar: Sized {
+    fn bar(self) { }
+}
+
+mod x {
+    use {Foo, Bar};
+
+    #[rustc_if_this_changed]
+    impl Foo for char { }
+
+    impl Bar for char { }
+}
+
+mod y {
+    use {Foo, Bar};
+
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    pub fn call_bar() {
+        char::bar('a');
+    }
+}
+
+mod z {
+    use y;
+
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    pub fn z() {
+        y::call_bar();
+    }
+}
diff --git a/src/test/compile-fail/dep-graph-trait-impl.rs b/src/test/compile-fail/dep-graph-trait-impl.rs
new file mode 100644 (file)
index 0000000..83e924f
--- /dev/null
@@ -0,0 +1,77 @@
+// 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.
+
+// Test that when a trait impl changes, fns whose body uses that trait
+// must also be recompiled.
+
+// compile-flags: -Z incr-comp
+
+#![feature(rustc_attrs)]
+#![allow(warnings)]
+
+fn main() { }
+
+pub trait Foo: Sized {
+    fn method(self) { }
+}
+
+mod x {
+    use Foo;
+
+    #[rustc_if_this_changed]
+    impl Foo for char { }
+
+    impl Foo for u32 { }
+}
+
+mod y {
+    use Foo;
+
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR OK
+    pub fn with_char() {
+        char::method('a');
+    }
+
+    // FIXME(#30741) tcx fulfillment cache not tracked
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path
+    pub fn take_foo_with_char() {
+        take_foo::<char>('a');
+    }
+
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR OK
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR OK
+    pub fn with_u32() {
+        u32::method(22);
+    }
+
+    // FIXME(#30741) tcx fulfillment cache not tracked
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path
+    pub fn take_foo_with_u32() {
+        take_foo::<u32>(22);
+    }
+
+    pub fn take_foo<T:Foo>(t: T) { }
+}
+
+mod z {
+    use y;
+
+    // These are expected to yield errors, because changes to `x`
+    // affect the BODY of `y`, but not its signature.
+    #[rustc_then_this_would_need(TypeckItemBody)] //~ ERROR no path
+    #[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path
+    pub fn z() {
+        y::with_char();
+        y::with_u32();
+    }
+}
diff --git a/src/test/compile-fail/dep-graph-unrelated.rs b/src/test/compile-fail/dep-graph-unrelated.rs
new file mode 100644 (file)
index 0000000..8feec12
--- /dev/null
@@ -0,0 +1,22 @@
+// 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.
+
+// Test that two unrelated functions have no trans dependency.
+
+// compile-flags: -Z incr-comp
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+#[rustc_if_this_changed]
+fn main() { }
+
+#[rustc_then_this_would_need(TransCrateItem)] //~ ERROR no path from `main`
+fn bar() { }
diff --git a/src/test/compile-fail/deprecation-in-staged-api.rs b/src/test/compile-fail/deprecation-in-staged-api.rs
new file mode 100644 (file)
index 0000000..4f4aed2
--- /dev/null
@@ -0,0 +1,18 @@
+// 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.
+
+// #[deprecated] can't be used in staged api
+
+#![feature(deprecated, staged_api)]
+
+#![stable(feature = "test_feature", since = "1.0.0")]
+
+#[deprecated]
+fn main() { } //~ERROR `#[deprecated]` cannot be used in staged api
diff --git a/src/test/compile-fail/deprecation-lint-2.rs b/src/test/compile-fail/deprecation-lint-2.rs
new file mode 100644 (file)
index 0000000..2817e06
--- /dev/null
@@ -0,0 +1,23 @@
+// 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.
+
+// aux-build:deprecation-lint.rs
+// error-pattern: use of deprecated item
+
+#![deny(deprecated)]
+
+#[macro_use]
+extern crate deprecation_lint;
+
+use deprecation_lint::*;
+
+fn main() {
+    macro_test!();
+}
diff --git a/src/test/compile-fail/deprecation-lint-3.rs b/src/test/compile-fail/deprecation-lint-3.rs
new file mode 100644 (file)
index 0000000..7faaa18
--- /dev/null
@@ -0,0 +1,24 @@
+// 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.
+
+// aux-build:deprecation-lint.rs
+// error-pattern: use of deprecated item
+
+#![deny(deprecated)]
+#![allow(warnings)]
+
+#[macro_use]
+extern crate deprecation_lint;
+
+use deprecation_lint::*;
+
+fn main() {
+    macro_test_arg_nested!(deprecated_text);
+}
diff --git a/src/test/compile-fail/deprecation-lint.rs b/src/test/compile-fail/deprecation-lint.rs
new file mode 100644 (file)
index 0000000..58fa00f
--- /dev/null
@@ -0,0 +1,389 @@
+// Copyright 2013-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:deprecation-lint.rs
+
+#![feature(deprecated)]
+
+#![deny(deprecated)]
+#![allow(warnings)]
+
+#[macro_use]
+extern crate deprecation_lint;
+
+mod cross_crate {
+    use deprecation_lint::*;
+
+    fn test() {
+        type Foo = MethodTester;
+        let foo = MethodTester;
+
+        deprecated(); //~ ERROR use of deprecated item
+        foo.method_deprecated(); //~ ERROR use of deprecated item
+        Foo::method_deprecated(&foo); //~ ERROR use of deprecated item
+        <Foo>::method_deprecated(&foo); //~ ERROR use of deprecated item
+        foo.trait_deprecated(); //~ ERROR use of deprecated item
+        Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item
+        <Foo>::trait_deprecated(&foo); //~ ERROR use of deprecated item
+        <Foo as Trait>::trait_deprecated(&foo); //~ ERROR use of deprecated item
+
+        deprecated_text(); //~ ERROR use of deprecated item: text
+        foo.method_deprecated_text(); //~ ERROR use of deprecated item: text
+        Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        <Foo>::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
+        Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        <Foo>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        <Foo as Trait>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+
+        let _ = DeprecatedStruct { //~ ERROR use of deprecated item
+            i: 0 //~ ERROR use of deprecated item
+        };
+
+        let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item
+
+        let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item
+
+        let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item
+
+        // At the moment, the lint checker only checks stability in
+        // in the arguments of macros.
+        // Eventually, we will want to lint the contents of the
+        // macro in the module *defining* it. Also, stability levels
+        // on macros themselves are not yet linted.
+        macro_test_arg!(deprecated_text()); //~ ERROR use of deprecated item: text
+        macro_test_arg!(macro_test_arg!(deprecated_text())); //~ ERROR use of deprecated item: text
+    }
+
+    fn test_method_param<Foo: Trait>(foo: Foo) {
+        foo.trait_deprecated(); //~ ERROR use of deprecated item
+        Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item
+        <Foo>::trait_deprecated(&foo); //~ ERROR use of deprecated item
+        <Foo as Trait>::trait_deprecated(&foo); //~ ERROR use of deprecated item
+        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
+        Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        <Foo>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        <Foo as Trait>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+    }
+
+    fn test_method_object(foo: &Trait) {
+        foo.trait_deprecated(); //~ ERROR use of deprecated item
+        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
+    }
+
+    struct S;
+
+    impl DeprecatedTrait for S {} //~ ERROR use of deprecated item: text
+    trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated item: text
+
+    pub fn foo() {
+        let x = Stable {
+            override2: 3,
+            //~^ ERROR use of deprecated item
+        };
+
+        let _ = x.override2;
+        //~^ ERROR use of deprecated item
+
+        let Stable {
+            override2: _
+            //~^ ERROR use of deprecated item
+        } = x;
+        // all fine
+        let Stable { .. } = x;
+
+        let x = Stable2(1, 2, 3);
+
+        let _ = x.2;
+        //~^ ERROR use of deprecated item
+
+        let Stable2(_,
+                   _,
+                   _)
+            //~^ ERROR use of deprecated item
+            = x;
+        // all fine
+        let Stable2(..) = x;
+
+        let x = Deprecated {
+            //~^ ERROR use of deprecated item
+            inherit: 1,
+            //~^ ERROR use of deprecated item
+        };
+
+        let _ = x.inherit;
+        //~^ ERROR use of deprecated item
+
+        let Deprecated {
+            //~^ ERROR use of deprecated item
+            inherit: _,
+            //~^ ERROR use of deprecated item
+        } = x;
+
+        let Deprecated
+            //~^ ERROR use of deprecated item
+            { .. } = x;
+
+        let x = Deprecated2(1, 2, 3);
+        //~^ ERROR use of deprecated item
+
+        let _ = x.0;
+        //~^ ERROR use of deprecated item
+        let _ = x.1;
+        //~^ ERROR use of deprecated item
+        let _ = x.2;
+        //~^ ERROR use of deprecated item
+
+        let Deprecated2
+        //~^ ERROR use of deprecated item
+            (_,
+             //~^ ERROR use of deprecated item
+             _,
+             //~^ ERROR use of deprecated item
+             _)
+             //~^ ERROR use of deprecated item
+            = x;
+        let Deprecated2
+        //~^ ERROR use of deprecated item
+            // the patterns are all fine:
+            (..) = x;
+    }
+}
+
+mod inheritance {
+    use deprecation_lint::*;
+
+    fn test_inheritance() {
+        deprecated_mod::deprecated(); //~ ERROR use of deprecated item
+    }
+}
+
+mod this_crate {
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub fn deprecated() {}
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub fn deprecated_text() {}
+
+    pub struct MethodTester;
+
+    impl MethodTester {
+        #[deprecated(since = "1.0.0", note = "text")]
+        pub fn method_deprecated(&self) {}
+        #[deprecated(since = "1.0.0", note = "text")]
+        pub fn method_deprecated_text(&self) {}
+    }
+
+    pub trait Trait {
+        #[deprecated(since = "1.0.0", note = "text")]
+        fn trait_deprecated(&self) {}
+        #[deprecated(since = "1.0.0", note = "text")]
+        fn trait_deprecated_text(&self) {}
+    }
+
+    impl Trait for MethodTester {}
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub struct DeprecatedStruct {
+        i: isize
+    }
+    pub struct UnstableStruct {
+        i: isize
+    }
+    pub struct StableStruct {
+        i: isize
+    }
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub struct DeprecatedUnitStruct;
+
+    pub enum Enum {
+        #[deprecated(since = "1.0.0", note = "text")]
+        DeprecatedVariant,
+    }
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub struct DeprecatedTupleStruct(isize);
+
+    fn test() {
+        // Only the deprecated cases of the following should generate
+        // errors, because other stability attributes now have meaning
+        // only *across* crates, not within a single crate.
+
+        type Foo = MethodTester;
+        let foo = MethodTester;
+
+        deprecated(); //~ ERROR use of deprecated item
+        foo.method_deprecated(); //~ ERROR use of deprecated item
+        Foo::method_deprecated(&foo); //~ ERROR use of deprecated item
+        <Foo>::method_deprecated(&foo); //~ ERROR use of deprecated item
+        foo.trait_deprecated(); //~ ERROR use of deprecated item
+        Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item
+        <Foo>::trait_deprecated(&foo); //~ ERROR use of deprecated item
+        <Foo as Trait>::trait_deprecated(&foo); //~ ERROR use of deprecated item
+
+        deprecated_text(); //~ ERROR use of deprecated item: text
+        foo.method_deprecated_text(); //~ ERROR use of deprecated item: text
+        Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        <Foo>::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
+        Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        <Foo>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        <Foo as Trait>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+
+        let _ = DeprecatedStruct {
+            //~^ ERROR use of deprecated item
+            i: 0 //~ ERROR use of deprecated item
+        };
+
+        let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item
+
+        let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item
+
+        let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item
+    }
+
+    fn test_method_param<Foo: Trait>(foo: Foo) {
+        foo.trait_deprecated(); //~ ERROR use of deprecated item
+        Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item
+        <Foo>::trait_deprecated(&foo); //~ ERROR use of deprecated item
+        <Foo as Trait>::trait_deprecated(&foo); //~ ERROR use of deprecated item
+        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
+        Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        <Foo>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        <Foo as Trait>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+    }
+
+    fn test_method_object(foo: &Trait) {
+        foo.trait_deprecated(); //~ ERROR use of deprecated item
+        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
+    }
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    fn test_fn_body() {
+        fn fn_in_body() {}
+        fn_in_body(); //~ ERROR use of deprecated item: text
+    }
+
+    impl MethodTester {
+        #[deprecated(since = "1.0.0", note = "text")]
+        fn test_method_body(&self) {
+            fn fn_in_body() {}
+            fn_in_body(); //~ ERROR use of deprecated item: text
+        }
+    }
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub trait DeprecatedTrait {
+        fn dummy(&self) { }
+    }
+
+    struct S;
+
+    impl DeprecatedTrait for S { } //~ ERROR use of deprecated item
+
+    trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated item
+}
+
+mod this_crate2 {
+    struct Stable {
+        #[deprecated(since = "1.0.0", note = "text")]
+        override2: u8,
+    }
+
+    struct Stable2(u8,
+                   u8,
+                   #[deprecated(since = "1.0.0", note = "text")] u8);
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    struct Deprecated {
+        inherit: u8,
+    }
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    struct Deprecated2(u8,
+                       u8,
+                       u8);
+
+    pub fn foo() {
+        let x = Stable {
+            override2: 3,
+            //~^ ERROR use of deprecated item
+        };
+
+        let _ = x.override2;
+        //~^ ERROR use of deprecated item
+
+        let Stable {
+            override2: _
+            //~^ ERROR use of deprecated item
+        } = x;
+        // all fine
+        let Stable { .. } = x;
+
+        let x = Stable2(1, 2, 3);
+
+        let _ = x.2;
+        //~^ ERROR use of deprecated item
+
+        let Stable2(_,
+                   _,
+                   _)
+            //~^ ERROR use of deprecated item
+            = x;
+        // all fine
+        let Stable2(..) = x;
+
+        let x = Deprecated {
+            //~^ ERROR use of deprecated item
+            inherit: 1,
+            //~^ ERROR use of deprecated item
+        };
+
+        let _ = x.inherit;
+        //~^ ERROR use of deprecated item
+
+        let Deprecated {
+            //~^ ERROR use of deprecated item
+            inherit: _,
+            //~^ ERROR use of deprecated item
+        } = x;
+
+        let Deprecated
+            //~^ ERROR use of deprecated item
+            // the patterns are all fine:
+            { .. } = x;
+
+        let x = Deprecated2(1, 2, 3);
+        //~^ ERROR use of deprecated item
+
+        let _ = x.0;
+        //~^ ERROR use of deprecated item
+        let _ = x.1;
+        //~^ ERROR use of deprecated item
+        let _ = x.2;
+        //~^ ERROR use of deprecated item
+
+        let Deprecated2
+        //~^ ERROR use of deprecated item
+            (_,
+             //~^ ERROR use of deprecated item
+             _,
+             //~^ ERROR use of deprecated item
+             _)
+            //~^ ERROR use of deprecated item
+            = x;
+        let Deprecated2
+        //~^ ERROR use of deprecated item
+            // the patterns are all fine:
+            (..) = x;
+    }
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/deprecation-sanity.rs b/src/test/compile-fail/deprecation-sanity.rs
new file mode 100644 (file)
index 0000000..6ee5cd2
--- /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.
+
+// Various checks that deprecation attributes are used correctly
+
+#![feature(deprecated)]
+
+mod bogus_attribute_types_1 {
+    #[deprecated(since = "a", note = "a", reason)] //~ ERROR unknown meta item 'reason'
+    fn f1() { }
+
+    #[deprecated(since = "a", note)] //~ ERROR incorrect meta item
+    fn f2() { }
+
+    #[deprecated(since, note = "a")] //~ ERROR incorrect meta item
+    fn f3() { }
+
+    #[deprecated(since = "a", note(b))] //~ ERROR incorrect meta item
+    fn f5() { }
+
+    #[deprecated(since(b), note = "a")] //~ ERROR incorrect meta item
+    fn f6() { }
+}
+
+#[deprecated(since = "a", note = "b")]
+#[deprecated(since = "a", note = "b")]
+fn multiple1() { } //~ ERROR multiple deprecated attributes
+
+#[deprecated(since = "a", since = "b", note = "c")] //~ ERROR multiple 'since' items
+fn f1() { }
+
+fn main() { }
index 42b67337c64e58bccdbfdfc2890937a5b2532835..3b0f8ee5bda7ac041e72feadd4105ae706e96766 100644 (file)
@@ -33,6 +33,7 @@ trait Qux<T,T> {}
 
 impl<T,T> Qux<T,T> for Option<T> {}
 //~^ ERROR the name `T` is already used
+//~^^ ERROR the type parameter `T` is not constrained
 
 fn main() {
 }
index 67167086b9c4a8ac546bd17c5793a04652f57d3c..6ae0dad0e7bfbd485a4370d0b2fd06f86c770423 100644 (file)
 
 // Can't use empty braced struct as constant or constructor function
 
+// aux-build:empty-struct.rs
+
 #![feature(braced_empty_structs)]
 
+extern crate empty_struct;
+use empty_struct::*;
+
 struct Empty1 {}
 
 enum E {
-    Empty2 {}
+    Empty3 {}
 }
 
 fn main() {
     let e1 = Empty1; //~ ERROR `Empty1` is the name of a struct or struct variant
     let e1 = Empty1(); //~ ERROR `Empty1` is the name of a struct or struct variant
-    let e2 = E::Empty2; //~ ERROR `E::Empty2` is the name of a struct or struct variant
-    let e2 = E::Empty2(); //~ ERROR `E::Empty2` is the name of a struct or struct variant
+    let e3 = E::Empty3; //~ ERROR `E::Empty3` is the name of a struct or struct variant
+    let e3 = E::Empty3(); //~ ERROR `E::Empty3` is the name of a struct or struct variant
+
+    // FIXME: non-local struct kind should be known early (e.g. kept in `DefStruct`)
+    // let xe1 = XEmpty1; // ERROR `XEmpty1` is the name of a struct or struct variant
+    let xe1 = XEmpty1(); //~ ERROR expected function, found `empty_struct::XEmpty1`
+    let xe3 = XE::Empty3; //~ ERROR no associated item named `Empty3` found for type
+    let xe3 = XE::Empty3(); //~ ERROR no associated item named `Empty3` found for type
 }
index 6a6c3f16c04afed220bc08c3777f9272147ce677..27c97a3a5509601919d1e96958428120d2702f6d 100644 (file)
 
 // Can't use empty braced struct as constant pattern
 
+// aux-build:empty-struct.rs
+
 #![feature(braced_empty_structs)]
 
+extern crate empty_struct;
+use empty_struct::*;
+
 struct Empty1 {}
 
 enum E {
-    Empty2 {}
+    Empty3 {}
 }
 
 fn main() {
     let e1 = Empty1 {};
-    let e2 = E::Empty2 {};
+    let e3 = E::Empty3 {};
+    let xe1 = XEmpty1 {};
+    let xe3 = XE::XEmpty3 {};
 
     match e1 {
         Empty1 => () // Not an error, `Empty1` is interpreted as a new binding
     }
-    match e2 {
-        E::Empty2 => () //~ ERROR `E::Empty2` does not name a tuple variant or a tuple struct
+    match e3 {
+        E::Empty3 => () //~ ERROR `E::Empty3` does not name a tuple variant or a tuple struct
+    }
+    match xe1 {
+        XEmpty1 => () // Not an error, `XEmpty1` is interpreted as a new binding
+    }
+    match xe3 {
+        XE::XEmpty3 => () //~ ERROR no associated item named `XEmpty3` found for type
     }
 }
index d98d64b712a8b42b36705daa7217813b9983b537..3436e2a2cd752c298ea13b6971583909d6967784 100644 (file)
 
 // Can't use empty braced struct as enum pattern
 
+// aux-build:empty-struct.rs
+
 #![feature(braced_empty_structs)]
 
+extern crate empty_struct;
+use empty_struct::*;
+
 struct Empty1 {}
 
 fn main() {
     let e1 = Empty1 {};
+    let xe1 = XEmpty1 {};
 
     // Rejected by parser as yet
     // match e1 {
     //     Empty1() => () // ERROR unresolved enum variant, struct or const `Empty1`
     // }
+    // match xe1 {
+    //     XEmpty1() => () // ERROR unresolved enum variant, struct or const `XEmpty1`
+    // }
     match e1 {
         Empty1(..) => () //~ ERROR unresolved enum variant, struct or const `Empty1`
     }
+    match xe1 {
+        XEmpty1(..) => () //~ ERROR `XEmpty1` does not name a tuple variant or a tuple struct
+    }
 }
index 9fae203f3894d4f42232953d7f4e978a825bd1c0..ca51a1cfc2186f64214843b591385cb82fb8ab92 100644 (file)
 
 // Can't use empty braced struct as enum pattern
 
+// aux-build:empty-struct.rs
+
 #![feature(braced_empty_structs)]
 
+extern crate empty_struct;
+use empty_struct::*;
+
 enum E {
-    Empty2 {}
+    Empty3 {}
 }
 
 fn main() {
-    let e2 = E::Empty2 {};
+    let e3 = E::Empty3 {};
+    let xe3 = XE::XEmpty3 {};
 
     // Rejected by parser as yet
-    // match e2 {
-    //     E::Empty2() => () // ERROR `E::Empty2` does not name a tuple variant or a tuple struct
+    // match e3 {
+    //     E::Empty3() => () // ERROR `E::Empty3` does not name a tuple variant or a tuple struct
     // }
-    match e2 {
-        E::Empty2(..) => () //~ ERROR `E::Empty2` does not name a tuple variant or a tuple struct
+    // match xe3 {
+    //     E::Empty3() => () // ERROR `XE::XEmpty3` does not name a tuple variant or a tuple struct
+    // }
+    match e3 {
+        E::Empty3(..) => () //~ ERROR `E::Empty3` does not name a tuple variant or a tuple struct
+    }
+    match xe3 {
+        XE::XEmpty3(..) => () //~ ERROR no associated item named `XEmpty3` found for type
     }
 }
index 199065665b9f65842c236516a22872f08da35071..822ee9e0dbc106923d8bc608674b30fabcb8f143 100644 (file)
 
 // Can't use unit struct as constructor function
 
+// aux-build:empty-struct.rs
+
 #![feature(braced_empty_structs)]
 
-struct Empty1;
+extern crate empty_struct;
+use empty_struct::*;
+
+struct Empty2;
 
 enum E {
-    Empty2
+    Empty4
 }
 
 fn main() {
-    let e1 = Empty1(); //~ ERROR expected function, found `Empty1`
-    let e2 = E::Empty2(); //~ ERROR expected function, found `E`
+    let e2 = Empty2(); //~ ERROR expected function, found `Empty2`
+    let e4 = E::Empty4(); //~ ERROR expected function, found `E`
+    let xe2 = XEmpty2(); //~ ERROR expected function, found `empty_struct::XEmpty2`
+    let xe4 = XE::XEmpty4(); //~ ERROR  expected function, found `empty_struct::XE`
 }
index 7e13f539bb0438c37640980e6cea60f34d4fbb30..0f54d1b7365854c99d46c05d3614e55be51b5d57 100644 (file)
 
 // Can't use unit struct as enum pattern
 
+// aux-build:empty-struct.rs
+
 #![feature(rustc_attrs)]
 // remove prior feature after warning cycle and promoting warnings to errors
 #![feature(braced_empty_structs)]
 
-struct Empty1;
+extern crate empty_struct;
+use empty_struct::*;
+
+struct Empty2;
 
 enum E {
-    Empty2
+    Empty4
 }
 
 // remove attribute after warning cycle and promoting warnings to errors
 #[rustc_error]
 fn main() { //~ ERROR: compilation successful
-    let e1 = Empty1;
-    let e2 = E::Empty2;
+    let e2 = Empty2;
+    let e4 = E::Empty4;
+    let xe2 = XEmpty2;
+    let xe4 = XE::XEmpty4;
 
     // Rejected by parser as yet
-    // match e1 {
-    //     Empty1() => () // ERROR `Empty1` does not name a tuple variant or a tuple struct
+    // match e2 {
+    //     Empty2() => () // ERROR `Empty2` does not name a tuple variant or a tuple struct
     // }
-    match e1 {
-        Empty1(..) => () //~ WARN `Empty1` does not name a tuple variant or a tuple struct
+    // match xe2 {
+    //     XEmpty2() => () // ERROR `XEmpty2` does not name a tuple variant or a tuple struct
+    // }
+    match e2 {
+        Empty2(..) => () //~ WARN `Empty2` does not name a tuple variant or a tuple struct
+            //~^ WARN hard error
+    }
+    match xe2 {
+        XEmpty2(..) => () //~ WARN `XEmpty2` does not name a tuple variant or a tuple struct
+            //~^ WARN hard error
     }
     // Rejected by parser as yet
-    // match e2 {
-    //     E::Empty2() => () // ERROR `E::Empty2` does not name a tuple variant or a tuple struct
+    // match e4 {
+    //     E::Empty4() => () // ERROR `E::Empty4` does not name a tuple variant or a tuple struct
     // }
-    match e2 {
-        E::Empty2(..) => () //~ WARN `E::Empty2` does not name a tuple variant or a tuple struct
+    // match xe4 {
+    //     XE::XEmpty4() => (), // ERROR `XE::XEmpty4` does not name a tuple variant or a tuple
+    //     _ => {},
+    // }
+    match e4 {
+        E::Empty4(..) => () //~ WARN `E::Empty4` does not name a tuple variant or a tuple struct
+            //~^ WARN hard error
+    }
+    match xe4 {
+        XE::XEmpty4(..) => (), //~ WARN `XE::XEmpty4` does not name a tuple variant or a tuple
+            //~^ WARN hard error
+        _ => {},
     }
 }
index cdf7d026d5eebd4b26039d7b9508496c51b8aad4..84a27a382006988820be6dac882733a5b7b3c9f0 100644 (file)
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(negate_unsigned)]
 
 #[repr(u8)] //~ NOTE discriminant type specified here
 enum Eu8 {
diff --git a/src/test/compile-fail/feature-gate-abi-vectorcall.rs b/src/test/compile-fail/feature-gate-abi-vectorcall.rs
new file mode 100644 (file)
index 0000000..79f3c8d
--- /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.
+
+extern "vectorcall" {   //~ ERROR vectorcall is experimental and subject to change
+    fn bar();
+}
+
+extern "vectorcall" fn baz() {  //~ ERROR vectorcall is experimental and subject to change
+}
+
+fn main() {
+}
index b1c73fab4ffa61f2623ad2128c12c3272c47bef1..15cc17b19db33bfc9bb1b2695d95fa6bdfb3df20 100644 (file)
@@ -8,8 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Test that negating unsigned integers is gated by `negate_unsigned` feature
-// gate
+// Test that negating unsigned integers doesn't compile
 
 struct S;
 impl std::ops::Neg for S {
@@ -18,21 +17,26 @@ impl std::ops::Neg for S {
 }
 
 const _MAX: usize = -1;
-//~^ ERROR unary negation of unsigned integers may be removed in the future
+//~^ ERROR unary negation of unsigned integer
+//~| HELP use a cast or the `!` operator
 
 fn main() {
     let a = -1;
-    //~^ ERROR unary negation of unsigned integers may be removed in the future
+    //~^ ERROR unary negation of unsigned integer
+    //~| HELP use a cast or the `!` operator
     let _b : u8 = a; // for infering variable a to u8.
 
     -a;
-    //~^ ERROR unary negation of unsigned integers may be removed in the future
+    //~^ ERROR unary negation of unsigned integer
+    //~| HELP use a cast or the `!` operator
 
     let _d = -1u8;
-    //~^ ERROR unary negation of unsigned integers may be removed in the future
+    //~^ ERROR unary negation of unsigned integer
+    //~| HELP use a cast or the `!` operator
 
     for _ in -10..10u8 {}
-    //~^ ERROR unary negation of unsigned integers may be removed in the future
+    //~^ ERROR unary negation of unsigned integer
+    //~| HELP use a cast or the `!` operator
 
     -S; // should not trigger the gate; issue 26840
 }
index f7bd2fcbceb4fe42334170a2add8fafce0ac654a..31c055f229c3e7a66bdc216563393f6420548ef1 100644 (file)
@@ -8,17 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(repr_simd, core_simd)]
-#![allow(dead_code, deprecated)]
+#![feature(repr_simd)]
+#![allow(dead_code)]
 
-use std::simd::f32x4;
-
-#[repr(simd)] #[derive(Copy, Clone)] #[repr(C)] struct LocalSimd(u8, u8);
+#[repr(simd)]
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct LocalSimd(u8, u8);
 
 extern {
-    fn foo() -> f32x4; //~ ERROR use of SIMD type
-    fn bar(x: f32x4); //~ ERROR use of SIMD type
-
     fn baz() -> LocalSimd; //~ ERROR use of SIMD type
     fn qux(x: LocalSimd); //~ ERROR use of SIMD type
 }
index 4163d531e870e9003046e676de9a7d6ea5898c89..2cb73cf2ef79b0ecb827442e58335ac2b2ebae79 100644 (file)
@@ -8,10 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// ignore-test this should fail to compile (#15844)
+#![deny(no_mangle_generic_items)]
 
 #[no_mangle]
-fn foo<T>() {} //~ ERROR generic functions must be mangled
+pub fn foo<T>() {} //~ ERROR generic functions must be mangled
 
 #[no_mangle]
-extern fn foo<T>() {} //~ ERROR generic functions must be mangled
+pub extern fn bar<T>() {} //~ ERROR generic functions must be mangled
+
+fn main() {}
index b7a5b991c6d561be4460ccc99ec16da95b7f7a7e..2c730531b823a85f86d2e0cd650fd3027ef5ed89 100644 (file)
@@ -13,6 +13,4 @@ fn main() {
     //~^ ERROR if and else have incompatible types
     //~| expected `i32`
     //~| found `u32`
-    //~| expected i32
-    //~| found u32
 }
index cf2a70deee513cf3945a6389557febc52367ec1f..56b681378cc9daf1141d41845dc302ee66d33463 100644 (file)
@@ -10,7 +10,7 @@
 
 // see #9186
 
-enum Bar<T> { What }
+enum Bar<T> { What } //~ ERROR parameter `T` is never used
 
 fn foo<T>() {
     static a: Bar<T> = Bar::What;
index 5d9314faef98219d1dbcd8c0e371af9569a9f252..8f04b58b77868a3076b820651ec88d1c97c58407 100644 (file)
@@ -43,79 +43,55 @@ fn main() {
     //~^ ERROR mismatched types
     //~| expected `i8`
     //~| found `i16`
-    //~| expected i8
-    //~| found i16
     id_i8(a32);
     //~^ ERROR mismatched types
     //~| expected `i8`
     //~| found `i32`
-    //~| expected i8
-    //~| found i32
     id_i8(a64);
     //~^ ERROR mismatched types
     //~| expected `i8`
     //~| found `i64`
-    //~| expected i8
-    //~| found i64
 
     id_i16(a8);
     //~^ ERROR mismatched types
     //~| expected `i16`
     //~| found `i8`
-    //~| expected i16
-    //~| found i8
     id_i16(a16); // ok
     id_i16(a32);
     //~^ ERROR mismatched types
     //~| expected `i16`
     //~| found `i32`
-    //~| expected i16
-    //~| found i32
     id_i16(a64);
     //~^ ERROR mismatched types
     //~| expected `i16`
     //~| found `i64`
-    //~| expected i16
-    //~| found i64
 
     id_i32(a8);
     //~^ ERROR mismatched types
     //~| expected `i32`
     //~| found `i8`
-    //~| expected i32
-    //~| found i8
     id_i32(a16);
     //~^ ERROR mismatched types
     //~| expected `i32`
     //~| found `i16`
-    //~| expected i32
-    //~| found i16
     id_i32(a32); // ok
     id_i32(a64);
     //~^ ERROR mismatched types
     //~| expected `i32`
     //~| found `i64`
-    //~| expected i32
-    //~| found i64
 
     id_i64(a8);
     //~^ ERROR mismatched types
     //~| expected `i64`
     //~| found `i8`
-    //~| expected i64
-    //~| found i8
     id_i64(a16);
     //~^ ERROR mismatched types
     //~| expected `i64`
     //~| found `i16`
-    //~| expected i64
-    //~| found i16
     id_i64(a32);
     //~^ ERROR mismatched types
     //~| expected `i64`
     //~| found `i32`
-    //~| expected i64
-    //~| found i32
     id_i64(a64); // ok
 
     id_i8(c8); // ok
@@ -123,79 +99,55 @@ fn main() {
     //~^ ERROR mismatched types
     //~| expected `i8`
     //~| found `i16`
-    //~| expected i8
-    //~| found i16
     id_i8(c32);
     //~^ ERROR mismatched types
     //~| expected `i8`
     //~| found `i32`
-    //~| expected i8
-    //~| found i32
     id_i8(c64);
     //~^ ERROR mismatched types
     //~| expected `i8`
     //~| found `i64`
-    //~| expected i8
-    //~| found i64
 
     id_i16(c8);
     //~^ ERROR mismatched types
     //~| expected `i16`
     //~| found `i8`
-    //~| expected i16
-    //~| found i8
     id_i16(c16); // ok
     id_i16(c32);
     //~^ ERROR mismatched types
     //~| expected `i16`
     //~| found `i32`
-    //~| expected i16
-    //~| found i32
     id_i16(c64);
     //~^ ERROR mismatched types
     //~| expected `i16`
     //~| found `i64`
-    //~| expected i16
-    //~| found i64
 
     id_i32(c8);
     //~^ ERROR mismatched types
     //~| expected `i32`
     //~| found `i8`
-    //~| expected i32
-    //~| found i8
     id_i32(c16);
     //~^ ERROR mismatched types
     //~| expected `i32`
     //~| found `i16`
-    //~| expected i32
-    //~| found i16
     id_i32(c32); // ok
     id_i32(c64);
     //~^ ERROR mismatched types
     //~| expected `i32`
     //~| found `i64`
-    //~| expected i32
-    //~| found i64
 
     id_i64(a8);
     //~^ ERROR mismatched types
     //~| expected `i64`
     //~| found `i8`
-    //~| expected i64
-    //~| found i8
     id_i64(a16);
     //~^ ERROR mismatched types
     //~| expected `i64`
-    //~| found `i16`
-    //~| expected i64
     //~| found i16
     id_i64(a32);
     //~^ ERROR mismatched types
     //~| expected `i64`
     //~| found `i32`
-    //~| expected i64
-    //~| found i32
     id_i64(a64); // ok
 
     id_u8(b8); // ok
@@ -203,78 +155,54 @@ fn main() {
     //~^ ERROR mismatched types
     //~| expected `u8`
     //~| found `u16`
-    //~| expected u8
-    //~| found u16
     id_u8(b32);
     //~^ ERROR mismatched types
     //~| expected `u8`
     //~| found `u32`
-    //~| expected u8
-    //~| found u32
     id_u8(b64);
     //~^ ERROR mismatched types
     //~| expected `u8`
     //~| found `u64`
-    //~| expected u8
-    //~| found u64
 
     id_u16(b8);
     //~^ ERROR mismatched types
     //~| expected `u16`
     //~| found `u8`
-    //~| expected u16
-    //~| found u8
     id_u16(b16); // ok
     id_u16(b32);
     //~^ ERROR mismatched types
     //~| expected `u16`
     //~| found `u32`
-    //~| expected u16
-    //~| found u32
     id_u16(b64);
     //~^ ERROR mismatched types
     //~| expected `u16`
     //~| found `u64`
-    //~| expected u16
-    //~| found u64
 
     id_u32(b8);
     //~^ ERROR mismatched types
     //~| expected `u32`
     //~| found `u8`
-    //~| expected u32
-    //~| found u8
     id_u32(b16);
     //~^ ERROR mismatched types
     //~| expected `u32`
     //~| found `u16`
-    //~| expected u32
-    //~| found u16
     id_u32(b32); // ok
     id_u32(b64);
     //~^ ERROR mismatched types
     //~| expected `u32`
     //~| found `u64`
-    //~| expected u32
-    //~| found u64
 
     id_u64(b8);
     //~^ ERROR mismatched types
     //~| expected `u64`
     //~| found `u8`
-    //~| expected u64
-    //~| found u8
     id_u64(b16);
     //~^ ERROR mismatched types
     //~| expected `u64`
     //~| found `u16`
-    //~| expected u64
-    //~| found u16
     id_u64(b32);
     //~^ ERROR mismatched types
     //~| expected `u64`
     //~| found `u32`
-    //~| expected u64
-    //~| found u32
     id_u64(b64); // ok
 }
index 2249741cdaac5964b9913bfc07f5d003ce2dd1ef..33fbdce4ee25a2b50fd3a4474bc31c840ccd7879 100644 (file)
@@ -9,8 +9,8 @@
 // except according to those terms.
 
 trait Trait {
-    fn outer(self) {
-        fn inner(_: Self) {
+    fn outer(&self) {
+        fn inner(_: &Self) {
             //~^ ERROR can't use type parameters from outer function
             //~^^ ERROR use of `Self` outside of an impl or trait
         }
index c53e5760941f15d9611f00255eafc5cc1d8e6b95..775412a12ca6a99c1efc68f8afa9a4f636be3b22 100644 (file)
@@ -17,13 +17,9 @@ fn main() {
     //~^ ERROR mismatched types
     //~| expected `i16`
     //~| found `isize`
-    //~| expected i16
-    //~| found isize
 
     bar(1*(1 as usize));
     //~^ ERROR mismatched types
     //~| expected `u32`
     //~| found `usize`
-    //~| expected u32
-    //~| found usize
 }
index ea0d880f4a1cc6a04f9da5415257a3a780e97379..1635a8f69a6cd2b481f91adf5c99402dfc6923d8 100644 (file)
@@ -10,7 +10,7 @@
 
 trait FromStructReader<'a> { }
 trait ResponseHook {
-     fn get<'a, T: FromStructReader<'a>>(&'a self);
+     fn get(&self);
 }
 fn foo(res : Box<ResponseHook>) { res.get } //~ ERROR attempted to take value of method
 fn main() {}
index f5d158d64e19fa0a776a78d61568b76431290587..7643310298da3d84c4e001caefc8d7e52422da53 100644 (file)
@@ -13,11 +13,14 @@ trait Node {
 }
 
 trait Graph<N: Node> {
-    fn nodes<'a, I: Iterator<Item=&'a N>>(&'a self) -> I;
+    fn nodes<'a, I: Iterator<Item=&'a N>>(&'a self) -> I
+        where N: 'a;
 }
 
 impl<N: Node> Graph<N> for Vec<N> {
-    fn nodes<'a, I: Iterator<Item=&'a N>>(&self) -> I {
+    fn nodes<'a, I: Iterator<Item=&'a N>>(&self) -> I
+        where N: 'a
+    {
         self.iter() //~ ERROR mismatched types
     }
 }
index ce5fa1f1fe1a5e66e4fc4cb9fc54f8d4de85c873..5f8ccd0b0634ee22b1c9e313b319a74f2cdedd25 100644 (file)
@@ -11,7 +11,7 @@
 trait Foo {
     fn bar(&self);
     fn baz(&self) { }
-    fn bah(_: Option<Self>) { }
+    fn bah(_: Option<&Self>) { }
 }
 
 struct BarTy {
index 51deb99a4f2cda7747feae6e2df16f83c0f80e9e..c6c1a0fd17781ab1209e1a4923073f294e043ba9 100644 (file)
@@ -12,7 +12,7 @@ use std::fmt::Debug;
 
 trait Str {}
 
-trait Something {
+trait Something: Sized {
     fn yay<T: Debug>(_: Option<Self>, thing: &[T]);
 }
 
index a0b7935550cd01b926f4fd8999c08b90c60b9729..e640ba3f00fb6d7d2faf69b529d1ab62d439c0fd 100644 (file)
@@ -14,7 +14,7 @@ use foo::NoResult; // Through a re-export
 mod foo {
     pub use self::MyEnum::NoResult;
 
-    enum MyEnum {
+    pub enum MyEnum {
         Result,
         NoResult
     }
index b4791eba76e264fb3c071feaaf502b54dfa28a6d..4381bf22e2acee44f667826dd86fdd94cd92725b 100644 (file)
@@ -15,12 +15,12 @@ struct Foo<'a> {
 impl <'a> Foo<'a>{
     fn bar(self: &mut Foo) {
     //~^ mismatched types
-    //~| expected `Foo<'a>`
-    //~| found `Foo<'_>`
+    //~| expected `&mut Foo<'a>`
+    //~| found `&mut Foo<'_>`
     //~| lifetime mismatch
     //~| mismatched types
-    //~| expected `Foo<'a>`
-    //~| found `Foo<'_>`
+    //~| expected `&mut Foo<'a>`
+    //~| found `&mut Foo<'_>`
     //~| lifetime mismatch
     }
 }
index eabdb36a7efc6989eee874982a150201a5bc08ed..f11d482ea16db55a42a9ad695a80a1c01f5c192b 100644 (file)
@@ -16,10 +16,6 @@ impl Pair<
     isize
 > {
     fn say(self: &Pair<&str, isize>) {
-//~^ ERROR mismatched types
-//~| expected `Pair<&'static str, isize>`
-//~| found `Pair<&str, isize>`
-//~| lifetime mismatch
         println!("{}", self);
     }
 }
diff --git a/src/test/compile-fail/issue-18118-2.rs b/src/test/compile-fail/issue-18118-2.rs
new file mode 100644 (file)
index 0000000..1fbf48f
--- /dev/null
@@ -0,0 +1,17 @@
+// 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 fn main() {
+    const z: &'static isize = {
+        static p: isize = 3;
+        &p
+        //~^ ERROR constants cannot refer to other statics, insert an intermediate constant instead
+    };
+}
index c5370879cc2db1b955f4af66f6a66a0d5f5e656e..9c8ed314d22d3971d798a00fe8b10696cbf59b9c 100644 (file)
@@ -13,6 +13,5 @@ pub fn main() {
         let p = 3;
         //~^ ERROR blocks in constants are limited to items and tail expressions
         &p
-        //~^ ERROR paths in constants may only refer to constants or functions
     };
 }
index 7d95082079f9f13bb2450dfa69024265ef6c44d3..300fc5a6ef736427ba86f9b33c6b92b342b4717d 100644 (file)
@@ -14,7 +14,7 @@ use std::any::TypeId;
 trait Private<P, R> {
     fn call(&self, p: P, r: R);
 }
-pub trait Public: Private< //~ ERROR private trait in exported type parameter bound
+pub trait Public: Private< //~ ERROR private trait in public interface
     <Self as Public>::P,
     <Self as Public>::R
 > {
index 5f6216a898a0b0351da1eedee6977fc948a58234..7a6d012a3b6054197879a581b577807953ef9e24 100644 (file)
@@ -19,14 +19,12 @@ impl Foo for Thing {
 
 #[inline(never)]
 fn foo(b: &Bar) {
+    //~^ ERROR E0038
     b.foo(&0)
-    //~^ ERROR the trait `Foo` is not implemented for the type `Bar`
-    //~| ERROR E0038
-    //~| WARNING E0038
 }
 
 fn main() {
     let mut thing = Thing;
-    let test: &Bar = &mut thing; //~ ERROR E0038
+    let test: &Bar = &mut thing;
     foo(test);
 }
index aae77c90b6bf2980e9820ae9fe0d9e363d3c59c1..322952ffef1e68aeca87fef0b0118e94adea117e 100644 (file)
@@ -19,10 +19,10 @@ impl Qiz for Foo {
 
 struct Bar {
   foos: &'static [&'static (Qiz + 'static)]
+//~^ ERROR E0038
 }
 
 const FOO : Foo = Foo;
 const BAR : Bar = Bar { foos: &[&FOO]};
-//~^ ERROR E0038
 
 fn main() { }
index c6ff82364b3e7cd57c279b9e4772aab1d386c51f..7ec3093a6e0588533f7d2ee4844d03b7ae60b044 100644 (file)
@@ -14,7 +14,7 @@ trait From<Src> {
     fn from(src: Src) -> <Self as From<Src>>::Output;
 }
 
-trait To {
+trait To: Sized {
     fn to<Dst: From<Self>>(self) ->
         <Dst as From<Self>>::Dst
         //~^ ERROR use of undeclared associated type `From::Dst`
index 041289c2ccdbdb4af3393b0e526e89c61cf3e6f4..aaf27ba527b18fb936fa49db9a246dd4bc4f380e 100644 (file)
@@ -15,13 +15,10 @@ trait From<Src> {
 }
 
 trait To {
-    fn to<Dst>(
-        self //~ error: the trait `core::marker::Sized` is not implemented
+    fn to<Dst>(  //~ ERROR the trait `core::marker::Sized` is not implemented
+        self
     ) -> <Dst as From<Self>>::Result where Dst: From<Self> {
-        From::from( //~ error: the trait `core::marker::Sized` is not implemented
-            //~^ ERROR E0277
-            self
-        )
+        From::from(self)
     }
 }
 
index a4b25ab9e56b31142bf2cb274bfc1424c2ee2103..99dd22a888cb56508f759f0a2183cfe23354200e 100644 (file)
@@ -62,7 +62,7 @@ fn usize<'usize>(usize: &'usize usize) -> &'usize usize { usize }
 fn main() {
     let bool = true;
     match bool {
-        str @ true => if str { i32 as i64 } else { 0 },
+        str @ true => if str { i32 as i64 } else { i64 },
         false => i64,
-    }
+    };
 }
index a38278eae2411f1c92871586e0d33d907f51faab..3f96a9c342283d0678db124b5d7bfe0e4ca200bc 100644 (file)
@@ -38,6 +38,8 @@ impl<'a> Publisher<'a> for MyStruct<'a> {
     fn subscribe(&mut self, t : Box<Subscriber<Input=<Self as Publisher>::Output> + '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-21659-show-relevant-trait-impls-1.rs b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-1.rs
new file mode 100644 (file)
index 0000000..8ea63fd
--- /dev/null
@@ -0,0 +1,40 @@
+// 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 Foo<A> {
+    fn foo(&self, a: A) -> A {
+        a
+    }
+}
+
+trait NotRelevant<A> {
+    fn nr(&self, a: A) -> A {
+        a
+    }
+}
+
+struct Bar;
+
+impl Foo<i32> for Bar {}
+
+impl Foo<u8> for Bar {}
+
+impl NotRelevant<usize> for Bar {}
+
+fn main() {
+    let f1 = Bar;
+
+    f1.foo(1usize);
+    //~^ error: the trait `Foo<usize>` is not implemented for the type `Bar`
+    //~| help: the following implementations were found:
+    //~| help:   <Bar as Foo<i32>>
+    //~| help:   <Bar as Foo<u8>>
+    //~| help: run `rustc --explain E0277`
+}
diff --git a/src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs
new file mode 100644 (file)
index 0000000..9460ac1
--- /dev/null
@@ -0,0 +1,47 @@
+// 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 Foo<A> {
+    fn foo(&self, a: A) -> A {
+        a
+    }
+}
+
+trait NotRelevant<A> {
+    fn nr(&self, a: A) -> A {
+        a
+    }
+}
+
+struct Bar;
+
+impl Foo<i8> for Bar {}
+impl Foo<i16> for Bar {}
+impl Foo<i32> for Bar {}
+
+impl Foo<u8> for Bar {}
+impl Foo<u16> for Bar {}
+impl Foo<u32> for Bar {}
+
+impl NotRelevant<usize> for Bar {}
+
+fn main() {
+    let f1 = Bar;
+
+    f1.foo(1usize);
+    //~^ error: the trait `Foo<usize>` is not implemented for the type `Bar`
+    //~| help: the following implementations were found:
+    //~| help:   <Bar as Foo<i8>>
+    //~| help:   <Bar as Foo<i16>>
+    //~| help:   <Bar as Foo<i32>>
+    //~| help:   <Bar as Foo<u8>>
+    //~| help: and 2 others
+    //~| help: run `rustc --explain E0277`
+}
diff --git a/src/test/compile-fail/issue-21659-show-relevant-trait-impls-3.rs b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-3.rs
new file mode 100644 (file)
index 0000000..0bb944e
--- /dev/null
@@ -0,0 +1,34 @@
+// 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 Foo<A> {
+    fn foo(&self, a: A) -> A {
+        a
+    }
+}
+
+trait NotRelevant<A> {
+    fn nr(&self, a: A) -> A {
+        a
+    }
+}
+
+struct Bar;
+
+impl NotRelevant<usize> for Bar {}
+
+fn main() {
+    let f1 = Bar;
+
+    f1.foo(1usize);
+    //~^ error: method named `foo` found for type `Bar` in the current scope
+    //~| help: items from traits can only be used if the trait is implemented and in scope
+    //~| help: candidate #1: `Foo`
+}
index f768d6c00ecdb1df44ed1d4e34e31db1866ac42b..6ddfa4c8e3e57c5473b070716e04217b652f7e6f 100644 (file)
@@ -17,11 +17,11 @@ trait Foo {
     fn foo(self);
 }
 
-fn foo<'a,'b,T>(x: &'a T, y: &'b T)
+fn foo<'a,'b,T>(x: &'a T, y: &'b T) //~ ERROR type annotations required
     where &'a T : Foo,
           &'b T : Foo
 {
-    x.foo(); //~ ERROR type annotations required
+    x.foo();
     y.foo();
 }
 
diff --git a/src/test/compile-fail/issue-22912.rs b/src/test/compile-fail/issue-22912.rs
deleted file mode 100644 (file)
index f4536ce..0000000
+++ /dev/null
@@ -1,41 +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.
-
-pub struct PublicType;
-struct PrivateType;
-
-pub trait PublicTrait {
-    type Item;
-}
-
-trait PrivateTrait {
-    type Item;
-}
-
-impl PublicTrait for PublicType {
-    type Item = PrivateType;  //~ ERROR private type in exported type signature
-}
-
-// OK
-impl PublicTrait for PrivateType {
-    type Item = PrivateType;
-}
-
-// OK
-impl PrivateTrait for PublicType {
-    type Item = PrivateType;
-}
-
-// OK
-impl PrivateTrait for PrivateType {
-    type Item = PrivateType;
-}
-
-fn main() {}
index c08cdd72b38258e74645ed96d7fc4a9b1ce3f941..1a9bb4c29f3e0183ad2f0a6a0cf45e5dd44f0214 100644 (file)
@@ -13,5 +13,5 @@ fn main()
 {
     fn bar(x:i32) ->i32 { 3*x };
     let b:Box<Any> = Box::new(bar as fn(_)->_);
-    b.downcast_ref::<fn(_)->_>(); //~ ERROR E0101
+    b.downcast_ref::<fn(_)->_>(); //~ ERROR E0282
 }
index 4b1010781ff3be251241fcf0be444c4ac7be3e99..4acb1f70d343c459825c7a6422755d9623b3aac4 100644 (file)
@@ -13,5 +13,6 @@ pub trait ToNbt<T> {
 }
 
 impl ToNbt<Self> {} //~ ERROR use of `Self` outside of an impl or trait
+//~^ ERROR the trait `ToNbt` cannot be made into an object
 
 fn main() {}
index 48cc27e228940b74f93d71640562f64224e88f59..6b81afe13c671d5fca639f058b7c8fdc4ada1e03 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 trait Groom {
-    fn shave();
+    fn shave(other: usize);
 }
 
 pub struct cat {
@@ -30,7 +30,7 @@ impl MaybeDog {
 }
 
 impl Groom for cat {
-  fn shave(&self, other: usize) {
+  fn shave(other: usize) {
     whiskers -= other;
     //~^ ERROR: unresolved name `whiskers`. Did you mean `self.whiskers`?
     shave(4);
index b5c875cc4cbd200c57efa5d4bdcaacc319296b3e..ef91188c5d166bd1d8de2e11c18a3a82517627f2 100644 (file)
@@ -12,7 +12,7 @@ struct S(String);
 
 impl S {
     fn f(self: *mut S) -> String { self.0 }
-    //~^ ERROR mismatched self type
+    //~^ ERROR mismatched method receiver
 }
 
 fn main() { S("".to_owned()).f(); }
diff --git a/src/test/compile-fail/issue-26656.rs b/src/test/compile-fail/issue-26656.rs
new file mode 100644 (file)
index 0000000..e5fa654
--- /dev/null
@@ -0,0 +1,52 @@
+// 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 #26656: Verify that trait objects cannot bypass dropck.
+
+// Using this instead of Fn etc. to take HRTB out of the equation.
+trait Trigger<B> { fn fire(&self, b: &mut B); }
+impl<B: Button> Trigger<B> for () {
+    fn fire(&self, b: &mut B) {
+        b.push();
+    }
+}
+
+// Still unsound Zook
+trait Button { fn push(&self); }
+struct Zook<B> { button: B, trigger: Box<Trigger<B>+'static> }
+
+impl<B> Drop for Zook<B> {
+    fn drop(&mut self) {
+        self.trigger.fire(&mut self.button);
+    }
+}
+
+// AND
+struct Bomb { usable: bool }
+impl Drop for Bomb { fn drop(&mut self) { self.usable = false; } }
+impl Bomb { fn activate(&self) { assert!(self.usable) } }
+
+enum B<'a> { HarmlessButton, BigRedButton(&'a Bomb) }
+impl<'a> Button for B<'a> {
+    fn push(&self) {
+        if let B::BigRedButton(borrowed) = *self {
+            borrowed.activate();
+        }
+    }
+}
+
+fn main() {
+    let (mut zook, ticking);
+    zook = Zook { button: B::HarmlessButton,
+                  trigger: Box::new(()) };
+    ticking = Bomb { usable: true };
+    zook.button = B::BigRedButton(&ticking);
+    //~^ ERROR `ticking` does not live long enough
+}
index c1ccfe269cdd3c49cd81977e114461e864102f12..060a66846d36f7afcaba4e4a38ac03f7d95c9fe3 100644 (file)
@@ -8,5 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(default_type_parameter_fallback)]
+
 fn avg<T=T::Item>(_: T) {} //~ ERROR associated type `Item` not found for `T`
 fn main() {}
index 73163caa455fc077bc5daf483f8b8dfc480ab054..0d372d300154af67727e1e7e9ec102df9896dec1 100644 (file)
 // Make sure that label for continue and break is spanned correctly
 
 fn main() {
-    continue
-    'b //~ ERROR use of undeclared label
-    ;
-    break
-    'c //~ ERROR use of undeclared label
-    ;
+    loop {
+        continue
+        'b //~ ERROR use of undeclared label
+        ;
+        break
+        'c //~ ERROR use of undeclared label
+        ;
+    }
 }
index 36b4a57eb117fc7641199f08f4484dcf137fb445..1dfff144cef98770b8661d782e4c2b231f8213c0 100644 (file)
 struct MyStruct;
 
 impl Drop for MyStruct {
-//~^ ERROR conflicting implementations for trait
+//~^ ERROR conflicting implementations of trait
     fn drop(&mut self) { }
 }
 
 impl Drop for MyStruct {
-//~^ NOTE conflicting implementation here
+//~^ NOTE conflicting implementation is here
     fn drop(&mut self) { }
 }
 
index 594f68e1812af7b2d6133f419fc8bf6335d1d9f2..ecc8ac34ecf2ba5f45cc5cf2c47805a385eed093 100644 (file)
@@ -13,13 +13,13 @@ trait siphash {
     fn reset(&self);
 }
 
-fn siphash(k0 : u64, k1 : u64) -> siphash {
+fn siphash(k0 : u64, k1 : u64) {
     struct SipState {
         v0: u64,
         v1: u64,
     }
 
-    fn mk_result(st : SipState) -> u64 {
+    fn mk_result(st : &SipState) -> u64 {
 
         let v0 = st.v0;
         let v1 = st.v1;
index 719eef1b63d5d8e191766dffa5e504ca7cc8ed49..7cf772b0728795334680a6b3ff01c68e27447623 100644 (file)
@@ -12,7 +12,7 @@ trait SipHash {
     fn reset(&self);
 }
 
-fn siphash(k0 : u64) -> SipHash {
+fn siphash(k0 : u64) {
     struct SipState {
         v0: u64,
     }
diff --git a/src/test/compile-fail/issue-30302.rs b/src/test/compile-fail/issue-30302.rs
new file mode 100644 (file)
index 0000000..26508a4
--- /dev/null
@@ -0,0 +1,26 @@
+// 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 Stack<T> {
+    Nil,
+    Cons(T, Box<Stack<T>>)
+}
+
+fn is_empty<T>(s: Stack<T>) -> bool {
+    match s {
+        Nil => true,
+//~^ WARN pattern binding `Nil` is named the same as one of the variants of the type `Stack`
+//~| HELP consider making the path in the pattern qualified: `Stack::Nil`
+        _ => false
+//~^ ERROR unreachable pattern
+    }
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/issue-30438-a.rs b/src/test/compile-fail/issue-30438-a.rs
new file mode 100644 (file)
index 0000000..441815d
--- /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.
+
+// Original regression test for Issue #30438.
+
+use std::ops::Index;
+
+struct Test<'a> {
+    s: &'a String
+}
+
+impl <'a> Index<usize> for Test<'a> {
+    type Output = Test<'a>;
+    fn index(&self, _: usize) -> &Self::Output {
+        return &Test { s: &self.s};
+        //~^ ERROR: borrowed value does not live long enough
+    }
+}
+
+fn main() {
+    let s = "Hello World".to_string();
+    let test = Test{s: &s};
+    let r = &test[0];
+    println!("{}", test.s); // OK since test is valid
+    println!("{}", r.s); // Segfault since value pointed by r has already been dropped
+}
diff --git a/src/test/compile-fail/issue-30438-b.rs b/src/test/compile-fail/issue-30438-b.rs
new file mode 100644 (file)
index 0000000..981b196
--- /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.
+
+// Modified regression test for Issue #30438 that exposed an
+// independent issue (see discussion on ticket).
+
+use std::ops::Index;
+
+struct Test<'a> {
+    s: &'a String
+}
+
+impl <'a> Index<usize> for Test<'a> {
+    type Output = Test<'a>;
+    fn index(&self, _: usize) -> &Self::Output {
+        &Test { s: &self.s}
+        //~^ ERROR: borrowed value does not live long enough
+    }
+}
+
+fn main() {
+    let s = "Hello World".to_string();
+    let test = Test{s: &s};
+    let r = &test[0];
+    println!("{}", test.s); // OK since test is valid
+    println!("{}", r.s); // Segfault since value pointed by r has already been dropped
+}
diff --git a/src/test/compile-fail/issue-30438-c.rs b/src/test/compile-fail/issue-30438-c.rs
new file mode 100644 (file)
index 0000000..06d391a
--- /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.
+
+// Simplfied regression test for #30438, inspired by arielb1.
+
+trait Trait { type Out; }
+
+struct Test<'a> { s: &'a str }
+
+fn silly<'y, 'z>(_s: &'y Test<'z>) -> &'y <Test<'z> as Trait>::Out where 'z: 'static {
+    let x = Test { s: "this cannot last" };
+    &x
+    //~^ ERROR: `x` does not live long enough
+}
+
+impl<'b> Trait for Test<'b> { type Out = Test<'b>; }
+
+fn main() {
+    let orig = Test { s: "Hello World" };
+    let r = silly(&orig);
+    println!("{}", orig.s); // OK since `orig` is valid
+    println!("{}", r.s); // Segfault (method does not return a sane value)
+}
diff --git a/src/test/compile-fail/issue-30589.rs b/src/test/compile-fail/issue-30589.rs
new file mode 100644 (file)
index 0000000..32765d5
--- /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.
+
+use std::fmt;
+
+impl fmt::Display for DecoderError { //~ ERROR E0412
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "Missing data: {}", self.0)
+    }
+}
+fn main() {
+}
diff --git a/src/test/compile-fail/issue-30715.rs b/src/test/compile-fail/issue-30715.rs
new file mode 100644 (file)
index 0000000..7ad4395
--- /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.
+
+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 no rules expected the token `+=`
+        }
+    }
+}
index be49ca1fe06a73742a8fa5f086b2629736695d2d..27b7fb752750347690c63ac7189b8245a7b54db7 100644 (file)
@@ -15,6 +15,8 @@ fn foo<T>() {
     }
 
     impl<T> Drop for foo<T> {
+        //~^ ERROR wrong number of type arguments
+        //~^^ ERROR the type parameter `T` is not constrained
         fn drop(&mut self) {}
     }
 }
index 5e7c23164cbbbbe65f35aa29e73703b35299d5a7..43ef1b59ccf275e09c9f19106791ee115de8cadf 100644 (file)
@@ -13,6 +13,4 @@ fn main() {
     //~^ ERROR mismatched types
     //~| expected `char`
     //~| found `u8`
-    //~| expected char
-    //~| found u8
 }
index f06aa45ac38fd1848c106847d37e1e5421e717af..34cd8cae2de32a64a3255c45b05354428b7c5910 100644 (file)
@@ -16,6 +16,7 @@ fn main() {
         Bar = foo
         //~^ ERROR attempt to use a non-constant value in a constant
         //~| ERROR unresolved name `foo`
+        //~^^^ ERROR constant evaluation error: non-constant path in constant expression
     }
 
     println!("{}", Stuff::Bar);
index ee8bc7d6e290103ccfd30543c9db1f45e59a389e..130647966f2d126760c07c634889ee17209b80cb 100644 (file)
@@ -18,6 +18,6 @@ struct S {
 }
 
 fn bar(_x: Foo) {}
-//~^ ERROR E0277
+//~^ ERROR E0038
 
 fn main() {}
index 2652fb5dfc2fd6dc453f73f1d3d9094847ccbe86..54eb2a908295568ce1d49356b954d4cd959f02b2 100644 (file)
@@ -30,5 +30,6 @@ impl ToString_ for Point {
 
 fn main() {
     let p = Point::new(0.0, 0.0);
-    println!("{}", p.to_string());
+    //~^ ERROR no associated item named `new` found for type `Point` in the current scope
+    println!("{}", p.to_string()); //~ ERROR type of this value must be known
 }
index 35096110e8f43a30fd1d76c0e1bc24b4ec15707b..304b6f185fe365342459b5a8a16e367995249b98 100644 (file)
@@ -71,6 +71,4 @@ fn main() {
     let x: char = true; //~  ERROR mismatched types
                         //~| expected `char`
                         //~| found `bool`
-                        //~| expected char
-                        //~| found bool
 }
index 0359248b36a490cdaac3563a8a1c8552bc25f73a..e5f091d873df9c1384207472e4f6061713bf5152 100644 (file)
@@ -9,12 +9,10 @@
 // except according to those terms.
 
 
-
-// error-pattern:unresolved enum variant
-
 fn main() {
     let z = match 3 {
-        x(1) => x(1)
+        x(1) => x(1) //~ ERROR unresolved enum variant
+        //~^ ERROR unresolved name `x`
     };
-    assert_eq!(z,3);
+    assert!(z == 3);
 }
index 30e4ec8ad0e6d104a25e499b2ad32544aece5e47..35be01970cb56d4acfef455f7ff060e05c899026 100644 (file)
@@ -13,14 +13,10 @@ enum Foo {
     //~^ ERROR mismatched types
     //~| expected `isize`
     //~| found `i64`
-    //~| expected isize
-    //~| found i64
     B = 2u8
     //~^ ERROR mismatched types
     //~| expected `isize`
     //~| found `u8`
-    //~| expected isize
-    //~| found u8
 }
 
 fn main() {}
index 1a3c926ba384ba9416c6c753d0117ac02c4fa2d2..f53122d19c1bd8bc25912ca75332e2a943bc7f2d 100644 (file)
@@ -13,4 +13,5 @@ struct A { foo: isize }
 fn main() {
     let A { foo, foo } = A { foo: 3 };
     //~^ ERROR: identifier `foo` is bound more than once in the same pattern
+    //~^^ ERROR: field `foo` bound multiple times
 }
index 26770a1d37c428b8e094952a03c5bc0792c1fdcd..f45e80f5252e31666841a9bb413364dee1962da1 100644 (file)
@@ -49,7 +49,7 @@ struct UsedStruct1 {
 }
 struct UsedStruct2(isize);
 struct UsedStruct3;
-struct UsedStruct4;
+pub struct UsedStruct4;
 // this struct is never used directly, but its method is, so we don't want
 // to warn it
 struct SemiUsedStruct;
index ce1406e80100ba104b3bde74cae3ba635964a176..e1ed21877c9f6e6e19a008a2099679ba71372973 100644 (file)
@@ -11,7 +11,7 @@
 #![deny(exceeding_bitshifts)]
 #![allow(unused_variables)]
 #![allow(dead_code)]
-#![feature(num_bits_bytes, const_indexing)]
+#![feature(const_indexing)]
 
 fn main() {
       let n = 1u8 << 7;
@@ -57,8 +57,13 @@ fn main() {
       let n = 1u8 << (4+3);
       let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits
 
-      let n = 1_isize << std::isize::BITS; //~ ERROR: bitshift exceeds the type's number of bits
-      let n = 1_usize << std::usize::BITS; //~ ERROR: bitshift exceeds the type's number of bits
+      #[cfg(target_pointer_width = "32")]
+      const BITS: usize = 32;
+      #[cfg(target_pointer_width = "64")]
+      const BITS: usize = 64;
+
+      let n = 1_isize << BITS; //~ ERROR: bitshift exceeds the type's number of bits
+      let n = 1_usize << BITS; //~ ERROR: bitshift exceeds the type's number of bits
 
 
       let n = 1i8<<(1isize+-1);
index 18a5a8ecc5d527d72fc7d2579bc45ba718f4002f..0ad3d2c5e73178a5141efcdef1fb28acf3c04869 100644 (file)
@@ -17,4 +17,5 @@ mod foo {
 fn main() {
     use foo::bar;
     foo::bar(); //~ ERROR: unnecessary qualification
+    bar();
 }
index f32d7db244bcf38f5942d61708cdd7aef1021383..414d2a857acc72b0f813ef977a9ffbe8166a1374 100644 (file)
@@ -227,8 +227,9 @@ mod cross_crate {
     struct S;
 
     impl UnstableTrait for S { } //~ ERROR use of unstable library feature
-
+    impl DeprecatedTrait for S {} //~ ERROR use of deprecated item: text
     trait LocalTrait : UnstableTrait { } //~ ERROR use of unstable library feature
+    trait LocalTrait2 : DeprecatedTrait { } //~ ERROR use of deprecated item: text
 
     impl Trait for S {
         fn trait_stable(&self) {}
index 839d50ae63f90359901ecb01dda7ad1489ecf6a4..0b414ad73db6f9109bd33fc5106102bae088e7e5 100644 (file)
@@ -8,9 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(negate_unsigned)]
 #![allow(dead_code)]
-#![feature(negate_unsigned)]
 
 // compile-flags: -D unused-comparisons
 fn main() { }
index 1468ae64d9418e52ffe5b92939f6e93b6b3927bd..35883293990a3ae19eee9101d45a66ca606916a2 100644 (file)
@@ -50,11 +50,14 @@ mod foo {
 mod bar {
     // Don't ignore on 'pub use' because we're not sure if it's used or not
     pub use std::cmp::PartialEq;
+    pub struct Square;
 
     pub mod c {
         use foo::Point;
         use foo::Square; //~ ERROR unused import
-        pub fn cc(p: Point) -> isize { return 2 * (p.x + p.y); }
+        pub fn cc(_p: Point) -> super::Square {
+            super::Square
+        }
     }
 
     #[allow(unused_imports)]
diff --git a/src/test/compile-fail/lint-visible-private-types.rs b/src/test/compile-fail/lint-visible-private-types.rs
deleted file mode 100644 (file)
index d347382..0000000
+++ /dev/null
@@ -1,123 +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.
-
-#![allow(dead_code)]
-#![crate_type="lib"]
-
-use std::marker;
-
-struct Private<T>(marker::PhantomData<T>);
-pub struct Public<T>(marker::PhantomData<T>);
-
-impl Private<Public<isize>> {
-    pub fn a(&self) -> Private<isize> { panic!() }
-    fn b(&self) -> Private<isize> { panic!() }
-
-    pub fn c() -> Private<isize> { panic!() }
-    fn d() -> Private<isize> { panic!() }
-}
-impl Private<isize> {
-    pub fn e(&self) -> Private<isize> { panic!() }
-    fn f(&self) -> Private<isize> { panic!() }
-}
-
-impl Public<Private<isize>> {
-    pub fn a(&self) -> Private<isize> { panic!() }
-    fn b(&self) -> Private<isize> { panic!() }
-
-    pub fn c() -> Private<isize> { panic!() } //~ ERROR private type in exported type signature
-    fn d() -> Private<isize> { panic!() }
-}
-impl Public<isize> {
-    pub fn e(&self) -> Private<isize> { panic!() } //~ ERROR private type in exported type signature
-    fn f(&self) -> Private<isize> { panic!() }
-}
-
-pub fn x(_: Private<isize>) {} //~ ERROR private type in exported type signature
-
-fn y(_: Private<isize>) {}
-
-
-pub struct Foo {
-    pub x: Private<isize>, //~ ERROR private type in exported type signature
-    y: Private<isize>
-}
-
-struct Bar {
-    x: Private<isize>,
-}
-
-pub enum Baz {
-    Baz1(Private<isize>), //~ ERROR private type in exported type signature
-    Baz2 {
-        y: Private<isize> //~ ERROR private type in exported type signature
-    },
-}
-
-enum Qux {
-    Qux1(Private<isize>),
-    Qux2 {
-        x: Private<isize>,
-    }
-}
-
-pub trait PubTrait {
-    fn foo(&self) -> Private<isize> { panic!( )} //~ ERROR private type in exported type signature
-    fn bar(&self) -> Private<isize>; //~ ERROR private type in exported type signature
-    fn baz() -> Private<isize>; //~ ERROR private type in exported type signature
-}
-
-impl PubTrait for Public<isize> {
-    fn bar(&self) -> Private<isize> { panic!() }
-    fn baz() -> Private<isize> { panic!() }
-}
-impl PubTrait for Public<Private<isize>> {
-    fn bar(&self) -> Private<isize> { panic!() }
-    fn baz() -> Private<isize> { panic!() }
-}
-
-impl PubTrait for Private<isize> {
-    fn bar(&self) -> Private<isize> { panic!() }
-    fn baz() -> Private<isize> { panic!() }
-}
-impl PubTrait for (Private<isize>,) {
-    fn bar(&self) -> Private<isize> { panic!() }
-    fn baz() -> Private<isize> { panic!() }
-}
-
-
-trait PrivTrait {
-    fn foo(&self) -> Private<isize> { panic!( )}
-    fn bar(&self) -> Private<isize>;
-}
-impl PrivTrait for Private<isize> {
-    fn bar(&self) -> Private<isize> { panic!() }
-}
-impl PrivTrait for (Private<isize>,) {
-    fn bar(&self) -> Private<isize> { panic!() }
-}
-
-pub trait ParamTrait<T> {
-    fn foo() -> T;
-}
-
-impl ParamTrait<Private<isize>> //~ ERROR private type in exported type signature
-   for Public<isize> {
-    fn foo() -> Private<isize> { panic!() }
-}
-
-impl ParamTrait<Private<isize>> for Private<isize> {
-    fn foo() -> Private<isize> { panic!( )}
-}
-
-impl<T: ParamTrait<Private<isize>>>  //~ ERROR private type in exported type signature
-     ParamTrait<T> for Public<i8> {
-    fn foo() -> T { panic!() }
-}
index af33dbb972ec12cef6e28a62f43f2e1592403a22..8fa5e0a70890ed2abf328ea6559dcdd5ec152fe2 100644 (file)
@@ -13,7 +13,6 @@
 // (typeof used because it's surprisingly hard to find an unparsed token after a stmt)
 macro_rules! m {
     () => ( i ; typeof );   //~ ERROR `typeof` is a reserved keyword
-                            //~| ERROR macro expansion ignores token `typeof`
                             //~| ERROR macro expansion ignores token `typeof`
                             //~| ERROR macro expansion ignores token `;`
                             //~| ERROR macro expansion ignores token `;`
@@ -29,5 +28,5 @@ fn main() {
         m!() => {}  //~ NOTE the usage of `m!` is likely invalid in pattern context
     }
 
-    m!();           //~ NOTE the usage of `m!` is likely invalid in statement context
+    m!();
 }
diff --git a/src/test/compile-fail/macro-follow.rs b/src/test/compile-fail/macro-follow.rs
new file mode 100644 (file)
index 0000000..35944ba
--- /dev/null
@@ -0,0 +1,122 @@
+// 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.
+//
+// Check the macro follow sets (see corresponding rpass test).
+
+// 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 ident) => {};    //~ERROR `$p:pat` is followed by `ident`
+    ($p:pat $p:pat) => {};   //~ERROR `$p:pat` is followed by `$p:pat`
+    ($p:pat $e:expr) => {};  //~ERROR `$p:pat` is followed by `$e:expr`
+    ($p:pat $t:ty) => {};    //~ERROR `$p:pat` is followed by `$t:ty`
+    ($p:pat $s:stmt) => {};  //~ERROR `$p:pat` is followed by `$s:stmt`
+    ($p:pat $p:path) => {};  //~ERROR `$p:pat` is followed by `$p:path`
+    ($p:pat $b:block) => {}; //~ERROR `$p:pat` is followed by `$b:block`
+    ($p:pat $i:ident) => {}; //~ERROR `$p:pat` is followed by `$i:ident`
+    ($p:pat $t:tt) => {};    //~ERROR `$p:pat` is followed by `$t:tt`
+    ($p:pat $i:item) => {};  //~ERROR `$p:pat` is followed by `$i:item`
+    ($p:pat $m:meta) => {};  //~ERROR `$p:pat` is followed by `$m:meta`
+}
+// 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 ident) => {};    //~ERROR `$e:expr` is followed by `ident`
+    ($e:expr if) => {};       //~ERROR `$e:expr` is followed by `if`
+    ($e:expr in) => {};       //~ERROR `$e:expr` is followed by `in`
+    ($e:expr $p:pat) => {};   //~ERROR `$e:expr` is followed by `$p:pat`
+    ($e:expr $e:expr) => {};  //~ERROR `$e:expr` is followed by `$e:expr`
+    ($e:expr $t:ty) => {};    //~ERROR `$e:expr` is followed by `$t:ty`
+    ($e:expr $s:stmt) => {};  //~ERROR `$e:expr` is followed by `$s:stmt`
+    ($e:expr $p:path) => {};  //~ERROR `$e:expr` is followed by `$p:path`
+    ($e:expr $b:block) => {}; //~ERROR `$e:expr` is followed by `$b:block`
+    ($e:expr $i:ident) => {}; //~ERROR `$e:expr` is followed by `$i:ident`
+    ($e:expr $t:tt) => {};    //~ERROR `$e:expr` is followed by `$t:tt`
+    ($e:expr $i:item) => {};  //~ERROR `$e:expr` is followed by `$i:item`
+    ($e:expr $m:meta) => {};  //~ERROR `$e:expr` is followed by `$m:meta`
+}
+// FOLLOW(ty) = {OpenDelim(Brace), Comma, FatArrow, Colon, Eq, Gt, Semi, Or,
+//               Ident(as), Ident(where), OpenDelim(Bracket)}
+macro_rules! follow_ty {
+    ($t:ty ()) => {};       //~WARN  `$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`
+    ($t:ty if) => {};       //~ERROR `$t:ty` is followed by `if`
+    ($t:ty $p:pat) => {};   //~ERROR `$t:ty` is followed by `$p:pat`
+    ($t:ty $e:expr) => {};  //~ERROR `$t:ty` is followed by `$e:expr`
+    ($t:ty $t:ty) => {};    //~ERROR `$t:ty` is followed by `$t:ty`
+    ($t:ty $s:stmt) => {};  //~ERROR `$t:ty` is followed by `$s:stmt`
+    ($t:ty $p:path) => {};  //~ERROR `$t:ty` is followed by `$p:path`
+    ($t:ty $b:block) => {}; //~ERROR `$t:ty` is followed by `$b:block`
+    ($t:ty $i:ident) => {}; //~ERROR `$t:ty` is followed by `$i:ident`
+    ($t:ty $t:tt) => {};    //~ERROR `$t:ty` is followed by `$t:tt`
+    ($t:ty $i:item) => {};  //~ERROR `$t:ty` is followed by `$i:item`
+    ($t:ty $m:meta) => {};  //~ERROR `$t:ty` is followed by `$m:meta`
+}
+// 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 ident) => {};    //~ERROR `$s:stmt` is followed by `ident`
+    ($s:stmt if) => {};       //~ERROR `$s:stmt` is followed by `if`
+    ($s:stmt in) => {};       //~ERROR `$s:stmt` is followed by `in`
+    ($s:stmt $p:pat) => {};   //~ERROR `$s:stmt` is followed by `$p:pat`
+    ($s:stmt $e:expr) => {};  //~ERROR `$s:stmt` is followed by `$e:expr`
+    ($s:stmt $t:ty) => {};    //~ERROR `$s:stmt` is followed by `$t:ty`
+    ($s:stmt $s:stmt) => {};  //~ERROR `$s:stmt` is followed by `$s:stmt`
+    ($s:stmt $p:path) => {};  //~ERROR `$s:stmt` is followed by `$p:path`
+    ($s:stmt $b:block) => {}; //~ERROR `$s:stmt` is followed by `$b:block`
+    ($s:stmt $i:ident) => {}; //~ERROR `$s:stmt` is followed by `$i:ident`
+    ($s:stmt $t:tt) => {};    //~ERROR `$s:stmt` is followed by `$t:tt`
+    ($s:stmt $i:item) => {};  //~ERROR `$s:stmt` is followed by `$i:item`
+    ($s:stmt $m:meta) => {};  //~ERROR `$s:stmt` is followed by `$m:meta`
+}
+// FOLLOW(path) = FOLLOW(ty)
+macro_rules! follow_path {
+    ($p:path ()) => {};       //~WARN  `$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`
+    ($p:path if) => {};       //~ERROR `$p:path` is followed by `if`
+    ($p:path $p:pat) => {};   //~ERROR `$p:path` is followed by `$p:pat`
+    ($p:path $e:expr) => {};  //~ERROR `$p:path` is followed by `$e:expr`
+    ($p:path $t:ty) => {};    //~ERROR `$p:path` is followed by `$t:ty`
+    ($p:path $s:stmt) => {};  //~ERROR `$p:path` is followed by `$s:stmt`
+    ($p:path $p:path) => {};  //~ERROR `$p:path` is followed by `$p:path`
+    ($p:path $b:block) => {}; //~ERROR `$p:path` is followed by `$b:block`
+    ($p:path $i:ident) => {}; //~ERROR `$p:path` is followed by `$i:ident`
+    ($p:path $t:tt) => {};    //~ERROR `$p:path` is followed by `$t:tt`
+    ($p:path $i:item) => {};  //~ERROR `$p:path` is followed by `$i:item`
+    ($p:path $m:meta) => {};  //~ERROR `$p:path` is followed by `$m:meta`
+}
+// FOLLOW(block) = any token
+// FOLLOW(ident) = any token
+
+fn main() {}
+
index 15f6d88fd8998da324e6d34fd415ec4d6bdedc3b..fe758a4a6310fbfe925eeb75d0a1e9c234d0209c 100644 (file)
@@ -18,13 +18,14 @@ macro_rules! errors_everywhere {
     ($bl:block < ) => ();
     ($pa:pat >) => (); //~ ERROR `$pa:pat` is followed by `>`, which is not allowed for `pat`
     ($pa:pat , ) => ();
-    ($pa:pat | ) => (); //~ ERROR `$pa:pat` is followed by `|`
     ($pa:pat $pb:pat $ty:ty ,) => ();
     //~^ ERROR `$pa:pat` is followed by `$pb:pat`, which is not allowed
     //~^^ ERROR `$pb:pat` is followed by `$ty:ty`, which is not allowed
     ($($ty:ty)* -) => (); //~ ERROR `$ty:ty` is followed by `-`
     ($($a:ty, $b:ty)* -) => (); //~ ERROR `$b:ty` is followed by `-`
     ($($ty:ty)-+) => (); //~ ERROR `$ty:ty` is followed by `-`, which is not allowed for `ty`
+    ( $($a:expr)* $($b:tt)* ) => { };
+    //~^ ERROR `$a:expr` is followed by `$b:tt`, which is not allowed for `expr` fragments
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/macro-parameter-span.rs b/src/test/compile-fail/macro-parameter-span.rs
new file mode 100644 (file)
index 0000000..2ef6975
--- /dev/null
@@ -0,0 +1,23 @@
+// 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! foo {
+    ($id: ident) => {
+        $id
+    }
+}
+
+// Testing that the error span points to the parameter 'x' in the callsite,
+// not to the macro variable '$id'
+fn main() {
+    foo!(
+        x //~ ERROR unresolved name `x`
+        );
+}
diff --git a/src/test/compile-fail/macro-seq-followed-by-seq.rs b/src/test/compile-fail/macro-seq-followed-by-seq.rs
deleted file mode 100644 (file)
index b4f7134..0000000
+++ /dev/null
@@ -1,18 +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.
-
-// Check that we cannot have two sequence repetitions in a row.
-
-macro_rules! foo {
-  ( $($a:expr)* $($b:tt)* ) => { }; //~ ERROR sequence repetition followed by another sequence
-  ( $($a:tt)* $($b:tt)* ) => { }; //~ ERROR sequence repetition followed by another sequence
-}
-
-fn main() { }
index 3b794da10536e078c8dd6d8b204196205a0500ee..f8ea5dda183366df38ecfa9544f08d21d4406a97 100644 (file)
@@ -13,5 +13,5 @@
 mod mod_file_aux;
 
 fn main() {
-    assert_eq!(mod_file_aux::bar(), 10); //~ ERROR unresolved name
+    assert!(mod_file_aux::bar() == 10); //~ ERROR unresolved name
 }
diff --git a/src/test/compile-fail/not-panic-safe-2.rs b/src/test/compile-fail/not-panic-safe-2.rs
new file mode 100644 (file)
index 0000000..47a6550
--- /dev/null
@@ -0,0 +1,24 @@
+// 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.
+
+#![allow(dead_code)]
+#![feature(recover)]
+
+use std::panic::RecoverSafe;
+use std::rc::Rc;
+use std::cell::RefCell;
+
+fn assert<T: RecoverSafe + ?Sized>() {}
+
+fn main() {
+    assert::<Rc<RefCell<i32>>>(); //~ ERROR: is not implemented
+    //~^ ERROR: is not implemented
+}
+
diff --git a/src/test/compile-fail/not-panic-safe-3.rs b/src/test/compile-fail/not-panic-safe-3.rs
new file mode 100644 (file)
index 0000000..a0c7865
--- /dev/null
@@ -0,0 +1,23 @@
+// 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.
+
+#![allow(dead_code)]
+#![feature(recover)]
+
+use std::panic::RecoverSafe;
+use std::sync::Arc;
+use std::cell::RefCell;
+
+fn assert<T: RecoverSafe + ?Sized>() {}
+
+fn main() {
+    assert::<Arc<RefCell<i32>>>(); //~ ERROR: is not implemented
+    //~^ ERROR: is not implemented
+}
diff --git a/src/test/compile-fail/not-panic-safe-4.rs b/src/test/compile-fail/not-panic-safe-4.rs
new file mode 100644 (file)
index 0000000..9e71613
--- /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.
+
+#![allow(dead_code)]
+#![feature(recover)]
+
+use std::panic::RecoverSafe;
+use std::cell::RefCell;
+
+fn assert<T: RecoverSafe + ?Sized>() {}
+
+fn main() {
+    assert::<&RefCell<i32>>(); //~ ERROR: is not implemented
+    //~^ ERROR is not implemented
+}
diff --git a/src/test/compile-fail/not-panic-safe-5.rs b/src/test/compile-fail/not-panic-safe-5.rs
new file mode 100644 (file)
index 0000000..1fa76c2
--- /dev/null
@@ -0,0 +1,21 @@
+// 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.
+
+#![allow(dead_code)]
+#![feature(recover)]
+
+use std::panic::RecoverSafe;
+use std::cell::UnsafeCell;
+
+fn assert<T: RecoverSafe + ?Sized>() {}
+
+fn main() {
+    assert::<*const UnsafeCell<i32>>(); //~ ERROR: is not implemented
+}
diff --git a/src/test/compile-fail/not-panic-safe-6.rs b/src/test/compile-fail/not-panic-safe-6.rs
new file mode 100644 (file)
index 0000000..90c730d
--- /dev/null
@@ -0,0 +1,23 @@
+// 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.
+
+#![allow(dead_code)]
+#![feature(recover)]
+
+use std::panic::RecoverSafe;
+use std::cell::RefCell;
+
+fn assert<T: RecoverSafe + ?Sized>() {}
+
+fn main() {
+    assert::<*mut RefCell<i32>>(); //~ ERROR: is not implemented
+    //~^ ERROR is not implemented
+}
+
diff --git a/src/test/compile-fail/not-panic-safe.rs b/src/test/compile-fail/not-panic-safe.rs
new file mode 100644 (file)
index 0000000..f06464c
--- /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.
+
+#![allow(dead_code)]
+#![feature(recover)]
+
+use std::panic::RecoverSafe;
+
+fn assert<T: RecoverSafe + ?Sized>() {}
+
+fn main() {
+    assert::<&mut i32>(); //~ ERROR: RecoverSafe` is not implemented
+}
index 63e5718537cf9166881efafaa66be30ff683b1a0..341736f7ab5ee8c938539b8f48dc2282ac46861b 100644 (file)
@@ -22,16 +22,13 @@ trait Quux {
 }
 
 fn make_bar<T:Bar>(t: &T) -> &Bar {
-    t
         //~^ ERROR E0038
         //~| NOTE method `bar` has generic type parameters
+    t
 }
 
 fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
     t as &Bar
-        //~^ ERROR E0038
-        //~| NOTE method `bar` has generic type parameters
-        //~| ERROR E0038
 }
 
 fn make_quux<T:Quux>(t: &T) -> &Quux {
index 55b780906355a8f8b1a21f12679ab1f132938cb5..edd31c1f79649fe98c89f8bc6b5e80f61c0a69eb 100644 (file)
@@ -25,29 +25,15 @@ trait Quux {
 }
 
 fn make_bar<T:Bar>(t: &T) -> &Bar {
-    t
-        //~^ ERROR E0038
-        //~| NOTE method `bar` references the `Self` type in its arguments or return type
-}
-
-fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
-    t as &Bar
         //~^ ERROR E0038
         //~| NOTE method `bar` references the `Self` type in its arguments or return type
-        //~| ERROR E0038
+    loop { }
 }
 
 fn make_baz<T:Baz>(t: &T) -> &Baz {
-    t
         //~^ ERROR E0038
         //~| NOTE method `bar` references the `Self` type in its arguments or return type
-}
-
-fn make_baz_explicit<T:Baz>(t: &T) -> &Baz {
-    t as &Baz
-        //~^ ERROR E0038
-        //~| NOTE method `bar` references the `Self` type in its arguments or return type
-        //~| ERROR E0038
+    t
 }
 
 fn make_quux<T:Quux>(t: &T) -> &Quux {
index 2dc7983d1b561d3f522177862a62ed295ef757d4..dd1d5af3f4a128a6db005b6fab2d17d2724b0bda 100644 (file)
@@ -16,16 +16,8 @@ trait Foo {
 }
 
 fn foo_implicit<T:Foo+'static>(b: Box<T>) -> Box<Foo+'static> {
-    b
-        //~^ ERROR E0038
-        //~| NOTE method `foo` has no receiver
-}
-
-fn foo_explicit<T:Foo+'static>(b: Box<T>) -> Box<Foo+'static> {
-    b as Box<Foo>
-        //~^ ERROR E0038
-        //~| NOTE method `foo` has no receiver
-        //~| ERROR E0038
+    //~^ ERROR E0038
+    loop { }
 }
 
 fn main() {
index 401602bd681a38b5a536e98fd533c8e8c2492c5f..3e1942d5a01884467d5ae54fed17430553dd0351 100644 (file)
@@ -18,16 +18,8 @@ trait Bar
 }
 
 fn make_bar<T:Bar>(t: &T) -> &Bar {
-    t
         //~^ ERROR E0038
-        //~| NOTE the trait cannot require that `Self : Sized`
-}
-
-fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
-    t as &Bar
-        //~^ ERROR E0038
-        //~| NOTE the trait cannot require that `Self : Sized`
-        //~| ERROR E0038
+    loop { }
 }
 
 fn main() {
index 29b4e4db65c36d11a70a7b04bbceb1e1dc988c11..501d61d20fed124453edb3b2bad2050c71f7d273 100644 (file)
@@ -16,16 +16,9 @@ trait Bar : Sized {
 }
 
 fn make_bar<T:Bar>(t: &T) -> &Bar {
-    t
-        //~^ ERROR E0038
-        //~| NOTE the trait cannot require that `Self : Sized`
-}
-
-fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
-    t as &Bar
         //~^ ERROR E0038
         //~| NOTE the trait cannot require that `Self : Sized`
-        //~| ERROR E0038
+    t
 }
 
 fn main() {
index be321b6290354197c03dfcd2c5b0447e271b0cc2..bc18b52a0c1c9e3f8fdfd0f65994e4e2e0daf464 100644 (file)
@@ -16,7 +16,6 @@ struct IWantToCopyThis {
 
 impl Copy for IWantToCopyThis {}
 //~^ ERROR the trait `Copy` may not be implemented for this type
-//~| ERROR E0277
 
 enum CantCopyThisEither {
     A,
@@ -29,6 +28,5 @@ enum IWantToCopyThisToo {
 
 impl Copy for IWantToCopyThisToo {}
 //~^ ERROR the trait `Copy` may not be implemented for this type
-//~| ERROR E0277
 
 fn main() {}
index aa7202574abfc3d15c0230790723e81f0d97b56c..9ebdcf1a9ecb02551164fbaa3a1971917fbae899 100644 (file)
@@ -42,6 +42,4 @@ fn main() {
     //~^ ERROR mismatched types
     //~| expected `char`
     //~| found `bool`
-    //~| expected char
-    //~| found bool
 }
index ccf82e90b7a1d89fc9b18e4bafb32fa03fb418d1..1a1c87ff47d467371b0390d974bd3ad80289228a 100644 (file)
@@ -15,13 +15,9 @@ fn main() {
     //~^ ERROR mismatched types
     //~| expected `u32`
     //~| found `i32`
-    //~| expected u32
-    //~| found i32
 
     let_in(3i32, |i| { assert!(i == 3u32); });
     //~^ ERROR mismatched types
     //~| expected `i32`
     //~| found `u32`
-    //~| expected i32
-    //~| found u32
 }
diff --git a/src/test/compile-fail/priv_in_pub_sig_priv_mod.rs b/src/test/compile-fail/priv_in_pub_sig_priv_mod.rs
deleted file mode 100644 (file)
index f589daf..0000000
+++ /dev/null
@@ -1,28 +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.
-
-// Test that we properly check for private types in public signatures, even
-// inside a private module (#22261).
-
-mod a {
-    struct Priv;
-
-    pub fn expose_a() -> Priv { //~Error: private type in exported type signature
-        panic!();
-    }
-
-    mod b {
-        pub fn expose_b() -> super::Priv { //~Error: private type in exported type signature
-            panic!();
-        }
-    }
-}
-
-pub fn main() {}
diff --git a/src/test/compile-fail/private-in-public-lint.rs b/src/test/compile-fail/private-in-public-lint.rs
new file mode 100644 (file)
index 0000000..f9b049c
--- /dev/null
@@ -0,0 +1,33 @@
+// 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.
+
+mod m1 {
+    #![deny(private_in_public)]
+
+    pub struct Pub;
+    struct Priv;
+
+    impl Pub {
+        pub fn f() -> Priv {} //~ ERROR private type in public interface
+    }
+}
+
+mod m2 {
+    #![deny(future_incompatible)]
+
+    pub struct Pub;
+    struct Priv;
+
+    impl Pub {
+        pub fn f() -> Priv {} //~ ERROR private type in public interface
+    }
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/private-in-public-warn.rs b/src/test/compile-fail/private-in-public-warn.rs
new file mode 100644 (file)
index 0000000..9aab06c
--- /dev/null
@@ -0,0 +1,287 @@
+// 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.
+
+// Private types and traits are not allowed in public interfaces.
+// This test also ensures that the checks are performed even inside private modules.
+
+#![feature(rustc_attrs)]
+#![feature(associated_consts)]
+#![feature(associated_type_defaults)]
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(improper_ctypes)]
+
+mod types {
+    struct Priv;
+    pub struct Pub;
+    pub trait PubTr {
+        type Alias;
+    }
+
+    pub type Alias = Priv; //~ WARN private type in public interface
+    //~^ WARNING hard error
+    pub enum E {
+        V1(Priv), //~ WARN private type in public interface
+        //~^ WARNING hard error
+        V2 { field: Priv }, //~ WARN private type in public interface
+        //~^ WARNING hard error
+    }
+    pub trait Tr {
+        const C: Priv = Priv; //~ WARN private type in public interface
+        //~^ WARNING hard error
+        type Alias = Priv; //~ WARN private type in public interface
+        //~^ WARNING hard error
+        fn f1(arg: Priv) {} //~ WARN private type in public interface
+        //~^ WARNING hard error
+        fn f2() -> Priv { panic!() } //~ WARN private type in public interface
+        //~^ WARNING hard error
+    }
+    extern {
+        pub static ES: Priv; //~ WARN private type in public interface
+        //~^ WARNING hard error
+        pub fn ef1(arg: Priv); //~ WARN private type in public interface
+        //~^ WARNING hard error
+        pub fn ef2() -> Priv; //~ WARN private type in public interface
+        //~^ WARNING hard error
+    }
+    impl PubTr for Pub {
+        type Alias = Priv; //~ WARN private type in public interface
+        //~^ WARNING hard error
+    }
+}
+
+mod traits {
+    trait PrivTr {}
+    pub struct Pub<T>(T);
+    pub trait PubTr {}
+
+    pub type Alias<T: PrivTr> = T; //~ WARN private trait in public interface
+    //~^ WARN trait bounds are not (yet) enforced in type definitions
+    //~| WARNING hard error
+    pub trait Tr1: PrivTr {} //~ WARN private trait in public interface
+    //~^ WARNING hard error
+    pub trait Tr2<T: PrivTr> {} //~ WARN private trait in public interface
+        //~^ WARNING hard error
+    pub trait Tr3 {
+        type Alias: PrivTr; //~ WARN private trait in public interface
+        //~^ WARNING hard error
+        fn f<T: PrivTr>(arg: T) {} //~ WARN private trait in public interface
+        //~^ WARNING hard error
+    }
+    impl<T: PrivTr> Pub<T> {} //~ WARN private trait in public interface
+        //~^ WARNING hard error
+    impl<T: PrivTr> PubTr for Pub<T> {} //~ WARN private trait in public interface
+        //~^ WARNING hard error
+}
+
+mod traits_where {
+    trait PrivTr {}
+    pub struct Pub<T>(T);
+    pub trait PubTr {}
+
+    pub type Alias<T> where T: PrivTr = T; //~ WARN private trait in public interface
+        //~^ WARNING hard error
+    pub trait Tr2<T> where T: PrivTr {} //~ WARN private trait in public interface
+        //~^ WARNING hard error
+    pub trait Tr3 {
+        fn f<T>(arg: T) where T: PrivTr {} //~ WARN private trait in public interface
+        //~^ WARNING hard error
+    }
+    impl<T> Pub<T> where T: PrivTr {} //~ WARN private trait in public interface
+        //~^ WARNING hard error
+    impl<T> PubTr for Pub<T> where T: PrivTr {} //~ WARN private trait in public interface
+        //~^ WARNING hard error
+}
+
+mod generics {
+    struct Priv<T = u8>(T);
+    pub struct Pub<T = u8>(T);
+    trait PrivTr<T> {}
+    pub trait PubTr<T> {}
+
+    pub trait Tr1: PrivTr<Pub> {} //~ WARN private trait in public interface
+        //~^ WARNING hard error
+    pub trait Tr2: PubTr<Priv> {} //~ WARN private type in public interface
+        //~^ WARNING hard error
+    pub trait Tr3: PubTr<[Priv; 1]> {} //~ WARN private type in public interface
+        //~^ WARNING hard error
+    pub trait Tr4: PubTr<Pub<Priv>> {} //~ WARN private type in public interface
+        //~^ WARNING hard error
+}
+
+mod impls {
+    struct Priv;
+    pub struct Pub;
+    trait PrivTr {
+        type Alias;
+    }
+    pub trait PubTr {
+        type Alias;
+    }
+
+    impl Priv {
+        pub fn f(arg: Priv) {} // OK
+    }
+    impl PrivTr for Priv {
+        type Alias = Priv; // OK
+    }
+    impl PubTr for Priv {
+        type Alias = Priv; // OK
+    }
+    impl PrivTr for Pub {
+        type Alias = Priv; // OK
+    }
+    impl PubTr for Pub {
+        type Alias = Priv; //~ WARN private type in public interface
+        //~^ WARNING hard error
+    }
+}
+
+mod impls_generics {
+    struct Priv<T = u8>(T);
+    pub struct Pub<T = u8>(T);
+    trait PrivTr<T = u8> {
+        type Alias;
+    }
+    pub trait PubTr<T = u8> {
+        type Alias;
+    }
+
+    impl Priv<Pub> {
+        pub fn f(arg: Priv) {} // OK
+    }
+    impl Pub<Priv> {
+        pub fn f(arg: Priv) {} // OK
+    }
+    impl PrivTr<Pub> for Priv {
+        type Alias = Priv; // OK
+    }
+    impl PubTr<Priv> for Priv {
+        type Alias = Priv; // OK
+    }
+    impl PubTr for Priv<Pub> {
+        type Alias = Priv; // OK
+    }
+    impl PubTr for [Priv; 1] {
+        type Alias = Priv; // OK
+    }
+    impl PubTr for Pub<Priv> {
+        type Alias = Priv; // OK
+    }
+    impl PrivTr<Pub> for Pub {
+        type Alias = Priv; // OK
+    }
+    impl PubTr<Priv> for Pub {
+        type Alias = Priv; // OK
+    }
+}
+
+mod aliases_pub {
+    struct Priv;
+    mod m {
+        pub struct Pub1;
+        pub struct Pub2;
+        pub struct Pub3;
+        pub trait PubTr<T = u8> {
+            type Check = u8;
+        }
+    }
+
+    use self::m::Pub1 as PrivUseAlias;
+    use self::m::PubTr as PrivUseAliasTr;
+    type PrivAlias = m::Pub2;
+    trait PrivTr {
+        type AssocAlias = m::Pub3;
+    }
+    impl PrivTr for Priv {}
+
+    pub fn f1(arg: PrivUseAlias) {} // OK
+
+    pub trait Tr1: PrivUseAliasTr {} // OK
+    // This should be OK, if type aliases are substituted
+    pub trait Tr2: PrivUseAliasTr<PrivAlias> {} //~ WARN private type in public interface
+        //~^ WARNING hard error
+
+    impl PrivAlias {
+        pub fn f(arg: Priv) {} //~ WARN private type in public interface
+        //~^ WARNING hard error
+    }
+    // This doesn't even parse
+    // impl <Priv as PrivTr>::AssocAlias {
+    //     pub fn f(arg: Priv) {} // WARN private type in public interface
+    // }
+    impl PrivUseAliasTr for PrivUseAlias {
+        type Check = Priv; //~ WARN private type in public interface
+        //~^ WARNING hard error
+    }
+    impl PrivUseAliasTr for PrivAlias {
+        type Check = Priv; //~ WARN private type in public interface
+        //~^ WARNING hard error
+    }
+    impl PrivUseAliasTr for <Priv as PrivTr>::AssocAlias {
+        type Check = Priv; //~ WARN private type in public interface
+        //~^ WARNING hard error
+    }
+}
+
+mod aliases_priv {
+    struct Priv;
+
+    struct Priv1;
+    struct Priv2;
+    struct Priv3;
+    trait PrivTr1<T = u8> {
+        type Check = u8;
+    }
+
+    use self::Priv1 as PrivUseAlias;
+    use self::PrivTr1 as PrivUseAliasTr;
+    type PrivAlias = Priv2;
+    trait PrivTr {
+        type AssocAlias = Priv3;
+    }
+    impl PrivTr for Priv {}
+
+    pub trait Tr1: PrivUseAliasTr {} //~ WARN private trait in public interface
+        //~^ WARNING hard error
+    pub trait Tr2: PrivUseAliasTr<PrivAlias> {} //~ WARN private trait in public interface
+     //~^ WARN private type in public interface
+        //~| WARNING hard error
+        //~| WARNING hard error
+
+    impl PrivUseAlias {
+        pub fn f(arg: Priv) {} // OK
+    }
+    impl PrivAlias {
+        pub fn f(arg: Priv) {} // OK
+    }
+    // This doesn't even parse
+    // impl <Priv as PrivTr>::AssocAlias {
+    //     pub fn f(arg: Priv) {} // OK
+    // }
+    impl PrivUseAliasTr for PrivUseAlias {
+        type Check = Priv; // OK
+    }
+    impl PrivUseAliasTr for PrivAlias {
+        type Check = Priv; // OK
+    }
+    impl PrivUseAliasTr for <Priv as PrivTr>::AssocAlias {
+        type Check = Priv; // OK
+    }
+}
+
+mod aliases_params {
+    struct Priv;
+    type PrivAliasGeneric<T = Priv> = T;
+    type Result<T> = ::std::result::Result<T, Priv>;
+}
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
diff --git a/src/test/compile-fail/private-in-public.rs b/src/test/compile-fail/private-in-public.rs
new file mode 100644 (file)
index 0000000..be22a2e
--- /dev/null
@@ -0,0 +1,152 @@
+// 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.
+
+// Private types and traits are not allowed in public interfaces.
+// This test also ensures that the checks are performed even inside private modules.
+
+#![feature(associated_consts)]
+#![feature(associated_type_defaults)]
+
+mod types {
+    struct Priv;
+    pub struct Pub;
+    pub trait PubTr {
+        type Alias;
+    }
+
+    pub const C: Priv = Priv; //~ ERROR private type in public interface
+    pub static S: Priv = Priv; //~ ERROR private type in public interface
+    pub fn f1(arg: Priv) {} //~ ERROR private type in public interface
+    pub fn f2() -> Priv { panic!() } //~ ERROR private type in public interface
+    pub struct S1(pub Priv); //~ ERROR private type in public interface
+    pub struct S2 { pub field: Priv } //~ ERROR private type in public interface
+    impl Pub {
+        pub const C: Priv = Priv; //~ ERROR private type in public interface
+        pub fn f1(arg: Priv) {} //~ ERROR private type in public interface
+        pub fn f2() -> Priv { panic!() } //~ ERROR private type in public interface
+    }
+}
+
+mod traits {
+    trait PrivTr {}
+    pub struct Pub<T>(T);
+    pub trait PubTr {}
+
+    pub enum E<T: PrivTr> { V(T) } //~ ERROR private trait in public interface
+    pub fn f<T: PrivTr>(arg: T) {} //~ ERROR private trait in public interface
+    pub struct S1<T: PrivTr>(T); //~ ERROR private trait in public interface
+    impl<T: PrivTr> Pub<T> {
+        pub fn f<U: PrivTr>(arg: U) {} //~ ERROR private trait in public interface
+    }
+}
+
+mod traits_where {
+    trait PrivTr {}
+    pub struct Pub<T>(T);
+    pub trait PubTr {}
+
+    pub enum E<T> where T: PrivTr { V(T) } //~ ERROR private trait in public interface
+    pub fn f<T>(arg: T) where T: PrivTr {} //~ ERROR private trait in public interface
+    pub struct S1<T>(T) where T: PrivTr; //~ ERROR private trait in public interface
+    impl<T> Pub<T> where T: PrivTr {
+        pub fn f<U>(arg: U) where U: PrivTr {} //~ ERROR private trait in public interface
+    }
+}
+
+mod generics {
+    struct Priv<T = u8>(T);
+    pub struct Pub<T = u8>(T);
+    trait PrivTr<T> {}
+    pub trait PubTr<T> {}
+
+    pub fn f1(arg: [Priv; 1]) {} //~ ERROR private type in public interface
+    pub fn f2(arg: Pub<Priv>) {} //~ ERROR private type in public interface
+    pub fn f3(arg: Priv<Pub>) {} //~ ERROR private type in public interface
+}
+
+mod impls {
+    struct Priv;
+    pub struct Pub;
+    trait PrivTr {
+        type Alias;
+    }
+    pub trait PubTr {
+        type Alias;
+    }
+
+    impl Pub {
+        pub fn f(arg: Priv) {} //~ ERROR private type in public interface
+    }
+}
+
+mod aliases_pub {
+    struct Priv;
+    mod m {
+        pub struct Pub1;
+        pub struct Pub2;
+        pub struct Pub3;
+        pub trait PubTr<T = u8> {
+            type Check = u8;
+        }
+    }
+
+    use self::m::Pub1 as PrivUseAlias;
+    use self::m::PubTr as PrivUseAliasTr;
+    type PrivAlias = m::Pub2;
+    trait PrivTr {
+        type AssocAlias = m::Pub3;
+    }
+    impl PrivTr for Priv {}
+
+    // This should be OK, if type aliases are substituted
+    pub fn f2(arg: PrivAlias) {} //~ ERROR private type in public interface
+    // This should be OK, but associated type aliases are not substituted yet
+    pub fn f3(arg: <Priv as PrivTr>::AssocAlias) {} //~ ERROR private type in public interface
+
+    impl PrivUseAlias {
+        pub fn f(arg: Priv) {} //~ ERROR private type in public interface
+    }
+}
+
+mod aliases_priv {
+    struct Priv;
+
+    struct Priv1;
+    struct Priv2;
+    struct Priv3;
+    trait PrivTr1<T = u8> {
+        type Check = u8;
+    }
+
+    use self::Priv1 as PrivUseAlias;
+    use self::PrivTr1 as PrivUseAliasTr;
+    type PrivAlias = Priv2;
+    trait PrivTr {
+        type AssocAlias = Priv3;
+    }
+    impl PrivTr for Priv {}
+
+    pub fn f1(arg: PrivUseAlias) {} //~ ERROR private type in public interface
+    pub fn f2(arg: PrivAlias) {} //~ ERROR private type in public interface
+    pub fn f3(arg: <Priv as PrivTr>::AssocAlias) {} //~ ERROR private type in public interface
+}
+
+mod aliases_params {
+    struct Priv;
+    type PrivAliasGeneric<T = Priv> = T;
+    type Result<T> = ::std::result::Result<T, Priv>;
+
+    // This should be OK, if type aliases are substituted
+    pub fn f1(arg: PrivAliasGeneric<u8>) {} //~ ERROR private type in public interface
+    pub fn f2(arg: PrivAliasGeneric) {} //~ ERROR private type in public interface
+    pub fn f3(arg: Result<u8>) {} //~ ERROR private type in public interface
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/private-variant-reexport.rs b/src/test/compile-fail/private-variant-reexport.rs
new file mode 100644 (file)
index 0000000..06f08dc
--- /dev/null
@@ -0,0 +1,37 @@
+// 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.
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+mod m1 {
+    pub use ::E::V; //~ WARN variant `V` is private, and cannot be reexported
+    //~^ WARNING hard error
+}
+
+mod m2 {
+    pub use ::E::{V}; //~ WARN variant `V` is private, and cannot be reexported
+    //~^ WARNING hard error
+}
+
+mod m3 {
+    pub use ::E::V::{self}; //~ WARN variant `V` is private, and cannot be reexported
+    //~^ WARNING hard error
+}
+
+mod m4 {
+    pub use ::E::*; //~ WARN variant `V` is private, and cannot be reexported
+    //~^ WARNING hard error
+}
+
+enum E { V }
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
diff --git a/src/test/compile-fail/pub-item-macro.rs b/src/test/compile-fail/pub-item-macro.rs
deleted file mode 100644 (file)
index 8809e9a..0000000
+++ /dev/null
@@ -1,28 +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.
-
-// Issue #14660
-
-macro_rules! priv_x { () => {
-    static x: u32 = 0;
-}}
-
-macro_rules! pub_x { () => {
-    pub priv_x!(); //~ ERROR can't qualify macro invocation with `pub`
-    //~^ HELP try adjusting the macro to put `pub` inside the invocation
-}}
-
-mod foo {
-    pub_x!();
-}
-
-fn main() {
-    let y: u32 = foo::x;
-}
diff --git a/src/test/compile-fail/pub-method-macro.rs b/src/test/compile-fail/pub-method-macro.rs
deleted file mode 100644 (file)
index 198fa5b..0000000
+++ /dev/null
@@ -1,34 +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.
-
-// Issue #18317
-
-mod bleh {
-    macro_rules! defn {
-        ($n:ident) => (
-            fn $n (&self) -> i32 {
-                println!("{}", stringify!($n));
-                1
-            }
-        )
-    }
-
-    #[derive(Copy, Clone)]
-    pub struct S;
-
-    impl S {
-        pub defn!(f); //~ ERROR can't qualify macro invocation with `pub`
-        //~^ HELP try adjusting the macro to put `pub` inside the invocation
-    }
-}
-
-fn main() {
-    bleh::S.f();
-}
diff --git a/src/test/compile-fail/region-borrow-params-issue-29793-big.rs b/src/test/compile-fail/region-borrow-params-issue-29793-big.rs
new file mode 100644 (file)
index 0000000..887f783
--- /dev/null
@@ -0,0 +1,84 @@
+// 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 #29793, big regression test: do not let borrows of
+// parameters to ever be returned (expanded with exploration of
+// variations).
+//
+// This is the version of the test that actually exposed unsound
+// behavior (because the improperly accepted closure was actually
+// able to be invoked).
+
+struct WrapA<F>(Option<F>);
+
+impl<F> WrapA<F> {
+    fn new() -> WrapA<F> {
+        WrapA(None)
+    }
+    fn set(mut self, f: F) -> Self {
+        self.0 = Some(f);
+        self
+    }
+}
+
+struct WrapB<F>(Option<F>);
+
+impl<F> WrapB<F> {
+    fn new() -> WrapB<F> {
+        WrapB(None)
+    }
+    fn set(mut self, f: F) -> Self {
+        self.0 = Some(f);
+        self
+    }
+}
+
+trait DoStuff : Sized {
+    fn handle(self);
+}
+
+impl<F, T> DoStuff for WrapA<F>
+    where F: FnMut(usize, usize) -> T, T: DoStuff {
+        fn handle(mut self) {
+            if let Some(ref mut f) = self.0 {
+                let x = f(1, 2);
+                let _foo = [0usize; 16];
+                x.handle();
+            }
+        }
+    }
+
+impl<F> DoStuff for WrapB<F> where F: FnMut(bool) -> usize {
+    fn handle(mut self) {
+        if let Some(ref mut f) = self.0 {
+            println!("{}", f(true));
+        }
+    }
+}
+
+impl<F, T> WrapA<F>
+    where F: FnMut(usize, usize) -> T, T: DoStuff {
+        fn handle_ref(&mut self) {
+            if let Some(ref mut f) = self.0 {
+                let x = f(1, 2);
+            }
+        }
+    }
+
+fn main() {
+    let mut w = WrapA::new().set(|x: usize, y: usize| {
+        WrapB::new().set(|t: bool| if t { x } else { y }) // (separate errors for `x` vs `y`)
+            //~^ ERROR `x` does not live long enough
+            //~| ERROR `y` does not live long enough
+    });
+
+    w.handle(); // This works
+    // w.handle_ref(); // This doesn't
+}
diff --git a/src/test/compile-fail/region-borrow-params-issue-29793-small.rs b/src/test/compile-fail/region-borrow-params-issue-29793-small.rs
new file mode 100644 (file)
index 0000000..4fda8ec
--- /dev/null
@@ -0,0 +1,223 @@
+// 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 #29793, small regression tests: do not let borrows of
+// parameters to ever be returned (expanded with exploration of
+// variations).
+
+// CLOSURES
+
+fn escaping_borrow_of_closure_params_1() {
+    let g = |x: usize, y:usize| {
+        let f = |t: bool| if t { x } else { y }; // (separate errors for `x` vs `y`)
+        //~^ ERROR `x` does not live long enough
+        //~| ERROR `y` does not live long enough
+        return f;
+    };
+
+    // We delberately do not call `g`; this small version of the test,
+    // after adding such a call, was (properly) rejected even when the
+    // system still suffered from issue #29793.
+
+    // g(10, 20)(true);
+}
+
+fn escaping_borrow_of_closure_params_2() {
+    let g = |x: usize, y:usize| {
+        let f = |t: bool| if t { x } else { y }; // (separate errors for `x` vs `y`)
+        //~^ ERROR `x` does not live long enough
+        //~| ERROR `y` does not live long enough
+        f
+    };
+
+    // (we don't call `g`; see above)
+}
+
+fn move_of_closure_params() {
+    let g = |x: usize, y:usize| {
+        let f = move |t: bool| if t { x } else { y };
+        f;
+    };
+    // (this code is fine, so lets go ahead and ensure rustc accepts call of `g`)
+    (g(1,2));
+}
+
+fn ok_borrow_of_fn_params(a: usize, b:usize) {
+    let g = |x: usize, y:usize| {
+        let f = |t: bool| if t { a } else { b };
+        return f;
+    };
+    // (this code is fine, so lets go ahead and ensure rustc accepts call of `g`)
+    (g(1,2))(true);
+}
+
+// TOP-LEVEL FN'S
+
+fn escaping_borrow_of_fn_params_1() {
+    fn g<'a>(x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a> {
+        let f = |t: bool| if t { x } else { y }; // (separate errors for `x` vs `y`)
+        //~^ ERROR E0373
+        //~| ERROR E0373
+        return Box::new(f);
+    };
+
+    // (we don't call `g`; see above)
+}
+
+fn escaping_borrow_of_fn_params_2() {
+    fn g<'a>(x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a> {
+        let f = |t: bool| if t { x } else { y }; // (separate errors for `x` vs `y`)
+        //~^ ERROR E0373
+        //~| ERROR E0373
+        Box::new(f)
+    };
+
+    // (we don't call `g`; see above)
+}
+
+fn move_of_fn_params() {
+    fn g<'a>(x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a> {
+        let f = move |t: bool| if t { x } else { y };
+        return Box::new(f);
+    };
+    // (this code is fine, so lets go ahead and ensure rustc accepts call of `g`)
+    (g(1,2))(true);
+}
+
+// INHERENT METHODS
+
+fn escaping_borrow_of_method_params_1() {
+    struct S;
+    impl S {
+        fn g<'a>(&self, x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a> {
+            let f = |t: bool| if t { x } else { y }; // (separate errors for `x` vs `y`)
+            //~^ ERROR E0373
+            //~| ERROR E0373
+            return Box::new(f);
+        }
+    }
+
+    // (we don't call `g`; see above)
+}
+
+fn escaping_borrow_of_method_params_2() {
+    struct S;
+    impl S {
+        fn g<'a>(&self, x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a> {
+            let f = |t: bool| if t { x } else { y }; // (separate errors for `x` vs `y`)
+            //~^ ERROR E0373
+            //~| ERROR E0373
+            Box::new(f)
+        }
+    }
+    // (we don't call `g`; see above)
+}
+
+fn move_of_method_params() {
+    struct S;
+    impl S {
+        fn g<'a>(&self, x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a> {
+            let f = move |t: bool| if t { x } else { y };
+            return Box::new(f);
+        }
+    }
+    // (this code is fine, so lets go ahead and ensure rustc accepts call of `g`)
+    (S.g(1,2))(true);
+}
+
+// TRAIT IMPL METHODS
+
+fn escaping_borrow_of_trait_impl_params_1() {
+    trait T { fn g<'a>(&self, x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a>; }
+    struct S;
+    impl T for S {
+        fn g<'a>(&self, x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a> {
+            let f = |t: bool| if t { x } else { y }; // (separate errors for `x` vs `y`)
+            //~^ ERROR E0373
+            //~| ERROR E0373
+            return Box::new(f);
+        }
+    }
+
+    // (we don't call `g`; see above)
+}
+
+fn escaping_borrow_of_trait_impl_params_2() {
+    trait T { fn g<'a>(&self, x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a>; }
+    struct S;
+    impl T for S {
+        fn g<'a>(&self, x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a> {
+            let f = |t: bool| if t { x } else { y }; // (separate errors for `x` vs `y`)
+            //~^ ERROR E0373
+            //~| ERROR E0373
+            Box::new(f)
+        }
+    }
+    // (we don't call `g`; see above)
+}
+
+fn move_of_trait_impl_params() {
+    trait T { fn g<'a>(&self, x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a>; }
+    struct S;
+    impl T for S {
+        fn g<'a>(&self, x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a> {
+            let f = move |t: bool| if t { x } else { y };
+            return Box::new(f);
+        }
+    }
+    // (this code is fine, so lets go ahead and ensure rustc accepts call of `g`)
+    (S.g(1,2))(true);
+}
+
+// TRAIT DEFAULT METHODS
+
+fn escaping_borrow_of_trait_default_params_1() {
+    struct S;
+    trait T {
+        fn g<'a>(&self, x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a> {
+            let f = |t: bool| if t { x } else { y }; // (separate errors for `x` vs `y`)
+            //~^ ERROR E0373
+            //~| ERROR E0373
+            return Box::new(f);
+        }
+    }
+    impl T for S {}
+    // (we don't call `g`; see above)
+}
+
+fn escaping_borrow_of_trait_default_params_2() {
+    struct S;
+    trait T {
+        fn g<'a>(&self, x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a> {
+            let f = |t: bool| if t { x } else { y }; // (separate errors for `x` vs `y`)
+            //~^ ERROR E0373
+            //~| ERROR E0373
+            Box::new(f)
+        }
+    }
+    impl T for S {}
+    // (we don't call `g`; see above)
+}
+
+fn move_of_trait_default_params() {
+    struct S;
+    trait T {
+        fn g<'a>(&self, x: usize, y:usize) -> Box<Fn(bool) -> usize + 'a> {
+            let f = move |t: bool| if t { x } else { y };
+            return Box::new(f);
+        }
+    }
+    impl T for S {}
+    // (this code is fine, so lets go ahead and ensure rustc accepts call of `g`)
+    (S.g(1,2))(true);
+}
+
+fn main() { }
+
index ac269a4d896f2566bbe0600366e7590d602a5d62..152c65cb69bf24b7f3a5aa1076ddd411fad4b03a 100644 (file)
@@ -26,10 +26,10 @@ fn f<'a, T, U>(v: Box<A<T>+'static>) -> Box<X+'static> {
     // oh dear!
     box B(&*v) as Box<X>
         //~^ ERROR the parameter type `T` may not live long enough
-        //~| WARNING the parameter type `T` may not live long enough
-        //~| WARNING the parameter type `T` may not live long enough
         //~| ERROR the parameter type `T` may not live long enough
-        //~| WARNING the parameter type `T` may not live long enough
+        //~| ERROR the parameter type `T` may not live long enough
+        //~| ERROR the parameter type `T` may not live long enough
+        //~| ERROR the parameter type `T` may not live long enough
         //~| ERROR the parameter type `T` may not live long enough
         //~| ERROR the parameter type `T` may not live long enough
 }
diff --git a/src/test/compile-fail/regions-free-region-ordering-callee-4.rs b/src/test/compile-fail/regions-free-region-ordering-callee-4.rs
new file mode 100644 (file)
index 0000000..bd31d1a
--- /dev/null
@@ -0,0 +1,21 @@
+// 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.
+
+// Tests that callees correctly infer an ordering between free regions
+// that appear in their parameter list.  See also
+// regions-free-region-ordering-caller.rs
+
+fn ordering4<'a, 'b, F>(a: &'a usize, b: &'b usize, x: F) where F: FnOnce(&'a &'b usize) {
+    //~^ ERROR reference has a longer lifetime than the data it references
+    // Do not infer ordering from closure argument types.
+    let z: Option<&'a &'b usize> = None;
+}
+
+fn main() {}
index 22724081a1bcec374a1fc33a50c5650d198b6cb2..1893395e2b0079e480edad4b4a6c0963e1b3b707 100644 (file)
@@ -30,11 +30,7 @@ fn ordering3<'a, 'b>(x: &'a usize, y: &'b usize) -> &'a &'b usize {
     panic!();
 }
 
-fn ordering4<'a, 'b, F>(a: &'a usize, b: &'b usize, x: F) where F: FnOnce(&'a &'b usize) {
-    // Do not infer ordering from closure argument types.
-    let z: Option<&'a &'b usize> = None;
-    //~^ ERROR reference has a longer lifetime than the data it references
-}
+// see regions-free-region-ordering-callee-4.rs
 
 fn ordering5<'a, 'b>(a: &'a usize, b: &'b usize, x: Option<&'a &'b usize>) {
     let z: Option<&'a &'b usize> = None;
index 47985f931dd34fe190cb588e123f6c69d1959a26..fd186d16559162b214001b548fc5b11301c10c82 100644 (file)
@@ -24,16 +24,13 @@ trait Trait2<'a, 'b> {
     type Foo;
 }
 
-fn wf<T>() { }
-
-// As a side-effect of the conservative process above, this argument
-// is not automatically considered well-formed, since for it to be WF,
-// we would need to know that `'y: 'x`, but we do not infer that.
-fn callee<'x, 'y, T>(
-    t: &'x for<'z> Trait1< <T as Trait2<'y, 'z>>::Foo >)
-{
-    wf::<&'x &'y i32>();
+// As a side-effect of the conservative process above, the type of
+// this argument `t` is not automatically considered well-formed,
+// since for it to be WF, we would need to know that `'y: 'x`, but we
+// do not infer that.
+fn callee<'x, 'y, T>(t: &'x for<'z> Trait1< <T as Trait2<'y, 'z>>::Foo >)
     //~^ ERROR reference has a longer lifetime than the data it references
+{
 }
 
 fn main() { }
index e1f1fdaeb341d5bbcb10469de482e124e9d9ebf0..40b715cf3b14d7ac4dfa59c338ed8aaebef32900 100644 (file)
@@ -14,8 +14,7 @@
 trait TheTrait<'t>: 't { }
 
 struct Foo<'a,'b> {
-    x: Box<TheTrait<'a>+'b>
-        //~^ ERROR reference has a longer lifetime
+    x: Box<TheTrait<'a>+'b> //~ ERROR E0478
 }
 
 fn main() { }
index 7a0623ba44f0240c2713d3dd644ffa5917ad6d77..9aa61418d6d047d60d2270c5d09de01525a8ab78 100644 (file)
@@ -25,8 +25,6 @@ fn main() {
     //~^ ERROR mismatched types
     //~| expected `usize`
     //~| found `bool`
-    //~| expected usize
-    //~| found bool) [E0308]
     //~| ERROR expected positive integer for repeat count, found boolean [E0306]
     let d = [0; 0.5];
     //~^ ERROR mismatched types
@@ -46,15 +44,11 @@ fn main() {
     //~^ ERROR mismatched types
     //~| expected `usize`
     //~| found `isize`
-    //~| expected usize
-    //~| found isize) [E0308]
     //~| ERROR expected positive integer for repeat count, found negative integer [E0306]
     let f = [0_usize; -1_isize];
     //~^ ERROR mismatched types
     //~| expected `usize`
     //~| found `isize`
-    //~| expected usize
-    //~| found isize) [E0308]
     //~| ERROR expected positive integer for repeat count, found negative integer [E0306]
     struct G {
         g: (),
index cdb812790480c109ed28dce24a18bd709fe7e902..284c08ef09b28fa4c475ab575af71d706fce72db 100644 (file)
@@ -16,6 +16,7 @@ fn matcher1(x: opts) {
     match x {
       opts::a(ref i) | opts::b(i) => {}
       //~^ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
+      //~^^ ERROR mismatched types
       opts::c(_) => {}
     }
 }
@@ -24,6 +25,7 @@ fn matcher2(x: opts) {
     match x {
       opts::a(ref i) | opts::b(i) => {}
       //~^ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
+      //~^^ ERROR mismatched types
       opts::c(_) => {}
     }
 }
@@ -32,6 +34,7 @@ fn matcher4(x: opts) {
     match x {
       opts::a(ref mut i) | opts::b(ref i) => {}
       //~^ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
+      //~^^ ERROR mismatched types
       opts::c(_) => {}
     }
 }
index 341fe173a03f4d7d0b43eed77f3ab4a6cff79ce5..88f09233d107ee07f5f9079220fd2dcaef924ebe 100644 (file)
@@ -15,6 +15,7 @@
 trait TraitA<A> {
     fn outer(self) {
         enum Foo<B> {
+            //~^ ERROR parameter `B` is never used
             Variance(A)
                 //~^ ERROR can't use type parameters from outer function
                 //~^^ ERROR use of undeclared type name `A`
@@ -27,6 +28,7 @@ trait TraitB<A> {
         struct Foo<B>(A);
                 //~^ ERROR can't use type parameters from outer function
                 //~^^ ERROR use of undeclared type name `A`
+                //~^^^ ERROR parameter `B` is never used
     }
 }
 
@@ -35,6 +37,7 @@ trait TraitC<A> {
         struct Foo<B> { a: A }
                 //~^ ERROR can't use type parameters from outer function
                 //~^^ ERROR use of undeclared type name `A`
+                //~^^^ ERROR parameter `B` is never used
     }
 }
 
diff --git a/src/test/compile-fail/rfc1214-warn-and-error.rs b/src/test/compile-fail/rfc1214-warn-and-error.rs
deleted file mode 100644 (file)
index 50fd3fc..0000000
+++ /dev/null
@@ -1,37 +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.
-
-// Test that an RFC1214 warning from an earlier function (`foo`) does
-// not suppress an error for the same problem (`WantEq<NotEq>`,
-// `NotEq: !Eq`) in a later function (`bar)`. Earlier versions of the
-// warning mechanism had an issue due to caching.
-
-#![allow(dead_code)]
-#![allow(unused_variables)]
-
-struct WantEq<T:Eq> { t: T }
-
-struct NotEq;
-
-trait Trait<T> { }
-
-fn foo() {
-    let x: Box<Trait<WantEq<NotEq>>> = loop { };
-    //~^ WARN E0277
-}
-
-fn bar() {
-    wf::<WantEq<NotEq>>();
-    //~^ ERROR E0277
-}
-
-fn wf<T>() { }
-
-fn main() { }
diff --git a/src/test/compile-fail/shadowed-use-visibility.rs b/src/test/compile-fail/shadowed-use-visibility.rs
new file mode 100644 (file)
index 0000000..bfc6a4e
--- /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.
+
+mod foo {
+    pub fn f() {}
+
+    use foo as bar;
+    pub use self::f as bar;
+}
+
+mod bar {
+    use foo::bar::f as g; //~ ERROR unresolved import
+}
+
+fn main() {}
index c980572fa152f595693a2e5132be769fb85f23c6..560af9193b35ea8b2eb34402faa53be07c0bed3a 100644 (file)
@@ -36,8 +36,6 @@ fn foo(p: &Panolpy) {
     //~^ ERROR mismatched types
     //~| expected `i32`
     //~| found `i64`
-    //~| expected i32
-    //~| found i64)
 }
 
 fn main() {
index 1fd9357cf2d4e3276e206b56bfc4d1dad4857117..d5ab13affafb1641df8bfa179252be37ae60ff40 100644 (file)
@@ -15,7 +15,7 @@ struct BuildData {
 }
 
 fn main() {
-    let foo = BuildData { //~ ERROR missing field: `bar`
+    let foo = BuildData { //~ ERROR missing field `bar` in initializer of `BuildData`
         foo: 0
     };
 }
diff --git a/src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs b/src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs
new file mode 100644 (file)
index 0000000..1d04679
--- /dev/null
@@ -0,0 +1,72 @@
+// 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.
+
+// Beginners write `mod.item` when they should write `mod::item`.
+// This tests that we suggest the latter when we encounter the former.
+
+pub mod a {
+    pub const I: i32 = 1;
+
+    pub fn f() -> i32 { 2 }
+
+    pub mod b {
+        pub const J: i32 = 3;
+
+        pub fn g() -> i32 { 4 }
+    }
+}
+
+fn h1() -> i32 {
+    a.I
+        //~^ ERROR E0425
+        //~| 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(..)`
+}
+
+fn h3() -> i32 {
+    a.b.J
+        //~^ ERROR E0425
+        //~| 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`
+}
+
+fn h5() -> i32 {
+    a.b.f()
+        //~^ ERROR E0425
+        //~| HELP To reference an item from the `a` module, use `a::b`
+}
+
+fn h6() -> i32 {
+    a::b.f()
+        //~^ ERROR E0425
+        //~| 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 the value of an expression
+}
+
+fn h8() -> i32 {
+    a::b()
+        //~^ ERROR E0425
+        //~| HELP No function corresponds to `a::b(..)`
+}
index 506aed6b2ee1db80740ae59788d486294f231be9..38a6834a9c46034772d41ffd94c7bebd76cabac3 100644 (file)
@@ -14,7 +14,7 @@
 
 pub fn main() {
     let asdf_fdsa = "<.<".to_string();
-    assert_eq!(concat_idents!(asd, f_f, dsa), "<.<".to_string());
+    assert!(concat_idents!(asd, f_f, dsa) == "<.<".to_string());
     //~^ ERROR: unresolved name `asdf_fdsa`
 
     assert_eq!(stringify!(use_mention_distinction), "use_mention_distinction");
index beabdcea2bbe9f5beb3571ebeb19eb9e84e07cc9..01910939a80ebd6af13a30eb67a4e1a4646e0438 100644 (file)
@@ -14,7 +14,7 @@ trait Iterator<A> {
     fn next(&mut self) -> Option<A>;
 }
 
-trait IteratorUtil<A>
+trait IteratorUtil<A>: Sized
 {
     fn zip<B, U: Iterator<U>>(self, other: U) -> ZipIterator<Self, U>;
 }
index 6050b549b656f5bf5f228686136a98ab014ed14b..f30c8f521bdc75b919c2dccbbeefa1c8d5cf8853 100644 (file)
@@ -18,7 +18,7 @@ extern crate trait_safety_lib as lib;
 struct Bar;
 impl lib::Foo for Bar { //~ ERROR requires an `unsafe impl` declaration
     fn foo(&self) -> isize {
-        *self as isize
+        panic!();
     }
 }
 
index 1bd6d76360768f8602e46615c73e2eff418776c8..e846b660c2a175f3380f5621feb580ecbe066bce 100644 (file)
 // impls cannot be unsafe.
 
 trait SafeTrait {
-    fn foo(self) { }
+    fn foo(&self) { }
 }
 
 unsafe trait UnsafeTrait {
-    fn foo(self) { }
+    fn foo(&self) { }
 }
 
 unsafe impl UnsafeTrait for u8 { } // OK
index 73be7cf0dc0325ad55531dd6dfe4b30e26663039..2d4df77f960452a126928ce40ee05fb108324012 100644 (file)
@@ -22,5 +22,4 @@ fn main() {
     //~^ ERROR E0038
     //~| ERROR E0038
     //~| ERROR E0277
-    //~| WARNING E0038
 }
index bb1e199920d5c512165b2436f5086e31a6c274a3..99d6437c02ed4ecbab77b8fdd2abe1800fc85bf6 100644 (file)
@@ -20,14 +20,10 @@ fn main() {
     //~^ ERROR mismatched types
     //~| expected `u16`
     //~| found `u8`
-    //~| expected u16
-    //~| found u8
     identity_u16(y);
     //~^ ERROR mismatched types
     //~| expected `u16`
     //~| found `i32`
-    //~| expected u16
-    //~| found i32
 
     let a = 3;
 
@@ -38,7 +34,4 @@ fn main() {
     //~^ ERROR mismatched types
     //~| expected `u16`
     //~| found `isize`
-    //~| expected u16
-    //~| found isize
-
 }
diff --git a/src/test/compile-fail/type-ascription-feature-gate.rs b/src/test/compile-fail/type-ascription-feature-gate.rs
new file mode 100644 (file)
index 0000000..d3c07d6
--- /dev/null
@@ -0,0 +1,15 @@
+// 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.
+
+// Type ascription is feature gated
+
+fn main() {
+    let a = 10: u8; //~ ERROR type ascription is experimental
+}
diff --git a/src/test/compile-fail/type-ascription-precedence.rs b/src/test/compile-fail/type-ascription-precedence.rs
new file mode 100644 (file)
index 0000000..bb7a8bc
--- /dev/null
@@ -0,0 +1,64 @@
+// 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.
+
+// Operator precedence of type ascription
+// Type ascription has very high precedence, the same as operator `as`
+
+#![feature(type_ascription)]
+
+use std::ops::*;
+
+struct S;
+struct Z;
+
+impl Add<Z> for S {
+    type Output = S;
+    fn add(self, _rhs: Z) -> S { panic!() }
+}
+impl Mul<Z> for S {
+    type Output = S;
+    fn mul(self, _rhs: Z) -> S { panic!() }
+}
+impl Neg for S {
+    type Output = Z;
+    fn neg(self) -> Z { panic!() }
+}
+impl Deref for S {
+    type Target = Z;
+    fn deref(&self) -> &Z { panic!() }
+}
+
+fn main() {
+    &S: &S; // OK
+    (&S): &S; // OK
+    &(S: &S); //~ ERROR mismatched types
+
+    *S: Z; // OK
+    (*S): Z; // OK
+    *(S: Z); //~ ERROR mismatched types
+    //~^ ERROR type `Z` cannot be dereferenced
+
+    -S: Z; // OK
+    (-S): Z; // OK
+    -(S: Z); //~ ERROR mismatched types
+    //~^ ERROR cannot apply unary operator `-` to type `Z`
+
+    S + Z: Z; // OK
+    S + (Z: Z); // OK
+    (S + Z): Z; //~ ERROR mismatched types
+
+    S * Z: Z; // OK
+    S * (Z: Z); // OK
+    (S * Z): Z; //~ ERROR mismatched types
+
+    S .. S: S; // OK
+    S .. (S: S); // OK
+    (S .. S): S; //~ ERROR mismatched types
+}
diff --git a/src/test/compile-fail/type-ascription-soundness.rs b/src/test/compile-fail/type-ascription-soundness.rs
new file mode 100644 (file)
index 0000000..2d882e8
--- /dev/null
@@ -0,0 +1,23 @@
+// 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.
+
+// Type ascription doesn't lead to unsoundness
+
+#![feature(type_ascription)]
+
+fn main() {
+    let arr = &[1u8, 2, 3];
+    let ref x = arr: &[u8]; //~ ERROR mismatched types
+    let ref mut x = arr: &[u8]; //~ ERROR mismatched types
+    match arr: &[u8] { //~ ERROR mismatched types
+        ref x => {}
+    }
+    let _len = (arr: &[u8]).len(); //~ ERROR mismatched types
+}
index 627300a037720b92ba9a45e7981b1f9a985638ee..dd8f54cdabb8fe92c2945716848f2c1518b095a0 100644 (file)
@@ -19,5 +19,4 @@ fn main() { let a: bool = 1; let b: i32 = true; }
 //~| ERROR mismatched types
 //~| expected `i32`
 //~| found `bool`
-//~| expected i32
-//~| found bool
+
diff --git a/src/test/compile-fail/type-parameter-invalid-lint.rs b/src/test/compile-fail/type-parameter-invalid-lint.rs
new file mode 100644 (file)
index 0000000..9291329
--- /dev/null
@@ -0,0 +1,17 @@
+// 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.
+
+#![deny(future_incompatible)]
+#![allow(dead_code)]
+
+fn avg<T=i32>(_: T) {}
+//~^ ERROR defaults for type parameters are only allowed
+//~| WARNING hard error
+fn main() {}
diff --git a/src/test/compile-fail/typeck-default-trait-impl-trait-where-clause-2.rs b/src/test/compile-fail/typeck-default-trait-impl-trait-where-clause-2.rs
new file mode 100644 (file)
index 0000000..ad58ae9
--- /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.
+
+// ignore-tidy-linelength
+
+// Test that when a `..` impl applies, we also check that any
+// supertrait conditions are met.
+
+#![feature(optin_builtin_traits)]
+
+trait NotImplemented { }
+
+trait MyTrait: Sized
+    where Option<Self> : NotImplemented
+{}
+
+impl NotImplemented for i32 {}
+
+impl MyTrait for .. {}
+
+fn bar<T:NotImplemented>() { }
+
+fn test() {
+    bar::<Option<i32>>();
+    //~^ ERROR the trait `NotImplemented` is not implemented for the type `core::option::Option<i32>`
+}
+
+fn main() {
+}
index 8057ca56621c12930278034aee6bc98b119f3821..ff8fbd49574917f78aac78c181fc5032a2aea6e0 100644 (file)
@@ -17,7 +17,7 @@
 
 trait NotImplemented { }
 
-trait MyTrait
+trait MyTrait: Sized
     where Option<Self> : NotImplemented
 {}
 
@@ -26,20 +26,11 @@ impl NotImplemented for i32 {}
 impl MyTrait for .. {}
 
 fn foo<T:MyTrait>() {
-    bar::<Option<T>>()
     //~^ ERROR the trait `NotImplemented` is not implemented for the type `core::option::Option<T>`
-    //
     // This should probably typecheck. This is #20671.
 }
 
 fn bar<T:NotImplemented>() { }
 
-fn test() {
-    bar::<Option<i32>>();
-    //~^ ERROR the trait `NotImplemented` is not implemented for the type `core::option::Option<i32>`
-}
-
 fn main() {
-    foo::<u32>();
-    //~^ ERROR the trait `NotImplemented` is not implemented for the type `core::option::Option<u32>`
 }
index 5d2b5fa52db41b29910a9952974f2c2258f12358..e54a7623cb0c3c4a5b7c5787e94d0d1545425f7b 100644 (file)
@@ -15,7 +15,7 @@ struct Foo {
 }
 
 impl Foo {
-    fn foo(self: isize, x: isize) -> isize {  //~ ERROR mismatched self type
+    fn foo(self: isize, x: isize) -> isize {  //~ ERROR mismatched method receiver
         self.f + x
     }
 }
@@ -25,10 +25,10 @@ struct Bar<T> {
 }
 
 impl<T> Bar<T> {
-    fn foo(self: Bar<isize>, x: isize) -> isize { //~ ERROR mismatched self type
+    fn foo(self: Bar<isize>, x: isize) -> isize { //~ ERROR mismatched method receiver
         x
     }
-    fn bar(self: &Bar<usize>, x: isize) -> isize {   //~ ERROR mismatched self type
+    fn bar(self: &Bar<usize>, x: isize) -> isize {   //~ ERROR mismatched method receiver
         x
     }
 }
@@ -41,15 +41,16 @@ trait SomeTrait {
 
 impl<'a, T> SomeTrait for &'a Bar<T> {
     fn dummy1(self: &&'a Bar<T>) { }
-    fn dummy2(self: &Bar<T>) {} //~ ERROR mismatched self type
+    fn dummy2(self: &Bar<T>) {} //~ ERROR mismatched types
+    //~^ ERROR mismatched types
     fn dummy3(self: &&Bar<T>) {}
     //~^ ERROR mismatched types
-    //~| expected `&'a Bar<T>`
-    //~| found `&Bar<T>`
+    //~| expected `&&'a Bar<T>`
+    //~| found `&&Bar<T>`
     //~| lifetime mismatch
     //~| ERROR mismatched types
-    //~| expected `&'a Bar<T>`
-    //~| found `&Bar<T>`
+    //~| expected `&&'a Bar<T>`
+    //~| found `&&Bar<T>`
     //~| lifetime mismatch
 }
 
index 5edcf1d99a02bda2285b092177f6a65d4fdf793d..b6207450d983594ffe1c8a4c9127cbe67d2b15d0 100644 (file)
@@ -8,24 +8,26 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-tidy-linelength
+
 use foo::bar; //~ ERROR unresolved import `foo::bar`. Maybe a missing `extern crate foo`?
 
-use bar::baz as x; //~ ERROR unresolved import `bar::baz`. There is no `baz` in `bar`
+use bar::Baz as x; //~ ERROR unresolved import `bar::Baz`. There is no `Baz` in `bar`. Did you mean to use `Bar`?
 
-use food::baz; //~ ERROR unresolved import `food::baz`. There is no `baz` in `food`
+use food::baz; //~ ERROR unresolved import `food::baz`. There is no `baz` in `food`. Did you mean to use the re-exported import `bag`?
 
-use food::{quux as beans}; //~ ERROR unresolved import `food::quux`. There is no `quux` in `food`
+use food::{beens as Foo}; //~ ERROR unresolved import `food::beens`. There is no `beens` in `food`. Did you mean to use the re-exported import `beans`?
 
 mod bar {
-    struct bar;
+    pub struct Bar;
 }
 
 mod food {
-    pub use self::zug::baz::{self as bag, quux as beans};
+    pub use self::zug::baz::{self as bag, foobar as beans};
 
     mod zug {
         pub mod baz {
-            pub struct quux;
+            pub struct foobar;
         }
     }
 }
diff --git a/src/test/compile-fail/variance-btree-invariant-types.rs b/src/test/compile-fail/variance-btree-invariant-types.rs
new file mode 100644 (file)
index 0000000..e9607de
--- /dev/null
@@ -0,0 +1,63 @@
+// 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::collections::btree_map::{IterMut, OccupiedEntry, VacantEntry};
+
+fn iter_cov_key<'a, 'new>(v: IterMut<'a, &'static (), ()>) -> IterMut<'a, &'new (), ()> {
+    v //~ ERROR mismatched types
+}
+fn iter_cov_val<'a, 'new>(v: IterMut<'a, (), &'static ()>) -> IterMut<'a, (), &'new ()> {
+    v //~ ERROR mismatched types
+}
+fn iter_contra_key<'a, 'new>(v: IterMut<'a, &'new (), ()>) -> IterMut<'a, &'static (), ()> {
+    v //~ ERROR mismatched types
+}
+fn iter_contra_val<'a, 'new>(v: IterMut<'a, (), &'new ()>) -> IterMut<'a, (), &'static ()> {
+    v //~ ERROR mismatched types
+}
+
+fn occ_cov_key<'a, 'new>(v: OccupiedEntry<'a, &'static (), ()>)
+                         -> OccupiedEntry<'a, &'new (), ()> {
+    v //~ ERROR mismatched types
+}
+fn occ_cov_val<'a, 'new>(v: OccupiedEntry<'a, (), &'static ()>)
+                         -> OccupiedEntry<'a, (), &'new ()> {
+    v //~ ERROR mismatched types
+}
+fn occ_contra_key<'a, 'new>(v: OccupiedEntry<'a, &'new (), ()>)
+                            -> OccupiedEntry<'a, &'static (), ()> {
+    v //~ ERROR mismatched types
+}
+fn occ_contra_val<'a, 'new>(v: OccupiedEntry<'a, (), &'new ()>)
+                            -> OccupiedEntry<'a, (), &'static ()> {
+    v //~ ERROR mismatched types
+}
+
+fn vac_cov_key<'a, 'new>(v: VacantEntry<'a, &'static (), ()>)
+                         -> VacantEntry<'a, &'new (), ()> {
+    v //~ ERROR mismatched types
+}
+fn vac_cov_val<'a, 'new>(v: VacantEntry<'a, (), &'static ()>)
+                         -> VacantEntry<'a, (), &'new ()> {
+    v //~ ERROR mismatched types
+}
+fn vac_contra_key<'a, 'new>(v: VacantEntry<'a, &'new (), ()>)
+                            -> VacantEntry<'a, &'static (), ()> {
+    v //~ ERROR mismatched types
+}
+fn vac_contra_val<'a, 'new>(v: VacantEntry<'a, (), &'new ()>)
+                            -> VacantEntry<'a, (), &'static ()> {
+    v //~ ERROR mismatched types
+}
+
+#[rustc_error]
+fn main() { }
index b46cd302ae5ea83ce1b6d2546295c358001aa148..fe61dee23bc2a03d04f63613132160f78766edc3 100644 (file)
@@ -15,13 +15,13 @@ trait Get {
 }
 
 fn get_min_from_max<'min, 'max, G>()
-    where 'max : 'min, &'max G : Get
+    where 'max : 'min, &'max G : Get, G : 'max
 {
     impls_get::<&'min G>(); //~ ERROR mismatched types
 }
 
 fn get_max_from_min<'min, 'max, G>()
-    where 'max : 'min, &'min G : Get
+    where 'max : 'min, &'min G : Get, G : 'min
 {
     impls_get::<&'max G>(); //~ ERROR mismatched types
 }
diff --git a/src/test/compile-fail/visible-private-types-generics.rs b/src/test/compile-fail/visible-private-types-generics.rs
deleted file mode 100644 (file)
index 1f2205b..0000000
+++ /dev/null
@@ -1,67 +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.
-
-trait Foo {
-    fn dummy(&self) { }
-}
-
-pub fn f<
-    T
-    : Foo //~ ERROR private trait in exported type parameter bound
->() {}
-
-pub fn g<T>() where
-    T
-    : Foo //~ ERROR private trait in exported type parameter bound
-{}
-
-pub struct S;
-
-impl S {
-    pub fn f<
-        T
-        : Foo //~ ERROR private trait in exported type parameter bound
-    >() {}
-
-    pub fn g<T>() where
-        T
-        : Foo //~ ERROR private trait in exported type parameter bound
-    {}
-}
-
-pub struct S1<
-    T
-    : Foo //~ ERROR private trait in exported type parameter bound
-> {
-    x: T
-}
-
-pub struct S2<T> where
-    T
-    : Foo //~ ERROR private trait in exported type parameter bound
-{
-    x: T
-}
-
-pub enum E1<
-    T
-    : Foo //~ ERROR private trait in exported type parameter bound
-> {
-    V1(T)
-}
-
-pub enum E2<T> where
-    T
-    : Foo //~ ERROR private trait in exported type parameter bound
-{
-    V2(T)
-}
-
-fn main() {}
diff --git a/src/test/compile-fail/visible-private-types-supertrait.rs b/src/test/compile-fail/visible-private-types-supertrait.rs
deleted file mode 100644 (file)
index 9d9eae4..0000000
+++ /dev/null
@@ -1,17 +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.
-
-trait Foo {
-    fn dummy(&self) { }
-}
-
-pub trait Bar : Foo {} //~ ERROR private trait in exported type
-
-fn main() {}
index c8b7f35b3aa58355940f5201b3f79a2fab0c7f20..946341a1a75bb7d0dae60c4ce7ed6c47a04f5ae5 100644 (file)
@@ -14,8 +14,8 @@
 #![allow(dead_code)]
 
 struct Foo {
-    foo: [[u8]], //~ WARN E0277
+    foo: [[u8]], //~ ERROR E0277
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 1d271d1530a75823fc7c3da83ab75e8d0eee0473..e3e79fdd940af47cc88842fb0efb45a6b355fe09 100644 (file)
 
 trait ExtraCopy<T:Copy> { }
 
-enum SomeEnum<T,U> //~ WARN E0277
+enum SomeEnum<T,U> //~ ERROR E0277
     where T: ExtraCopy<U>
 {
     SomeVariant(T,U)
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 769894613c76496dfb318e339ef46e2a9addc342..3ed9e5d9f1eb58123a5551b9bb75a29cbad3dfa7 100644 (file)
@@ -16,9 +16,9 @@
 
 trait ExtraCopy<T:Copy> { }
 
-fn foo<T,U>() where T: ExtraCopy<U> //~ WARN E0277
+fn foo<T,U>() where T: ExtraCopy<U> //~ ERROR E0277
 {
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 2d7727fff35033c2c98157c381bff06cf90ee414..a319b676eeb880cf660434e616a5e7212ad60638 100644 (file)
@@ -17,8 +17,8 @@ pub trait Foo<'a> {
 }
 
 impl<'a, T> Foo<'a> for T {
-    type Bar = &'a T; //~ WARN E0309
+    type Bar = &'a T; //~ ERROR E0309
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation
+fn main() { }
index 8a612c321570d4253a196955fc9de04be17e46fb..ba31de98e7f95186f7bb54d11fa222ce70f1d945 100644 (file)
@@ -25,9 +25,9 @@ pub trait Foo {
 
 impl<T> Foo for T {
     type Bar = MySet<T>;
-    //~^ WARN the trait `MyHash` is not implemented for the type `T`
+    //~^ ERROR the trait `MyHash` is not implemented for the type `T`
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
 
index 593c9435f6c751dc607575a58e2018a826507442..8e3bca09758139ac179680b3ca2ddfa771ef01eb 100644 (file)
@@ -20,13 +20,13 @@ struct MustBeCopy<T:Copy> {
 
 struct Foo<T> {
     // needs T: 'static
-    x: fn() -> &'static T //~ WARN E0310
+    x: fn() -> &'static T //~ ERROR E0310
 }
 
 struct Bar<T> {
     // needs T: Copy
-    x: fn(&'static T) //~ WARN E0310
+    x: fn(&'static T) //~ ERROR E0310
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index fc3d234aac252d6ae31db99f70c9c46185e30602..c2f66a2a460c5690c72905c93b65771aabb73173 100644 (file)
 trait MustBeCopy<T:Copy> {
 }
 
-fn bar<T,U>() //~ WARN E0277
+fn bar<T,U>() //~ ERROR E0277
     where T: MustBeCopy<U>
 {
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index c697dfd50ad471c3b9add3da74dfd81b82e08878..11535fb9f9e45685a710d2ac452f4ca4481cf19a 100644 (file)
@@ -21,8 +21,8 @@ struct MustBeCopy<T:Copy> {
 
 struct Foo<T> {
     // needs T: 'static
-    x: Object<&'static T> //~ WARN E0310
+    x: Object<&'static T> //~ ERROR E0310
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 44671be8355336674ed49ef30e406ec064a2bda1..78e12c47e24debc7b8f631ece604c86003d5783d 100644 (file)
@@ -19,9 +19,9 @@ trait ExtraCopy<T:Copy> { }
 struct Foo<T,U>(T,U);
 
 impl<T,U> Foo<T,U> {
-    fn foo(self) where T: ExtraCopy<U> //~ WARN E0277
+    fn foo(self) where T: ExtraCopy<U> //~ ERROR E0277
     {}
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index a0f588c1961d17ec2c9d6315e6b6bb20e6d270ca..7edbb11e245200270033c8db2d6446d0867cfce0 100644 (file)
@@ -18,9 +18,9 @@ trait ExtraCopy<T:Copy> { }
 
 struct Foo<T,U>(T,U);
 
-impl<T,U> Foo<T,U> where T: ExtraCopy<U> //~ WARN E0277
+impl<T,U> Foo<T,U> where T: ExtraCopy<U> //~ ERROR E0277
 {
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index dc0cbeff153a96c66447e498e5c566b7801918ff..c11b2e4c544e1fd524f25affa77fe82a637c2fed 100644 (file)
@@ -19,14 +19,14 @@ trait Trait<T> { }
 
 struct Foo<'a,T> {
     f: &'a fn(T),
-    //~^ WARN E0309
+    //~^ ERROR E0309
 }
 
 struct Bar<'a,T> {
     f: &'a Trait<T>,
-    //~^ WARN E0309
+    //~^ ERROR E0309
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
 
index 43378061e40c0bc5e04f48e8c458847cfdc67692..e263b251aa379eb58bf5f12a21ebd7a1d47bf20b 100644 (file)
 
 trait ExtraCopy<T:Copy> { }
 
-struct SomeStruct<T,U> //~ WARN E0277
+struct SomeStruct<T,U> //~ ERROR E0277
     where T: ExtraCopy<U>
 {
     data: (T,U)
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 63a532138e3bd84b3464f78fa581c20818f9a5e1..8420edd66a1809dcaa92f1fe5448fe09b43d518b 100644 (file)
@@ -16,9 +16,9 @@
 
 trait ExtraCopy<T:Copy> { }
 
-trait SomeTrait<T> { //~ WARN E0277
+trait SomeTrait<T> { //~ ERROR E0277
     type Type1: ExtraCopy<T>;
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index b3aa4e19c96560a4cb5c830b8a2da609acf1522f..95d9ffdf9d35958782c0ab8ad105aa2dfc6f5021 100644 (file)
@@ -17,8 +17,8 @@
 trait SomeTrait<'a> {
     type Type1;
     type Type2 = &'a Self::Type1;
-    //~^ WARN E0309
+    //~^ ERROR E0309
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 8c491e04c981dff443772c539de9a0334f06d8a6..902cbe2676b06a5995ccd67137a695831c1ec096 100644 (file)
@@ -19,8 +19,8 @@ struct IsCopy<T:Copy> { x: T }
 trait SomeTrait {
     type Type1;
     type Type2 = IsCopy<Self::Type1>;
-    //~^ WARN E0277
+    //~^ ERROR E0277
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 147b3ce236d42d1e599e401ac82eb1399c418235..ca15a6ab648630d17c15f2502463fa1c7ec80ae8 100644 (file)
 
 trait ExtraCopy<T:Copy> { }
 
-trait SomeTrait<T,U> //~ WARN E0277
+trait SomeTrait<T,U> //~ ERROR E0277
     where T: ExtraCopy<U>
 {
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 57c6c1979f87ed0094714a5f672c53964673af87..453aa2428ce5d1ee94dcf0f5236e524a15ea4970 100644 (file)
@@ -19,11 +19,11 @@ struct Bar<T:Eq+?Sized> { value: Box<T> }
 
 trait Foo {
     fn bar(&self, x: &Bar<Self>) {
-        //~^ WARN E0277
+        //~^ ERROR E0277
         //
         // Here, Eq ought to be implemented.
     }
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 939876403e54db06d2dfb3e96b88f5c605ec59d5..d94708d3e06a557305a48f7e7abe8722ef71c8cf 100644 (file)
@@ -19,12 +19,11 @@ struct Bar<T:Eq+?Sized> { value: Box<T> }
 
 trait Foo {
     fn bar(&self) -> Bar<Self> {
-        //~^ WARN E0277
+        //~^ ERROR E0277
         //
         // Here, Eq ought to be implemented.
         loop { }
     }
 }
 
-#[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index b1c0d71fc5b3b75c784c8fb315c4c266931db5d6..29c85250583c7900c8d90875bc9711b83cc7d674 100644 (file)
@@ -19,11 +19,11 @@ trait Bar<T:Eq+?Sized> { }
 
 trait Foo {
     fn bar<A>(&self) where A: Bar<Self> {
-        //~^ WARN E0277
+        //~^ ERROR E0277
         //
         // Here, Eq ought to be implemented.
     }
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index ff263c85eb371a6bce67ec6b1430c5c786424914..d88e36faeec600578eda6a43a06d068284929b5d 100644 (file)
@@ -18,10 +18,9 @@ struct Bar<T:Eq+?Sized> { value: Box<T> }
 
 trait Foo {
     fn bar(&self, x: &Bar<Self>);
-        //~^ WARN E0277
+        //~^ ERROR E0277
         //
         // Here, Eq ought to be implemented.
 }
 
-#[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 5c8f3030c2c21b947879a13dde2a9ac53e12459a..c368ff9a4a82dbf3bb99a3c6f49d5a952eae37dc 100644 (file)
@@ -18,10 +18,9 @@ struct Bar<T:Eq+?Sized> { value: Box<T> }
 
 trait Foo {
     fn bar(&self) -> &Bar<Self>;
-        //~^ WARN E0277
+        //~^ ERROR E0277
         //
         // Here, Eq ought to be implemented.
 }
 
-#[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 51b5475e51fba1df8398509b65f3482983fcb5c9..f59dca93bb90a72e292fdea49289cad5fc8b2671 100644 (file)
@@ -18,10 +18,10 @@ struct Bar<T:Eq+?Sized> { value: Box<T> }
 
 trait Foo {
     fn bar(&self) where Bar<Self>: Copy;
-        //~^ WARN E0277
+        //~^ ERROR E0277
         //
         // Here, Eq ought to be implemented.
 }
 
 #[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 58ee766dad112fdb40c52ae89e9a03c8ab6cb794..ea8b2fdf3a14766ad28dbb0d09a8fdf3ac49864f 100644 (file)
@@ -16,8 +16,7 @@
 
 trait ExtraCopy<T:Copy> { }
 
-trait SomeTrait<T>: ExtraCopy<T> { //~ WARN E0277
+trait SomeTrait<T>: ExtraCopy<T> { //~ ERROR E0277
 }
 
-#[rustc_error]
-fn main() { } //~ ERROR compilation successful
+fn main() { }
index 24eb407612c3e514669b12e4104559fd5ad2d091..620e1a73b4d64c7e10dd6370333df957add4ff14 100644 (file)
 #![allow(unused_variables)]
 #![feature(omit_gdb_pretty_printer_section)]
 #![omit_gdb_pretty_printer_section]
-#![feature(core_simd)]
+#![feature(repr_simd)]
 
-use std::simd::{i8x16, i16x8,i32x4,i64x2,u8x16,u16x8,u32x4,u64x2,f32x4,f64x2};
+#[repr(simd)]
+struct i8x16(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8);
+#[repr(simd)]
+struct i16x8(i16, i16, i16, i16, i16, i16, i16, i16);
+#[repr(simd)]
+struct i32x4(i32, i32, i32, i32);
+#[repr(simd)]
+struct i64x2(i64, i64);
+#[repr(simd)]
+struct u8x16(u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8);
+#[repr(simd)]
+struct u16x8(u16, u16, u16, u16, u16, u16, u16, u16);
+#[repr(simd)]
+struct u32x4(u32, u32, u32, u32);
+#[repr(simd)]
+struct u64x2(u64, u64);
+#[repr(simd)]
+struct f32x4(f32, f32, f32, f32);
+#[repr(simd)]
+struct f64x2(f64, f64);
 
 fn main() {
 
index 3db3e9d311da09a640ea0e51d89e20e7610cad63..a74369ed3c3a13b7a779c7099185c29f1a7b7bbd 100644 (file)
@@ -182,7 +182,7 @@ use self::Enum1::{Variant1, Variant2};
 use std::marker::PhantomData;
 use std::ptr;
 
-struct Struct1;
+pub struct Struct1;
 struct GenericStruct<T1, T2>(PhantomData<(T1,T2)>);
 
 enum Enum1 {
diff --git a/src/test/parse-fail/assoc-oddities-3.rs b/src/test/parse-fail/assoc-oddities-3.rs
deleted file mode 100644 (file)
index 0d4f21f..0000000
+++ /dev/null
@@ -1,16 +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.
-
-// compile-flags: -Z parse-only
-
-fn that_odd_parse() {
-    // see assoc-oddities-1 for explanation
-    x + if c { a } else { b }[n]; //~ ERROR expected one of
-}
diff --git a/src/test/parse-fail/lex-bad-char-literals-1.rs b/src/test/parse-fail/lex-bad-char-literals-1.rs
new file mode 100644 (file)
index 0000000..7e22a11
--- /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.
+
+// compile-flags: -Z parse-only
+static c3: char =
+    '\x1' //~ ERROR: numeric character escape is too short
+;
+
+static s: &'static str =
+    "\x1" //~ ERROR: numeric character escape is too short
+;
+
+static c: char =
+    '\●' //~ ERROR: unknown character escape
+;
+
+static s: &'static str =
+    "\●" //~ ERROR: unknown character escape
+;
+
diff --git a/src/test/parse-fail/lex-bad-char-literals-2.rs b/src/test/parse-fail/lex-bad-char-literals-2.rs
new file mode 100644 (file)
index 0000000..8bd6808
--- /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
+
+// This test needs to the last one appearing in this file as it kills the parser
+static c: char =
+    'nope' //~ ERROR: character literal may only contain one codepoint: 'nope'
+;
+
diff --git a/src/test/parse-fail/lex-bad-char-literals-3.rs b/src/test/parse-fail/lex-bad-char-literals-3.rs
new file mode 100644 (file)
index 0000000..92432dc
--- /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.
+
+// compile-flags: -Z parse-only
+
+// This test needs to the last one appearing in this file as it kills the parser
+static c: char =
+    '●●' //~ ERROR: character literal may only contain one codepoint: '●
+;
diff --git a/src/test/parse-fail/lex-bad-char-literals-4.rs b/src/test/parse-fail/lex-bad-char-literals-4.rs
new file mode 100644 (file)
index 0000000..b230e62
--- /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.
+
+// compile-flags: -Z parse-only
+//
+// This test needs to the last one appearing in this file as it kills the parser
+static c: char =
+    '●  //~ ERROR: character literal may only contain one codepoint: '●
+;
diff --git a/src/test/parse-fail/lex-bad-char-literals-5.rs b/src/test/parse-fail/lex-bad-char-literals-5.rs
new file mode 100644 (file)
index 0000000..5259175
--- /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.
+
+// compile-flags: -Z parse-only
+//
+// This test needs to the last one appearing in this file as it kills the parser
+static c: char =
+    '\x10\x10'  //~ ERROR: character literal may only contain one codepoint: '\x10
+;
diff --git a/src/test/parse-fail/lex-bad-char-literals.rs b/src/test/parse-fail/lex-bad-char-literals.rs
deleted file mode 100644 (file)
index 6335632..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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.
-
-// compile-flags: -Z parse-only
-static c3: char =
-    '\x1' //~ ERROR: numeric character escape is too short
-;
-
-static s: &'static str =
-    "\x1" //~ ERROR: numeric character escape is too short
-;
-
-static c: char =
-    '\●' //~ ERROR: unknown character escape
-;
-
-static s: &'static str =
-    "\●" //~ ERROR: unknown character escape
-;
-
-// THIS MUST BE LAST, since it kills the lexer
-
-static c: char =
-    '●  //~ ERROR: character literal may only contain one codepoint
-;
diff --git a/src/test/parse-fail/lifetime-semicolon.rs b/src/test/parse-fail/lifetime-semicolon.rs
new file mode 100644 (file)
index 0000000..7010d0e
--- /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.
+
+// compile-flags: -Z parse-only
+
+struct Foo<'a, 'b> {
+    a: &'a &'b i32
+}
+
+fn foo<'a, 'b>(x: &mut Foo<'a; 'b>) {}
+//~^ ERROR expected `,` or `>` after lifetime name, found `;`
+//~^^ NOTE did you mean a single argument type &'a Type, or did you mean the comma-separated
index 987eae66f3e63b8fc1fb41377fcaa381247982e0..1ef8cd2714d7f4124d134384e44904bd9dc21c08 100644 (file)
@@ -14,6 +14,7 @@
 
 fn foo(p: proc()) { } //~ ERROR `proc` is a reserved keyword
 
-fn bar() { proc() 1; }
+fn bar() { proc() 1; } //~ ERROR `proc` is a reserved keyword
+                       //~^ ERROR expected
 
 fn main() { }
diff --git a/src/test/parse-fail/pub-item-macro.rs b/src/test/parse-fail/pub-item-macro.rs
new file mode 100644 (file)
index 0000000..8809e9a
--- /dev/null
@@ -0,0 +1,28 @@
+// 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 #14660
+
+macro_rules! priv_x { () => {
+    static x: u32 = 0;
+}}
+
+macro_rules! pub_x { () => {
+    pub priv_x!(); //~ ERROR can't qualify macro invocation with `pub`
+    //~^ HELP try adjusting the macro to put `pub` inside the invocation
+}}
+
+mod foo {
+    pub_x!();
+}
+
+fn main() {
+    let y: u32 = foo::x;
+}
diff --git a/src/test/parse-fail/pub-macro-rules.rs b/src/test/parse-fail/pub-macro-rules.rs
new file mode 100644 (file)
index 0000000..93b992f
--- /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.
+
+#[macro_use] mod bleh {
+    pub macro_rules! foo { //~ ERROR can't qualify macro_rules invocation with `pub`
+    //~^ HELP did you mean #[macro_export]?
+        ($n:ident) => (
+            fn $n () -> i32 {
+                1
+            }
+        )
+    }
+
+}
+
+foo!(meh);
+
+fn main() {
+    println!("{}", meh());
+}
diff --git a/src/test/parse-fail/pub-method-macro.rs b/src/test/parse-fail/pub-method-macro.rs
new file mode 100644 (file)
index 0000000..198fa5b
--- /dev/null
@@ -0,0 +1,34 @@
+// 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 #18317
+
+mod bleh {
+    macro_rules! defn {
+        ($n:ident) => (
+            fn $n (&self) -> i32 {
+                println!("{}", stringify!($n));
+                1
+            }
+        )
+    }
+
+    #[derive(Copy, Clone)]
+    pub struct S;
+
+    impl S {
+        pub defn!(f); //~ ERROR can't qualify macro invocation with `pub`
+        //~^ HELP try adjusting the macro to put `pub` inside the invocation
+    }
+}
+
+fn main() {
+    bleh::S.f();
+}
index 8fb71e13f16a28c5b1ebf4dd019e37d9d564d2f9..107b836d160a5669bbb47bed4b4090795e923205 100644 (file)
@@ -22,7 +22,7 @@ impl Foo {
 
 fn main() {
     for x in Foo {
-        x: 3    //~ ERROR expected one of `!`, `.`, `::`, `;`, `{`, `}`, or an operator, found `:`
+        x: 3    //~ ERROR expected type, found `3`
     }.hi() {
         println!("yo");
     }
index 1560c83bc70baaad8f55d590579796f14886087e..b1cccc51d7bb9acc70bb389e09a4a83047d0c277 100644 (file)
@@ -22,7 +22,7 @@ impl Foo {
 
 fn main() {
     if Foo {
-        x: 3    //~ ERROR expected one of `!`, `.`, `::`, `;`, `{`, `}`, or an operator, found `:`
+        x: 3    //~ ERROR expected type, found `3`
     }.hi() {
         println!("yo");
     }
index 2052193df91252610ec3def1a3e54e3657e03152..1c52dc48ccd1ac125f4271f00b7810006f806e47 100644 (file)
@@ -22,7 +22,7 @@ impl Foo {
 
 fn main() {
     while Foo {
-        x: 3    //~ ERROR expected one of `!`, `.`, `::`, `;`, `{`, `}`, or an operator, found `:`
+        x: 3    //~ ERROR expected type, found `3`
     }.hi() {
         println!("yo");
     }
diff --git a/src/test/run-fail/mir_indexing_oob_1.rs b/src/test/run-fail/mir_indexing_oob_1.rs
new file mode 100644 (file)
index 0000000..e0d20a2
--- /dev/null
@@ -0,0 +1,23 @@
+// 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.
+
+// error-pattern:index out of bounds: the len is 5 but the index is 10
+#![feature(rustc_attrs)]
+
+const C: [u32; 5] = [0; 5];
+
+#[rustc_mir]
+fn test() -> u32 {
+    C[10]
+}
+
+fn main() {
+    test();
+}
diff --git a/src/test/run-fail/mir_indexing_oob_2.rs b/src/test/run-fail/mir_indexing_oob_2.rs
new file mode 100644 (file)
index 0000000..6c65be5
--- /dev/null
@@ -0,0 +1,23 @@
+// 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.
+
+// error-pattern:index out of bounds: the len is 5 but the index is 10
+#![feature(rustc_attrs)]
+
+const C: &'static [u8; 5] = b"hello";
+
+#[rustc_mir]
+fn test() -> u8 {
+    C[10]
+}
+
+fn main() {
+    test();
+}
diff --git a/src/test/run-fail/mir_indexing_oob_3.rs b/src/test/run-fail/mir_indexing_oob_3.rs
new file mode 100644 (file)
index 0000000..5f3fc93
--- /dev/null
@@ -0,0 +1,23 @@
+// 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.
+
+// error-pattern:index out of bounds: the len is 5 but the index is 10
+#![feature(rustc_attrs)]
+
+const C: &'static [u8; 5] = b"hello";
+
+#[rustc_mir]
+fn mir() -> u8 {
+    C[10]
+}
+
+fn main() {
+    mir();
+}
diff --git a/src/test/run-fail/mir_trans_calls_converging_drops.rs b/src/test/run-fail/mir_trans_calls_converging_drops.rs
new file mode 100644 (file)
index 0000000..754f616
--- /dev/null
@@ -0,0 +1,37 @@
+// 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.
+#![feature(rustc_attrs)]
+// error-pattern:converging_fn called
+// error-pattern:0 dropped
+// error-pattern:exit
+
+use std::io::{self, Write};
+
+struct Droppable(u8);
+impl Drop for Droppable {
+    fn drop(&mut self) {
+        write!(io::stderr(), "{} dropped\n", self.0);
+    }
+}
+
+fn converging_fn() {
+    write!(io::stderr(), "converging_fn called\n");
+}
+
+#[rustc_mir]
+fn mir(d: Droppable) {
+    converging_fn();
+}
+
+fn main() {
+    let d = Droppable(0);
+    mir(d);
+    panic!("exit");
+}
diff --git a/src/test/run-fail/mir_trans_calls_converging_drops_2.rs b/src/test/run-fail/mir_trans_calls_converging_drops_2.rs
new file mode 100644 (file)
index 0000000..5e870be
--- /dev/null
@@ -0,0 +1,41 @@
+// 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.
+#![feature(rustc_attrs)]
+// error-pattern:complex called
+// error-pattern:dropped
+// error-pattern:exit
+
+use std::io::{self, Write};
+
+struct Droppable;
+impl Drop for Droppable {
+    fn drop(&mut self) {
+        write!(io::stderr(), "dropped\n");
+    }
+}
+
+// return value of this function is copied into the return slot
+fn complex() -> u64 {
+    write!(io::stderr(), "complex called\n");
+    42
+}
+
+
+#[rustc_mir]
+fn mir() -> u64 {
+    let x = Droppable;
+    return complex();
+    drop(x);
+}
+
+pub fn main() {
+    assert_eq!(mir(), 42);
+    panic!("exit");
+}
diff --git a/src/test/run-fail/mir_trans_calls_diverging.rs b/src/test/run-fail/mir_trans_calls_diverging.rs
new file mode 100644 (file)
index 0000000..fcd8ab2
--- /dev/null
@@ -0,0 +1,24 @@
+// 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.
+#![feature(rustc_attrs)]
+// error-pattern:diverging_fn called
+
+fn diverging_fn() -> ! {
+    panic!("diverging_fn called")
+}
+
+#[rustc_mir]
+fn mir() {
+    diverging_fn();
+}
+
+fn main() {
+    mir();
+}
diff --git a/src/test/run-fail/mir_trans_calls_diverging_drops.rs b/src/test/run-fail/mir_trans_calls_diverging_drops.rs
new file mode 100644 (file)
index 0000000..ffa1ff0
--- /dev/null
@@ -0,0 +1,34 @@
+// 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.
+#![feature(rustc_attrs)]
+// error-pattern:diverging_fn called
+// error-pattern:0 dropped
+use std::io::{self, Write};
+
+struct Droppable(u8);
+impl Drop for Droppable {
+    fn drop(&mut self) {
+        write!(io::stderr(), "{} dropped", self.0);
+    }
+}
+
+fn diverging_fn() -> ! {
+    panic!("diverging_fn called")
+}
+
+#[rustc_mir]
+fn mir(d: Droppable) {
+    diverging_fn();
+}
+
+fn main() {
+    let d = Droppable(0);
+    mir(d);
+}
diff --git a/src/test/run-fail/mir_trans_no_landing_pads.rs b/src/test/run-fail/mir_trans_no_landing_pads.rs
new file mode 100644 (file)
index 0000000..bc913fd
--- /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.
+#![feature(rustc_attrs)]
+// compile-flags: -Z no-landing-pads
+// error-pattern:converging_fn called
+use std::io::{self, Write};
+
+struct Droppable;
+impl Drop for Droppable {
+    fn drop(&mut self) {
+        ::std::process::exit(1)
+    }
+}
+
+fn converging_fn() {
+    panic!("converging_fn called")
+}
+
+#[rustc_mir]
+fn mir(d: Droppable) {
+    let x = Droppable;
+    converging_fn();
+    drop(x);
+    drop(d);
+}
+
+fn main() {
+    mir(Droppable);
+}
diff --git a/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs b/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs
new file mode 100644 (file)
index 0000000..d97eb8c
--- /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.
+#![feature(rustc_attrs)]
+// compile-flags: -Z no-landing-pads
+// error-pattern:diverging_fn called
+use std::io::{self, Write};
+
+struct Droppable;
+impl Drop for Droppable {
+    fn drop(&mut self) {
+        ::std::process::exit(1)
+    }
+}
+
+fn diverging_fn() -> ! {
+    panic!("diverging_fn called")
+}
+
+#[rustc_mir]
+fn mir(d: Droppable) {
+    let x = Droppable;
+    diverging_fn();
+    drop(x);
+    drop(d);
+}
+
+fn main() {
+    mir(Droppable);
+}
diff --git a/src/test/run-fail/panic-set-handler.rs b/src/test/run-fail/panic-set-handler.rs
new file mode 100644 (file)
index 0000000..bfeb407
--- /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.
+
+// error-pattern:greetings from the panic handler
+
+#![feature(std_panic, panic_handler)]
+use std::panic;
+use std::io::{self, Write};
+
+fn main() {
+    panic::set_handler(|i| {
+        write!(io::stderr(), "greetings from the panic handler");
+    });
+    panic!("foobar");
+}
diff --git a/src/test/run-fail/panic-set-unset-handler.rs b/src/test/run-fail/panic-set-unset-handler.rs
new file mode 100644 (file)
index 0000000..6999aa7
--- /dev/null
@@ -0,0 +1,23 @@
+// 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.
+
+// error-pattern:thread '<main>' panicked at 'foobar'
+
+#![feature(std_panic, panic_handler)]
+use std::panic;
+use std::io::{self, Write};
+
+fn main() {
+    panic::set_handler(|i| {
+        write!(io::stderr(), "greetings from the panic handler");
+    });
+    panic::take_handler();
+    panic!("foobar");
+}
diff --git a/src/test/run-fail/panic-take-handler-nop.rs b/src/test/run-fail/panic-take-handler-nop.rs
new file mode 100644 (file)
index 0000000..fec1db2
--- /dev/null
@@ -0,0 +1,19 @@
+// 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.
+
+// error-pattern:thread '<main>' panicked at 'foobar'
+
+#![feature(std_panic, panic_handler)]
+use std::panic;
+
+fn main() {
+    panic::take_handler();
+    panic!("foobar");
+}
diff --git a/src/test/run-make/emit/Makefile b/src/test/run-make/emit/Makefile
new file mode 100644 (file)
index 0000000..be34028
--- /dev/null
@@ -0,0 +1,15 @@
+-include ../tools.mk
+
+all:
+       $(RUSTC) -Copt-level=0 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs
+       $(RUSTC) -Copt-level=1 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs
+       $(RUSTC) -Copt-level=2 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs
+       $(RUSTC) -Copt-level=3 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs
+       $(RUSTC) -Copt-level=0 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs
+       $(call RUN,test-26235) || exit 1
+       $(RUSTC) -Copt-level=1 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs
+       $(call RUN,test-26235) || exit 1
+       $(RUSTC) -Copt-level=2 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs
+       $(call RUN,test-26235) || exit 1
+       $(RUSTC) -Copt-level=3 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs
+       $(call RUN,test-26235) || exit 1
diff --git a/src/test/run-make/emit/test-24876.rs b/src/test/run-make/emit/test-24876.rs
new file mode 100644 (file)
index 0000000..ab69dec
--- /dev/null
@@ -0,0 +1,19 @@
+// 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.
+
+// Checks for issue #24876
+
+fn main() {
+    let mut v = 0;
+    for i in 0..0 {
+        v += i;
+    }
+    println!("{}", v)
+}
diff --git a/src/test/run-make/emit/test-26235.rs b/src/test/run-make/emit/test-26235.rs
new file mode 100644 (file)
index 0000000..97b58a3
--- /dev/null
@@ -0,0 +1,56 @@
+// 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.
+
+// Checks for issue #26235
+
+fn main() {
+    use std::thread;
+
+    type Key = u32;
+    const NUM_THREADS: usize = 2;
+
+    #[derive(Clone,Copy)]
+    struct Stats<S> {
+        upsert: S,
+        delete: S,
+        insert: S,
+        update: S
+    };
+
+    impl<S> Stats<S> where S: Copy {
+        fn dot<B, F, T>(self, s: Stats<T>, f: F) -> Stats<B> where F: Fn(S, T) -> B {
+            let Stats { upsert: u1, delete: d1, insert: i1, update: p1 } = self;
+            let Stats { upsert: u2, delete: d2, insert: i2, update: p2 } = s;
+            Stats { upsert: f(u1, u2), delete: f(d1, d2), insert: f(i1, i2), update: f(p1, p2) }
+        }
+
+        fn new(init: S) -> Self {
+            Stats { upsert: init, delete: init, insert: init, update: init }
+        }
+    }
+
+    fn make_threads() -> Vec<thread::JoinHandle<()>> {
+        let mut t = Vec::with_capacity(NUM_THREADS);
+        for _ in 0..NUM_THREADS {
+            t.push(thread::spawn(move || {}));
+        }
+        t
+    }
+
+    let stats = [Stats::new(0); NUM_THREADS];
+    make_threads();
+
+    {
+        let Stats { ref upsert, ref delete, ref insert, ref update } = stats.iter().fold(
+            Stats::new(0), |res, &s| res.dot(s, |x: Key, y: Key| x.wrapping_add(y)));
+        println!("upserts: {}, deletes: {}, inserts: {}, updates: {}",
+                 upsert, delete, insert, update);
+    }
+}
index ef646c5bf5d68bb1b3fe9437dda0a58740deff50..4c818cd99e2d795561535831d6653e323c54f113 100644 (file)
@@ -4,6 +4,8 @@
 # This is a basic test of LLVM ExecutionEngine functionality using compiled
 # Rust code built using the `rustc` crate.
 
+ifeq ($(filter executionengine,$(LLVM_COMPONENTS)),executionengine)
+
 ifneq ($(shell uname),FreeBSD)
 all:
        $(RUSTC) test.rs
@@ -12,3 +14,8 @@ else
 all:
 
 endif
+
+else
+all:
+
+endif
index 20dd16872a6535b5ba587114def42df640b15658..dc409f393a86a971fa82fb3005fc0ef78eb286dc 100644 (file)
@@ -195,7 +195,7 @@ fn build_exec_options(sysroot: PathBuf) -> Options {
     opts.maybe_sysroot = Some(sysroot);
 
     // Prefer faster build time
-    opts.optimize = config::No;
+    opts.optimize = config::OptLevel::No;
 
     // Don't require a `main` function
     opts.crate_types = vec![config::CrateTypeDylib];
@@ -215,9 +215,8 @@ fn compile_program(input: &str, sysroot: PathBuf)
     let handle = thread.spawn(move || {
         let opts = build_exec_options(sysroot);
         let cstore = Rc::new(CStore::new(token::get_ident_interner()));
-        let cstore_ = ::rustc_driver::cstore_to_cratestore(cstore.clone());
         let sess = build_session(opts, None, Registry::new(&rustc::DIAGNOSTICS),
-                                 cstore_);
+                                 cstore.clone());
         rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
 
         let cfg = build_configuration(&sess);
index 58bf5049cf13edfce6a04807455691b108af5c77..4b1c84ce64fee3e84c85292a8ae5e88af593011a 100644 (file)
@@ -55,8 +55,7 @@ fn basic_sess(sysroot: PathBuf) -> (Session, Rc<CStore>) {
 
     let descriptions = Registry::new(&rustc::DIAGNOSTICS);
     let cstore = Rc::new(CStore::new(token::get_ident_interner()));
-    let cstore_ = ::rustc_driver::cstore_to_cratestore(cstore.clone());
-    let sess = build_session(opts, None, descriptions, cstore_);
+    let sess = build_session(opts, None, descriptions, cstore.clone());
     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
     (sess, cstore)
 }
diff --git a/src/test/run-make/issue-28766/Makefile b/src/test/run-make/issue-28766/Makefile
new file mode 100644 (file)
index 0000000..1f47ef1
--- /dev/null
@@ -0,0 +1,5 @@
+-include ../tools.mk
+
+all:
+       $(RUSTC) -O foo.rs
+       $(RUSTC) -O -L $(TMPDIR) main.rs
diff --git a/src/test/run-make/issue-28766/foo.rs b/src/test/run-make/issue-28766/foo.rs
new file mode 100644 (file)
index 0000000..3ed0a6b
--- /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.
+
+#![crate_type="lib"]
+pub struct Foo(());
+
+impl Foo {
+  pub fn new() -> Foo {
+    Foo(())
+  }
+}
diff --git a/src/test/run-make/issue-28766/main.rs b/src/test/run-make/issue-28766/main.rs
new file mode 100644 (file)
index 0000000..d1dadbd
--- /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.
+
+#![crate_type="lib"]
+extern crate foo;
+use foo::Foo;
+
+pub fn crash() -> Box<Foo> {
+  Box::new(Foo::new())
+}
diff --git a/src/test/run-make/json-errors/Makefile b/src/test/run-make/json-errors/Makefile
new file mode 100644 (file)
index 0000000..2467e08
--- /dev/null
@@ -0,0 +1,8 @@
+-include ../tools.mk
+
+all:
+       cp foo.rs $(TMPDIR)
+       cd $(TMPDIR)
+       -$(RUSTC) -Z unstable-options --error-format=json foo.rs 2>foo.log
+       grep -q '{"message":"unresolved name `y`","code":{"code":"E0425","explanation":"\\nAn unresolved name was used. Example of erroneous codes.*"},"level":"error","span":{"file_name":"foo.rs","byte_start":496,"byte_end":497,"line_start":12,"line_end":12,"column_start":18,"column_end":19},"children":\[\]}' foo.log
+       grep -q '{"message":".*","code":{"code":"E0277","explanation":"\\nYou tried.*"},"level":"error","span":{.*},"children":\[{"message":"the .*","code":null,"level":"help","span":{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":0,"column_end":0},"children":\[\]},{"message":"  <u8 as core::ops::Add>","code":null,"level":"help",' foo.log
diff --git a/src/test/run-make/json-errors/foo.rs b/src/test/run-make/json-errors/foo.rs
new file mode 100644 (file)
index 0000000..4db3394
--- /dev/null
@@ -0,0 +1,15 @@
+// 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.
+
+fn main() {
+    let x = 42 + y;
+
+    42u8 + 42i32;
+}
index dc0fcec1980fd1f18ad85d728f7ef0dbcb998419..e9c974a01373282bd3aacf99d0a1f2cca5717d66 100644 (file)
@@ -1,15 +1,28 @@
 -include ../tools.mk
 
+TARGETS =
+ifeq ($(filter arm,$(LLVM_COMPONENTS)),arm)
 # construct a fairly exhaustive list of platforms that we
 # support. These ones don't follow a pattern
-TARGETS=arm-linux-androideabi arm-unknown-linux-gnueabihf arm-unknown-linux-gnueabi
+TARGETS += arm-linux-androideabi arm-unknown-linux-gnueabihf arm-unknown-linux-gnueabi
+endif
+
+ifeq ($(filter x86,$(LLVM_COMPONENTS)),x86)
+X86_ARCHS = i686 x86_64
+else
+X86_ARCHS =
+endif
 
 # these ones do, each OS lists the architectures it supports
-LINUX=aarch64 i686 x86_64 mips mipsel
-WINDOWS=i686 x86_64
+LINUX=$(filter aarch64 mips,$(LLVM_COMPONENTS)) $(X86_ARCHS)
+ifeq ($(filter mips,$(LLVM_COMPONENTS)),mips)
+LINUX += mipsel
+endif
+
+WINDOWS=$(X86_ARCHS)
 # fails with: failed to get iphonesimulator SDK path: no such file or directory
 #IOS=i386 aarch64 armv7
-DARWIN=i686 x86_64
+DARWIN=$(X86_ARCHS)
 
 $(foreach arch,$(LINUX),$(eval TARGETS += $(arch)-unknown-linux-gnu))
 $(foreach arch,$(WINDOWS),$(eval TARGETS += $(arch)-pc-windows-gnu))
index c0c4b1e7f3f312e4516c38d1e2fea8368eedf4c1..49fec6f3619e4fa9c7ed48562281a8cfaf8d50e9 100644 (file)
@@ -47,11 +47,11 @@ extern {
     fn integer(a: i32x4, b: i32x4) -> i32x4;
 
     // vmaxq_s32
-    #[cfg(any(target_arch = "arm"))]
+    #[cfg(target_arch = "arm")]
     #[link_name = "llvm.arm.neon.vmaxs.v4i32"]
     fn integer(a: i32x4, b: i32x4) -> i32x4;
     // vmaxq_s32
-    #[cfg(any(target_arch = "aarch64"))]
+    #[cfg(target_arch = "aarch64")]
     #[link_name = "llvm.aarch64.neon.maxs.v4i32"]
     fn integer(a: i32x4, b: i32x4) -> i32x4;
 
diff --git a/src/test/run-make/symbols-include-type-name/Makefile b/src/test/run-make/symbols-include-type-name/Makefile
new file mode 100644 (file)
index 0000000..1add39e
--- /dev/null
@@ -0,0 +1,9 @@
+-include ../tools.mk
+
+# Check that symbol names for methods include type names, instead of <impl>.
+
+OUT=$(TMPDIR)/lib.s
+
+all:
+       $(RUSTC) --crate-type staticlib --emit asm lib.rs
+       grep Def $(OUT)
diff --git a/src/test/run-make/symbols-include-type-name/lib.rs b/src/test/run-make/symbols-include-type-name/lib.rs
new file mode 100644 (file)
index 0000000..1c478ed
--- /dev/null
@@ -0,0 +1,19 @@
+// 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.
+
+pub struct Def {
+    pub id: i32,
+}
+
+impl Def {
+    pub fn new(id: i32) -> Def {
+        Def { id: id }
+    }
+}
index 23af568697f9fa083dd2aff87b4a0a38f618eb6a..efaf1197fbdbae82b30e2865eeb4f04af351fa14 100644 (file)
@@ -6,7 +6,7 @@ TARGET_RPATH_ENV = \
     $(LD_LIB_PATH_ENVVAR)="$(TMPDIR):$(TARGET_RPATH_DIR):$($(LD_LIB_PATH_ENVVAR))"
 
 BARE_RUSTC := $(HOST_RPATH_ENV) $(RUSTC)
-RUSTC := $(BARE_RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR)
+RUSTC := $(BARE_RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR) $(RUSTFLAGS)
 #CC := $(CC) -L $(TMPDIR)
 HTMLDOCCK := $(PYTHON) $(S)/src/etc/htmldocck.py
 
@@ -85,11 +85,7 @@ ifeq ($(UNAME),Bitrig)
 else
 ifeq ($(UNAME),OpenBSD)
        EXTRACFLAGS := -lm -lpthread
-       # extend search lib for found estdc++ if build using gcc from
-       # ports under OpenBSD. This is needed for:
-       #  - run-make/execution-engine
-       #  - run-make/issue-19371
-       RUSTC := $(RUSTC) -L/usr/local/lib
+       RUSTC := $(RUSTC) -C linker="$(word 1,$(CC:ccache=))"
 else
        EXTRACFLAGS := -lm -lrt -ldl -lpthread
        EXTRACXXFLAGS := -lstdc++
index e40abe050233331444fb50cb880ba377578d261c..b64e5778d90336ec989304a8e72110010deca8a7 100644 (file)
@@ -37,34 +37,36 @@ pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a>
                                source_str)
 }
 
-fn with_error_checking_parse<T, F>(s: String, f: F) -> PResult<T> where
-    F: FnOnce(&mut Parser) -> PResult<T>,
+fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> PResult<'a, T> where
+    F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
 {
-    let ps = ParseSess::new();
     let mut p = string_to_parser(&ps, s);
     let x = f(&mut p);
 
-    if ps.span_diagnostic.handler().has_errors() || p.token != token::Eof {
+    if ps.span_diagnostic.has_errors() || p.token != token::Eof {
+        if let Err(mut e) = x {
+            e.cancel();
+        }
         return Err(p.fatal("parse error"));
     }
 
     x
 }
 
-fn expr(s: &str) -> PResult<P<ast::Expr>> {
-    with_error_checking_parse(s.to_string(), |p| {
+fn expr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, P<ast::Expr>> {
+    with_error_checking_parse(s.to_string(), ps, |p| {
         p.parse_expr()
     })
 }
 
-fn stmt(s: &str) -> PResult<P<ast::Stmt>> {
-    with_error_checking_parse(s.to_string(), |p| {
+fn stmt<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, P<ast::Stmt>> {
+    with_error_checking_parse(s.to_string(), ps, |p| {
         p.parse_stmt().map(|s| s.unwrap())
     })
 }
 
-fn attr(s: &str) -> PResult<ast::Attribute> {
-    with_error_checking_parse(s.to_string(), |p| {
+fn attr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Attribute> {
+    with_error_checking_parse(s.to_string(), ps, |p| {
         p.parse_attribute(true)
     })
 }
@@ -79,29 +81,39 @@ fn str_compare<T, F: Fn(&T) -> String>(e: &str, expected: &[T], actual: &[T], f:
 }
 
 fn check_expr_attrs(es: &str, expected: &[&str]) {
-    let e = expr(es).expect("parse error");
+    let ps = ParseSess::new();
+    let e = expr(es, &ps).expect("parse error");
     let actual = &e.attrs;
     str_compare(es,
-                &expected.iter().map(|r| attr(r).unwrap()).collect::<Vec<_>>(),
+                &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(),
                 actual.as_attr_slice(),
                 pprust::attribute_to_string);
 }
 
 fn check_stmt_attrs(es: &str, expected: &[&str]) {
-    let e = stmt(es).expect("parse error");
+    let ps = ParseSess::new();
+    let e = stmt(es, &ps).expect("parse error");
     let actual = e.node.attrs();
     str_compare(es,
-                &expected.iter().map(|r| attr(r).unwrap()).collect::<Vec<_>>(),
+                &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(),
                 actual,
                 pprust::attribute_to_string);
 }
 
 fn reject_expr_parse(es: &str) {
-    assert!(expr(es).is_err(), "parser did not reject `{}`", es);
+    let ps = ParseSess::new();
+    match expr(es, &ps) {
+        Ok(_) => panic!("parser did not reject `{}`", es),
+        Err(mut e) => e.cancel(),
+    };
 }
 
 fn reject_stmt_parse(es: &str) {
-    assert!(stmt(es).is_err(), "parser did not reject `{}`", es);
+    let ps = ParseSess::new();
+    match stmt(es, &ps) {
+        Ok(_) => panic!("parser did not reject `{}`", es),
+        Err(mut e) => e.cancel(),
+    };
 }
 
 fn main() {
index 8850f6e6d2a6e5863db91511efd4438bba2925d5..56481dc646a9c5e35a694a4daac24cc24e532094 100644 (file)
@@ -23,7 +23,7 @@ extern crate syntax;
 use rustc::session::Session;
 use rustc::session::config::{self, Input};
 use rustc_driver::{driver, CompilerCalls, Compilation};
-use syntax::{diagnostics, diagnostic};
+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,
                       _: &diagnostics::registry::Registry,
-                      _: diagnostic::ColorConfig)
+                      _: config::ErrorOutputType)
                       -> Compilation {
         self.count *= 2;
         Compilation::Continue
index 76ecbfd2f223d2945f3c0ca41ebc2b564dbbe624..632693dd728c33a8400f8b7e5c15ac605049c3aa 100644 (file)
@@ -8,6 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-macos this needs valgrind 3.11 or higher; see
+// https://github.com/rust-lang/rust/pull/30365#issuecomment-165763679
+
 use std::env;
 use std::process::{exit, Command};
 
index 80fd548dfe3540bbd3341e63969f149d425d9ce1..d1873afb3a9b76fb842d40e61cfcd204d742cfc9 100644 (file)
@@ -22,17 +22,29 @@ fn read(ptr: &u32) -> u32 {
 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
 fn write(ptr: &mut u32, val: u32) {
     unsafe {
-        asm!("mov $1, $0" :: "=*m" (ptr), "r" (val));
+        asm!("mov $1, $0" : "=*m" (ptr) : "r" (val));
     }
 }
 
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+fn replace(ptr: &mut u32, val: u32) -> u32 {
+    let out: u32;
+    unsafe {
+        asm!("mov $0, $1; mov $2, $0" : "+*m" (ptr), "=&r" (out) : "r" (val));
+    }
+    out
+}
+
 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
 pub fn main() {
     let a = 1;
-    let mut b = 2;
     assert_eq!(read(&a), 1);
+    let mut b = 2;
     write(&mut b, 3);
     assert_eq!(b, 3);
+    let mut c = 4;
+    assert_eq!(replace(&mut c, 5), 4);
+    assert_eq!(c, 5);
 }
 
 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
diff --git a/src/test/run-pass/assoc-oddities-3.rs b/src/test/run-pass/assoc-oddities-3.rs
new file mode 100644 (file)
index 0000000..4913816
--- /dev/null
@@ -0,0 +1,21 @@
+// 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.
+
+fn that_odd_parse(c: bool, n: usize) -> u32 {
+    let x = 2;
+    let a = [1, 2, 3, 4];
+    let b = [5, 6, 7, 7];
+    x + if c { a } else { b }[n]
+}
+
+fn main() {
+    assert_eq!(4, that_odd_parse(true, 1));
+    assert_eq!(8, that_odd_parse(false, 1));
+}
diff --git a/src/test/run-pass/associated-const-type-parameters.rs b/src/test/run-pass/associated-const-type-parameters.rs
new file mode 100644 (file)
index 0000000..e3d1761
--- /dev/null
@@ -0,0 +1,41 @@
+// 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.
+
+#![feature(associated_consts)]
+
+trait Foo {
+    const X: i32;
+    fn get_x() -> i32 {
+       Self::X
+    }
+}
+
+struct Abc;
+impl Foo for Abc {
+    const X: i32 = 11;
+}
+
+struct Def;
+impl Foo for Def {
+    const X: i32 = 97;
+}
+
+fn sub<A: Foo, B: Foo>() -> i32 {
+    A::X - B::X
+}
+
+fn main() {
+    assert_eq!(11, Abc::X);
+    assert_eq!(97, Def::X);
+    assert_eq!(11, Abc::get_x());
+    assert_eq!(97, Def::get_x());
+    assert_eq!(-86, sub::<Abc, Def>());
+    assert_eq!(86, sub::<Def, Abc>());
+}
index 8bdac6808d089eb28c54d6bacca4362d0535fd8a..d85fd3a2b6b1c1bf3f87e4b4ab376e80d8eb9c31 100644 (file)
@@ -8,15 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(std_misc, binary_heap_extras, catch_panic, rand, sync_poison)]
+#![feature(recover, rand, std_panic)]
 
 use std::__rand::{thread_rng, Rng};
-use std::thread;
+use std::panic::{self, AssertRecoverSafe};
 
 use std::collections::BinaryHeap;
 use std::cmp;
-use std::sync::Arc;
-use std::sync::Mutex;
 use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
 
 static DROP_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
@@ -66,31 +64,24 @@ fn test_integrity() {
 
             // heapify the sane items
             rng.shuffle(&mut panic_ords);
-            let heap = Arc::new(Mutex::new(BinaryHeap::from_vec(panic_ords)));
+            let mut heap = BinaryHeap::from(panic_ords);
             let inner_data;
 
             {
-                let heap_ref = heap.clone();
-
-
                 // push the panicking item to the heap and catch the panic
-                let thread_result = thread::catch_panic(move || {
-                    heap.lock().unwrap().push(panic_item);
-                });
+                let thread_result = {
+                    let mut heap_ref = AssertRecoverSafe::new(&mut heap);
+                    panic::recover(move || {
+                        heap_ref.push(panic_item);
+                    })
+                };
                 assert!(thread_result.is_err());
 
                 // Assert no elements were dropped
                 let drops = DROP_COUNTER.load(Ordering::SeqCst);
-                //assert!(drops == 0, "Must not drop items. drops={}", drops);
-
-                {
-                    // now fetch the binary heap's data vector
-                    let mutex_guard = match heap_ref.lock() {
-                        Ok(x) => x,
-                        Err(poison) => poison.into_inner(),
-                    };
-                    inner_data = mutex_guard.clone().into_vec();
-                }
+                assert!(drops == 0, "Must not drop items. drops={}", drops);
+                inner_data = heap.clone().into_vec();
+                drop(heap);
             }
             let drops = DROP_COUNTER.load(Ordering::SeqCst);
             assert_eq!(drops, DATASZ);
index a9f19c12b0278e09b1d0f122028ae1f162671f7d..bb4b9cfecf7b62b609742d245f0a3d16d66dd854 100644 (file)
@@ -8,16 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(negate_unsigned)]
-
-#[cfg(any(target_arch = "x86", target_arch = "arm"))]
+#[cfg(any(target_pointer_width = "32"))]
 fn target() {
-    assert_eq!(-1000 as usize >> 3_usize, 536870787_usize);
+    assert_eq!(-1000isize as usize >> 3_usize, 536870787_usize);
 }
 
-#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
+#[cfg(any(target_pointer_width = "64"))]
 fn target() {
-    assert_eq!(-1000 as usize >> 3_usize, 2305843009213693827_usize);
+    assert_eq!(-1000isize as usize >> 3_usize, 2305843009213693827_usize);
 }
 
 fn general() {
index e51270fdc8d21f50a31a1a4db1c6f23536bd0dfc..4e4c98e50bca167c89217ec9697e82329a7a8220 100644 (file)
@@ -21,3 +21,9 @@ pub fn main() { }
 
 #[cfg(target_arch = "aarch64")]
 pub fn main() { }
+
+#[cfg(target_arch = "powerpc64")]
+pub fn main() { }
+
+#[cfg(target_arch = "powerpc64le")]
+pub fn main() { }
index 36e6e160a3bd298229c672c94f5c0aa2d751766d..eec4c940585c0f75a0e4d2e7c33bd4efb5f05440 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -8,15 +8,20 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(const_fn)]
 
 const FOO: isize = 10;
 const BAR: isize = 3;
 
+const fn foo() -> isize { 4 }
+const BOO: isize = foo();
+
 pub fn main() {
     let x: isize = 3;
     let y = match x {
         FOO => 1,
         BAR => 2,
+        BOO => 4,
         _ => 3
     };
     assert_eq!(y, 2);
index 86b41284cdf35ad35e79cc658bc4511109c5b07d..b6d84f9d5a24022dd865f999d15fd0899b38ae03 100644 (file)
@@ -15,4 +15,6 @@
 trait Chromosome<X: Chromosome<i32>> {
 }
 
+impl Chromosome<i32> for i32 { }
+
 fn main() { }
index 50bc9e971fbe587ade419de60ad3a7b6cbbdf8af..4ae5c599b43ddeb5e71b4bfc0c819c8a9010db04 100644 (file)
@@ -22,4 +22,12 @@ trait Get<A> {
 
 struct Struct<C:Chromosome> { c: C }
 
+impl Chromosome for i32 { }
+
+impl Get<Struct<i32>> for i32 {
+    fn get(&self) -> Struct<i32> {
+        Struct { c: *self }
+    }
+}
+
 fn main() { }
index 6e3e60a02e5e2fee05255c97d237e22428fcaab0..d3bdab9082e32a38a676bacb7b009e0270a646a8 100644 (file)
 
 use std::marker::PhantomData;
 
-struct DeterministicHasher;
-struct RandomHasher;
+pub struct DeterministicHasher;
+pub struct RandomHasher;
 
 
-struct MyHashMap<K, V, H=DeterministicHasher> {
+pub struct MyHashMap<K, V, H=DeterministicHasher> {
     data: PhantomData<(K, V, H)>
 }
 
diff --git a/src/test/run-pass/dst-irrefutable-bind.rs b/src/test/run-pass/dst-irrefutable-bind.rs
new file mode 100644 (file)
index 0000000..9f8067f
--- /dev/null
@@ -0,0 +1,24 @@
+// 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.
+
+struct Test<T: ?Sized>(T);
+
+fn main() {
+    let x = Test([1,2,3]);
+    let x : &Test<[i32]> = &x;
+
+    let & ref _y = x;
+
+    // Make sure binding to a fat pointer behind a reference
+    // still works
+    let slice = &[1,2,3];
+    let x = Test(&slice);
+    let Test(&_slice) = x;
+}
index 80ea1bc3a0e49d23b92016165060043766ae653f..b4ce1b97a4c00f09dae359ee38d8abbccc125d24 100644 (file)
 // Empty struct defined with braces add names into type namespace
 // Empty struct defined without braces add names into both type and value namespaces
 
+// aux-build:empty-struct.rs
+
 #![feature(braced_empty_structs)]
 
+extern crate empty_struct;
+use empty_struct::*;
+
 struct Empty1 {}
 struct Empty2;
 struct Empty3 {}
@@ -23,7 +28,7 @@ enum E {
     Empty5,
 }
 
-fn main() {
+fn local() {
     let e1: Empty1 = Empty1 {};
     let e2: Empty2 = Empty2 {};
     let e2: Empty2 = Empty2;
@@ -84,3 +89,59 @@ fn main() {
     let e22: Empty2 = Empty2 { ..e2 };
     let e33: Empty3 = Empty3 { ..e3 };
 }
+
+fn xcrate() {
+    let e1: XEmpty1 = XEmpty1 {};
+    let e2: XEmpty2 = XEmpty2 {};
+    let e2: XEmpty2 = XEmpty2;
+    let e3: XE = XE::XEmpty3 {};
+    // FIXME: Commented out tests are waiting for PR 30882 (fixes for variant namespaces)
+    // let e4: XE = XE::XEmpty4 {};
+    let e4: XE = XE::XEmpty4;
+
+    match e1 {
+        XEmpty1 {} => {}
+    }
+    match e2 {
+        XEmpty2 {} => {}
+    }
+    match e3 {
+        XE::XEmpty3 {} => {}
+        _ => {}
+    }
+    // match e4 {
+    //     XE::XEmpty4 {} => {}
+    //     _ => {}
+    // }
+
+    match e1 {
+        XEmpty1 { .. } => {}
+    }
+    match e2 {
+        XEmpty2 { .. } => {}
+    }
+    match e3 {
+        XE::XEmpty3 { .. } => {}
+        _ => {}
+    }
+    // match e4 {
+    //     XE::XEmpty4 { .. } => {}
+    //     _ => {}
+    // }
+
+    match e2 {
+        XEmpty2 => {}
+    }
+    // match e4 {
+    //     XE::XEmpty4 => {}
+    //     _ => {}
+    // }
+
+    let e11: XEmpty1 = XEmpty1 { ..e1 };
+    let e22: XEmpty2 = XEmpty2 { ..e2 };
+}
+
+fn main() {
+    local();
+    xcrate();
+}
diff --git a/src/test/run-pass/extern-vectorcall.rs b/src/test/run-pass/extern-vectorcall.rs
new file mode 100644 (file)
index 0000000..e8a9f92
--- /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.
+
+#![feature(abi_vectorcall)]
+
+trait A {
+    extern "vectorcall" fn test1(i: i32);
+}
+
+struct S;
+
+impl A for S {
+    extern "vectorcall" fn test1(i: i32) {
+        assert_eq!(i, 1);
+    }
+}
+
+extern "vectorcall" fn test2(i: i32) {
+    assert_eq!(i, 2);
+}
+
+fn main() {
+    <S as A>::test1(1);
+    test2(2);
+}
index ef69946d7aa2ad27c943bd0db86744bab4088819..a4720d48213ce43f527d0a369631d4d0b11d217a 100644 (file)
@@ -35,7 +35,7 @@ mod m {
     }
 
     #[main]
-    #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))]
+    #[cfg(not(target_arch = "x86"))]
     pub fn main() {
         unsafe {
             assert_eq!(::rusti::pref_align_of::<u64>(), 8);
index 170a6c95aa8a8f0dd204c1d34dd7968449751b80..759dc515456de1784a2421661821f348a7b4a16d 100644 (file)
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-
-#![feature(negate_unsigned)]
 #![feature(intrinsics)]
 
 mod rusti {
@@ -45,10 +43,10 @@ pub fn main() {
         assert_eq!(ctpop(100u32), 3); assert_eq!(ctpop(100i32), 3);
         assert_eq!(ctpop(100u64), 3); assert_eq!(ctpop(100i64), 3);
 
-        assert_eq!(ctpop(-1u8), 8); assert_eq!(ctpop(-1i8), 8);
-        assert_eq!(ctpop(-1u16), 16); assert_eq!(ctpop(-1i16), 16);
-        assert_eq!(ctpop(-1u32), 32); assert_eq!(ctpop(-1i32), 32);
-        assert_eq!(ctpop(-1u64), 64); assert_eq!(ctpop(-1i64), 64);
+        assert_eq!(ctpop(-1i8 as u8), 8); assert_eq!(ctpop(-1i8), 8);
+        assert_eq!(ctpop(-1i16 as u16), 16); assert_eq!(ctpop(-1i16), 16);
+        assert_eq!(ctpop(-1i32 as u32), 32); assert_eq!(ctpop(-1i32), 32);
+        assert_eq!(ctpop(-1i64 as u64), 64); assert_eq!(ctpop(-1i64), 64);
 
         assert_eq!(ctlz(0u8), 8); assert_eq!(ctlz(0i8), 8);
         assert_eq!(ctlz(0u16), 16); assert_eq!(ctlz(0i16), 16);
@@ -70,10 +68,10 @@ pub fn main() {
         assert_eq!(ctlz(100u32), 25); assert_eq!(ctlz(100i32), 25);
         assert_eq!(ctlz(100u64), 57); assert_eq!(ctlz(100i64), 57);
 
-        assert_eq!(cttz(-1u8), 0); assert_eq!(cttz(-1i8), 0);
-        assert_eq!(cttz(-1u16), 0); assert_eq!(cttz(-1i16), 0);
-        assert_eq!(cttz(-1u32), 0); assert_eq!(cttz(-1i32), 0);
-        assert_eq!(cttz(-1u64), 0); assert_eq!(cttz(-1i64), 0);
+        assert_eq!(cttz(-1i8 as u8), 0); assert_eq!(cttz(-1i8), 0);
+        assert_eq!(cttz(-1i16 as u16), 0); assert_eq!(cttz(-1i16), 0);
+        assert_eq!(cttz(-1i32 as u32), 0); assert_eq!(cttz(-1i32), 0);
+        assert_eq!(cttz(-1i64 as u64), 0); assert_eq!(cttz(-1i64), 0);
 
         assert_eq!(cttz(0u8), 8); assert_eq!(cttz(0i8), 8);
         assert_eq!(cttz(0u16), 16); assert_eq!(cttz(0i16), 16);
diff --git a/src/test/run-pass/issue-24956.rs b/src/test/run-pass/issue-24956.rs
deleted file mode 100644 (file)
index 501b713..0000000
+++ /dev/null
@@ -1,20 +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.
-
-struct Foo(bool);
-const NEW_FALSE: bool = false;
-const STATIC_FOO: Foo = Foo(NEW_FALSE);
-
-pub fn main() {
-    match (Foo(false)) {
-        STATIC_FOO => 3,
-        _ => 11
-    };
-}
diff --git a/src/test/run-pass/issue-26873-multifile.rs b/src/test/run-pass/issue-26873-multifile.rs
new file mode 100644 (file)
index 0000000..aa525ae
--- /dev/null
@@ -0,0 +1,16 @@
+// 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.
+//
+// ignore-pretty
+
+mod issue_26873_multifile;
+
+fn main() {}
+
diff --git a/src/test/run-pass/issue-26873-onefile.rs b/src/test/run-pass/issue-26873-onefile.rs
new file mode 100644 (file)
index 0000000..a9a04fd
--- /dev/null
@@ -0,0 +1,31 @@
+// 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.
+
+mod A {
+    pub mod B {
+        use super::*;
+
+        pub struct S;
+    }
+
+    pub mod C {
+        use super::*;
+        use super::B::S;
+
+        pub struct T;
+    }
+
+    pub use self::C::T;
+}
+
+use A::*;
+
+fn main() {}
+
diff --git a/src/test/run-pass/issue-27889.rs b/src/test/run-pass/issue-27889.rs
new file mode 100644 (file)
index 0000000..3f7d040
--- /dev/null
@@ -0,0 +1,31 @@
+// 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.
+
+// Test that a field can have the same name in different variants
+// of an enum
+// FIXME #27889
+
+pub enum Foo {
+    X { foo: u32 },
+    Y { foo: u32 }
+}
+
+pub fn foo(mut x: Foo) {
+    let mut y = None;
+    let mut z = None;
+    if let Foo::X { ref foo } = x {
+        z = Some(foo);
+    }
+    if let Foo::Y { ref mut foo } = x {
+        y = Some(foo);
+    }
+}
+
+fn main() {}
diff --git a/src/test/run-pass/issue-28777.rs b/src/test/run-pass/issue-28777.rs
new file mode 100644 (file)
index 0000000..ea5d4e4
--- /dev/null
@@ -0,0 +1,30 @@
+// 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.
+
+fn main() {
+    let v1 = { 1 + {2} * {3} };
+    let v2 =   1 + {2} * {3}  ;
+
+    assert_eq!(7, v1);
+    assert_eq!(7, v2);
+
+    let v3;
+    v3 = { 1 + {2} * {3} };
+    let v4;
+    v4 = 1 + {2} * {3};
+    assert_eq!(7, v3);
+    assert_eq!(7, v4);
+
+    let v5 = { 1 + {2} * 3 };
+    assert_eq!(7, v5);
+
+    let v9 = { 1 + if 1 > 2 {1} else {2} * {3} };
+    assert_eq!(7, v9);
+}
index 93d9300edf634c599b4d569b12b256c56e242daf..5587f68bd18544465c2aa5c8fb8dd7929ed233f4 100644 (file)
@@ -23,13 +23,13 @@ impl Drop for Kitty {
     fn drop(&mut self) {}
 }
 
-#[cfg(any(target_arch = "x86_64", target_arch="aarch64"))]
+#[cfg(target_pointer_width = "64")]
 pub fn main() {
     assert_eq!(mem::size_of::<Cat>(), 8 as usize);
     assert_eq!(mem::size_of::<Kitty>(), 16 as usize);
 }
 
-#[cfg(any(target_arch = "x86", target_arch = "arm"))]
+#[cfg(target_pointer_width = "32")]
 pub fn main() {
     assert_eq!(mem::size_of::<Cat>(), 4 as usize);
     assert_eq!(mem::size_of::<Kitty>(), 8 as usize);
index 658e9e14ee2e061aad929c0b090dd75d3a930b44..f70f8768766428d337074555fa2111ac679f614f 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-trait Test { type T; }
+pub trait Test { type T; }
 
 impl Test for u32 {
     type T = i32;
diff --git a/src/test/run-pass/issue-29092.rs b/src/test/run-pass/issue-29092.rs
new file mode 100644 (file)
index 0000000..c55cc91
--- /dev/null
@@ -0,0 +1,35 @@
+// 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.
+
+// Regression test for Issue #29092.
+//
+// (Possibly redundant with regression test run-pass/issue-30530.rs)
+
+use self::Term::*;
+
+#[derive(Clone)]
+pub enum Term {
+    Dummy,
+    A(Box<Term>),
+    B(Box<Term>),
+}
+
+// a small-step evaluator
+pub fn small_eval(v: Term) -> Term {
+    match v {
+        A(t) => *t.clone(),
+        B(t) => *t.clone(),
+        _ => Dummy,
+    }
+}
+
+fn main() {
+    small_eval(Dummy);
+}
diff --git a/src/test/run-pass/issue-29668.rs b/src/test/run-pass/issue-29668.rs
new file mode 100644 (file)
index 0000000..be785de
--- /dev/null
@@ -0,0 +1,25 @@
+// 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.
+
+// Functions can return unnameable types
+
+mod m1 {
+    mod m2 {
+        #[derive(Debug)]
+        pub struct A;
+    }
+    use self::m2::A;
+    pub fn x() -> A { A }
+}
+
+fn main() {
+    let x = m1::x();
+    println!("{:?}", x);
+}
diff --git a/src/test/run-pass/issue-29844.rs b/src/test/run-pass/issue-29844.rs
new file mode 100644 (file)
index 0000000..51df4d6
--- /dev/null
@@ -0,0 +1,33 @@
+// 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::sync::Arc;
+
+pub struct DescriptorSet<'a> {
+    pub slots: Vec<AttachInfo<'a, Resources>>
+}
+
+pub trait ResourcesTrait<'r>: Sized {
+    type DescriptorSet: 'r;
+}
+
+pub struct Resources;
+
+impl<'a> ResourcesTrait<'a> for Resources {
+    type DescriptorSet = DescriptorSet<'a>;
+}
+
+pub enum AttachInfo<'a, R: ResourcesTrait<'a>> {
+    NextDescriptorSet(Arc<R::DescriptorSet>)
+}
+
+fn main() {
+    let _x = DescriptorSet {slots: Vec::new()};
+}
diff --git a/src/test/run-pass/issue-30018-nopanic.rs b/src/test/run-pass/issue-30018-nopanic.rs
new file mode 100644 (file)
index 0000000..25eff9d
--- /dev/null
@@ -0,0 +1,111 @@
+// 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.
+
+// More thorough regression test for Issues #30018 and #30822. This
+// attempts to explore different ways that array element construction
+// (for both scratch arrays and non-scratch ones) interacts with
+// breaks in the control-flow, in terms of the order of evaluation of
+// the destructors (which may change; see RFC Issue 744) and the
+// number of times that the destructor evaluates for each value (which
+// should never exceed 1; this latter case is what #30822 is about).
+
+use std::cell::RefCell;
+
+struct D<'a>(&'a RefCell<Vec<i32>>, i32);
+
+impl<'a> Drop for D<'a> {
+    fn drop(&mut self) {
+        println!("Dropping D({})", self.1);
+        (self.0).borrow_mut().push(self.1);
+    }
+}
+
+fn main() {
+    println!("Start");
+    break_during_elem();
+    break_after_whole();
+    println!("Finis");
+}
+
+fn break_during_elem() {
+    let log = &RefCell::new(Vec::new());
+
+    // CASE 1: Fixed-size array itself is stored in _r slot.
+    loop {
+        let _r = [D(log, 10),
+                  D(log, 11),
+                  { D(log, 12); break; },
+                  D(log, 13)];
+    }
+    assert_eq!(&log.borrow()[..], &[12, 11, 10]);
+    log.borrow_mut().clear();
+
+    // CASE 2: Slice (borrow of array) is stored in _r slot.
+    // This is the case that is actually being reported in #30018.
+    loop {
+        let _r = &[D(log, 20),
+                   D(log, 21),
+                   { D(log, 22); break; },
+                   D(log, 23)];
+    }
+    assert_eq!(&log.borrow()[..], &[22, 21, 20]);
+    log.borrow_mut().clear();
+
+    // CASE 3: (Borrow of) slice-index of array is stored in _r slot.
+    loop {
+        let _r = &[D(log, 30),
+                  D(log, 31),
+                  { D(log, 32); break; },
+                  D(log, 33)][..];
+    }
+    assert_eq!(&log.borrow()[..], &[32, 31, 30]);
+    log.borrow_mut().clear();
+}
+
+// The purpose of these functions is to test what happens when we
+// panic after an array has been constructed in its entirety.
+//
+// It is meant to act as proof that we still need to continue
+// scheduling the destruction of an array even after we've scheduling
+// drop for its elements during construction; the latter is tested by
+// `fn break_during_elem()`.
+fn break_after_whole() {
+    let log = &RefCell::new(Vec::new());
+
+    // CASE 1: Fixed-size array itself is stored in _r slot.
+    loop {
+        let _r = [D(log, 10),
+                  D(log, 11),
+                  D(log, 12)];
+        break;
+    }
+    assert_eq!(&log.borrow()[..], &[10, 11, 12]);
+    log.borrow_mut().clear();
+
+    // CASE 2: Slice (borrow of array) is stored in _r slot.
+    loop {
+        let _r = &[D(log, 20),
+                   D(log, 21),
+                   D(log, 22)];
+        break;
+    }
+    assert_eq!(&log.borrow()[..], &[20, 21, 22]);
+    log.borrow_mut().clear();
+
+    // CASE 3: (Borrow of) slice-index of array is stored in _r slot.
+    loop {
+        let _r = &[D(log, 30),
+                   D(log, 31),
+                   D(log, 32)][..];
+        break;
+    }
+    assert_eq!(&log.borrow()[..], &[30, 31, 32]);
+    log.borrow_mut().clear();
+}
diff --git a/src/test/run-pass/issue-30018-panic.rs b/src/test/run-pass/issue-30018-panic.rs
new file mode 100644 (file)
index 0000000..da4d5f1
--- /dev/null
@@ -0,0 +1,32 @@
+// 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.
+
+// Regression test for Issue #30018. This is very similar to the
+// original reported test, except that the panic is wrapped in a
+// spawned thread to isolate the expected error result from the
+// SIGTRAP injected by the drop-flag consistency checking.
+
+struct Foo;
+
+impl Drop for Foo {
+    fn drop(&mut self) {}
+}
+
+fn foo() -> Foo {
+    panic!();
+}
+
+fn main() {
+    use std::thread;
+    let handle = thread::spawn(|| {
+        let _ = &[foo()];
+    });
+    let _ = handle.join();
+}
diff --git a/src/test/run-pass/issue-30371.rs b/src/test/run-pass/issue-30371.rs
new file mode 100644 (file)
index 0000000..a4ab475
--- /dev/null
@@ -0,0 +1,18 @@
+// 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.
+
+#![deny(unused_variables)]
+
+fn main() {
+    for _ in match return () {
+        () => Some(0),
+    } {}
+}
+
diff --git a/src/test/run-pass/issue-30490.rs b/src/test/run-pass/issue-30490.rs
new file mode 100644 (file)
index 0000000..ea603fc
--- /dev/null
@@ -0,0 +1,107 @@
+// 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.
+
+// Previously libstd would set stdio descriptors of a child process
+// by `dup`ing the requested descriptors to inherit directly into the
+// stdio descriptors. This, however, would incorrectly handle cases
+// where the descriptors to inherit were already stdio descriptors.
+// This test checks to avoid that regression.
+
+#![cfg_attr(unix, feature(libc))]
+#![cfg_attr(windows, allow(unused_imports))]
+
+#[cfg(unix)]
+extern crate libc;
+
+use std::fs::File;
+use std::io::{Read, Write};
+use std::io::{stdout, stderr};
+use std::process::{Command, Stdio};
+
+#[cfg(unix)]
+use std::os::unix::io::FromRawFd;
+
+#[cfg(not(unix))]
+fn main() {
+    // Bug not present in Windows
+}
+
+#[cfg(unix)]
+fn main() {
+    let mut args = std::env::args();
+    let name = args.next().unwrap();
+    let args: Vec<String> = args.collect();
+    if let Some("--child") = args.get(0).map(|s| &**s) {
+        return child();
+    } else if !args.is_empty() {
+        panic!("unknown options");
+    }
+
+    let stdout_backup = unsafe { libc::dup(libc::STDOUT_FILENO) };
+    let stderr_backup = unsafe { libc::dup(libc::STDERR_FILENO) };
+    assert!(stdout_backup > -1);
+    assert!(stderr_backup > -1);
+
+    let (stdout_reader, stdout_writer) = pipe();
+    let (stderr_reader, stderr_writer) = pipe();
+    assert!(unsafe { libc::dup2(stdout_writer, libc::STDOUT_FILENO) } > -1);
+    assert!(unsafe { libc::dup2(stderr_writer, libc::STDERR_FILENO) } > -1);
+
+    // Make sure we close any duplicates of the writer end of the pipe,
+    // otherwise we can get stuck reading from the pipe which has open
+    // writers but no one supplying any input
+    assert_eq!(unsafe { libc::close(stdout_writer) }, 0);
+    assert_eq!(unsafe { libc::close(stderr_writer) }, 0);
+
+    stdout().write_all("parent stdout\n".as_bytes()).expect("failed to write to stdout");
+    stderr().write_all("parent stderr\n".as_bytes()).expect("failed to write to stderr");
+
+    let child = {
+        Command::new(name)
+            .arg("--child")
+            .stdin(Stdio::inherit())
+            .stdout(unsafe { FromRawFd::from_raw_fd(libc::STDERR_FILENO) })
+            .stderr(unsafe { FromRawFd::from_raw_fd(libc::STDOUT_FILENO) })
+            .spawn()
+    };
+
+    // The Stdio passed into the Command took over (and closed) std{out, err}
+    // so we should restore them as they were.
+    assert!(unsafe { libc::dup2(stdout_backup, libc::STDOUT_FILENO) } > -1);
+    assert!(unsafe { libc::dup2(stderr_backup, libc::STDERR_FILENO) } > -1);
+
+    // Using File as a shim around the descriptor
+    let mut read = String::new();
+    let mut f: File = unsafe { FromRawFd::from_raw_fd(stdout_reader) };
+    f.read_to_string(&mut read).expect("failed to read from stdout file");
+    assert_eq!(read, "parent stdout\nchild stderr\n");
+
+    // Using File as a shim around the descriptor
+    read.clear();
+    let mut f: File = unsafe { FromRawFd::from_raw_fd(stderr_reader) };
+    f.read_to_string(&mut read).expect("failed to read from stderr file");
+    assert_eq!(read, "parent stderr\nchild stdout\n");
+
+    assert!(child.expect("failed to execute child process").wait().unwrap().success());
+}
+
+#[cfg(unix)]
+fn child() {
+    stdout().write_all("child stdout\n".as_bytes()).expect("child failed to write to stdout");
+    stderr().write_all("child stderr\n".as_bytes()).expect("child failed to write to stderr");
+}
+
+#[cfg(unix)]
+/// Returns a pipe (reader, writer combo)
+fn pipe() -> (i32, i32) {
+     let mut fds = [0; 2];
+     assert_eq!(unsafe { libc::pipe(fds.as_mut_ptr()) }, 0);
+     (fds[0], fds[1])
+}
diff --git a/src/test/run-pass/issue-30530.rs b/src/test/run-pass/issue-30530.rs
new file mode 100644 (file)
index 0000000..d5139c9
--- /dev/null
@@ -0,0 +1,35 @@
+// 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.
+
+// Regression test for Issue #30530: alloca's created for storing
+// intermediate scratch values during brace-less match arms need to be
+// initialized with their drop-flag set to "dropped" (or else we end
+// up running the destructors on garbage data at the end of the
+// function).
+
+pub enum Handler {
+    Default,
+    #[allow(dead_code)]
+    Custom(*mut Box<Fn()>),
+}
+
+fn main() {
+    take(Handler::Default, Box::new(main));
+}
+
+#[inline(never)]
+pub fn take(h: Handler, f: Box<Fn()>) -> Box<Fn()> {
+    unsafe {
+        match h {
+            Handler::Custom(ptr) => *Box::from_raw(ptr),
+            Handler::Default => f,
+        }
+    }
+}
index 8ea0804af18bf5499cf3bb4c11df8dbddb3c8c65..e0d2f13ad6899daabf21850561463fc224940094 100644 (file)
@@ -22,7 +22,7 @@ pub fn size_of_val<T>(val: &T) -> usize {
     val.size_of_val()
 }
 
-pub trait TypeInfo {
+pub trait TypeInfo: Sized {
     fn size_of(_lame_type_hint: Option<Self>) -> usize;
     fn size_of_val(&self) -> usize;
 }
diff --git a/src/test/run-pass/issue29927-1.rs b/src/test/run-pass/issue29927-1.rs
new file mode 100644 (file)
index 0000000..68271ac
--- /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.
+
+#![feature(const_fn)]
+const fn f() -> usize {
+    5
+}
+struct A {
+    field: usize,
+}
+fn main() {
+    let _ = [0; f()];
+}
diff --git a/src/test/run-pass/issue29927.rs b/src/test/run-pass/issue29927.rs
new file mode 100644 (file)
index 0000000..6d9adbc
--- /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.
+
+#![feature(const_fn)]
+struct A {
+    field: usize,
+}
+const fn f() -> usize {
+    5
+}
+fn main() {
+    let _ = [0; f()];
+}
diff --git a/src/test/run-pass/issue_26873_multifile/A/B.rs b/src/test/run-pass/issue_26873_multifile/A/B.rs
new file mode 100644 (file)
index 0000000..8917a98
--- /dev/null
@@ -0,0 +1,14 @@
+// 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 super::*;
+
+pub struct S;
+
diff --git a/src/test/run-pass/issue_26873_multifile/A/C.rs b/src/test/run-pass/issue_26873_multifile/A/C.rs
new file mode 100644 (file)
index 0000000..64aaf9c
--- /dev/null
@@ -0,0 +1,16 @@
+// 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 super::*;
+
+use super::B::S;
+
+pub struct T { i: i32 }
+
diff --git a/src/test/run-pass/issue_26873_multifile/A/mod.rs b/src/test/run-pass/issue_26873_multifile/A/mod.rs
new file mode 100644 (file)
index 0000000..a2aeb1c
--- /dev/null
@@ -0,0 +1,15 @@
+// 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.
+
+pub mod B;
+pub mod C;
+
+pub use self::C::T;
+
diff --git a/src/test/run-pass/issue_26873_multifile/mod.rs b/src/test/run-pass/issue_26873_multifile/mod.rs
new file mode 100644 (file)
index 0000000..3643b94
--- /dev/null
@@ -0,0 +1,14 @@
+// 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.
+
+mod A;
+
+use self::A::*;
+
diff --git a/src/test/run-pass/macro-follow.rs b/src/test/run-pass/macro-follow.rs
new file mode 100644 (file)
index 0000000..ce6498f
--- /dev/null
@@ -0,0 +1,190 @@
+// 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.
+
+// Check the macro follow sets (see corresponding cfail test).
+
+// FOLLOW(pat) = {FatArrow, Comma, Eq, Or, Ident(if), Ident(in)}
+macro_rules! follow_pat {
+    ($p:pat =>) => {};
+    ($p:pat ,) => {};
+    ($p:pat =) => {};
+    ($p:pat |) => {};
+    ($p:pat if) => {};
+    ($p:pat in) => {};
+}
+// FOLLOW(expr) = {FatArrow, Comma, Semicolon}
+macro_rules! follow_expr {
+    ($e:expr =>) => {};
+    ($e:expr ,) => {};
+    ($e:expr ;) => {};
+}
+// FOLLOW(ty) = {OpenDelim(Brace), Comma, FatArrow, Colon, Eq, Gt, Semi, Or,
+//               Ident(as), Ident(where), OpenDelim(Bracket)}
+macro_rules! follow_ty {
+    ($t:ty {}) => {};
+    ($t:ty ,) => {};
+    ($t:ty =>) => {};
+    ($t:ty :) => {};
+    ($t:ty =) => {};
+    ($t:ty >) => {};
+    ($t:ty ;) => {};
+    ($t:ty |) => {};
+    ($t:ty as) => {};
+    ($t:ty where) => {};
+    ($t:ty []) => {};
+}
+// FOLLOW(stmt) = FOLLOW(expr)
+macro_rules! follow_stmt {
+    ($s:stmt =>) => {};
+    ($s:stmt ,) => {};
+    ($s:stmt ;) => {};
+}
+// FOLLOW(path) = FOLLOW(ty)
+macro_rules! follow_path {
+    ($p:path {}) => {};
+    ($p:path ,) => {};
+    ($p:path =>) => {};
+    ($p:path :) => {};
+    ($p:path =) => {};
+    ($p:path >) => {};
+    ($p:path ;) => {};
+    ($p:path |) => {};
+    ($p:path as) => {};
+    ($p:path where) => {};
+    ($p:path []) => {};
+}
+// FOLLOW(block) = any token
+macro_rules! follow_block {
+    ($b:block ()) => {};
+    ($b:block []) => {};
+    ($b:block {}) => {};
+    ($b:block ,) => {};
+    ($b:block =>) => {};
+    ($b:block :) => {};
+    ($b:block =) => {};
+    ($b:block >) => {};
+    ($b:block ;) => {};
+    ($b:block |) => {};
+    ($b:block +) => {};
+    ($b:block ident) => {};
+    ($b:block $p:pat) => {};
+    ($b:block $e:expr) => {};
+    ($b:block $t:ty) => {};
+    ($b:block $s:stmt) => {};
+    ($b:block $p:path) => {};
+    ($b:block $b:block) => {};
+    ($b:block $i:ident) => {};
+    ($b:block $t:tt) => {};
+    ($b:block $i:item) => {};
+    ($b:block $m:meta) => {};
+}
+// FOLLOW(ident) = any token
+macro_rules! follow_ident {
+    ($i:ident ()) => {};
+    ($i:ident []) => {};
+    ($i:ident {}) => {};
+    ($i:ident ,) => {};
+    ($i:ident =>) => {};
+    ($i:ident :) => {};
+    ($i:ident =) => {};
+    ($i:ident >) => {};
+    ($i:ident ;) => {};
+    ($i:ident |) => {};
+    ($i:ident +) => {};
+    ($i:ident ident) => {};
+    ($i:ident $p:pat) => {};
+    ($i:ident $e:expr) => {};
+    ($i:ident $t:ty) => {};
+    ($i:ident $s:stmt) => {};
+    ($i:ident $p:path) => {};
+    ($i:ident $b:block) => {};
+    ($i:ident $i:ident) => {};
+    ($i:ident $t:tt) => {};
+    ($i:ident $i:item) => {};
+    ($i:ident $m:meta) => {};
+}
+// FOLLOW(tt) = any token
+macro_rules! follow_tt {
+    ($t:tt ()) => {};
+    ($t:tt []) => {};
+    ($t:tt {}) => {};
+    ($t:tt ,) => {};
+    ($t:tt =>) => {};
+    ($t:tt :) => {};
+    ($t:tt =) => {};
+    ($t:tt >) => {};
+    ($t:tt ;) => {};
+    ($t:tt |) => {};
+    ($t:tt +) => {};
+    ($t:tt ident) => {};
+    ($t:tt $p:pat) => {};
+    ($t:tt $e:expr) => {};
+    ($t:tt $t:ty) => {};
+    ($t:tt $s:stmt) => {};
+    ($t:tt $p:path) => {};
+    ($t:tt $b:block) => {};
+    ($t:tt $i:ident) => {};
+    ($t:tt $t:tt) => {};
+    ($t:tt $i:item) => {};
+    ($t:tt $m:meta) => {};
+}
+// FOLLOW(item) = any token
+macro_rules! follow_item {
+    ($i:item ()) => {};
+    ($i:item []) => {};
+    ($i:item {}) => {};
+    ($i:item ,) => {};
+    ($i:item =>) => {};
+    ($i:item :) => {};
+    ($i:item =) => {};
+    ($i:item >) => {};
+    ($i:item ;) => {};
+    ($i:item |) => {};
+    ($i:item +) => {};
+    ($i:item ident) => {};
+    ($i:item $p:pat) => {};
+    ($i:item $e:expr) => {};
+    ($i:item $t:ty) => {};
+    ($i:item $s:stmt) => {};
+    ($i:item $p:path) => {};
+    ($i:item $b:block) => {};
+    ($i:item $i:ident) => {};
+    ($i:item $t:tt) => {};
+    ($i:item $i:item) => {};
+    ($i:item $m:meta) => {};
+}
+// FOLLOW(meta) = any token
+macro_rules! follow_meta {
+    ($m:meta ()) => {};
+    ($m:meta []) => {};
+    ($m:meta {}) => {};
+    ($m:meta ,) => {};
+    ($m:meta =>) => {};
+    ($m:meta :) => {};
+    ($m:meta =) => {};
+    ($m:meta >) => {};
+    ($m:meta ;) => {};
+    ($m:meta |) => {};
+    ($m:meta +) => {};
+    ($m:meta ident) => {};
+    ($m:meta $p:pat) => {};
+    ($m:meta $e:expr) => {};
+    ($m:meta $t:ty) => {};
+    ($m:meta $s:stmt) => {};
+    ($m:meta $p:path) => {};
+    ($m:meta $b:block) => {};
+    ($m:meta $i:ident) => {};
+    ($m:meta $t:tt) => {};
+    ($m:meta $i:item) => {};
+    ($m:meta $m:meta) => {};
+}
+
+fn main() {}
+
index 77c6ed4447f116a174c0ada89133f1cf1a7a0d58..c1abebd5f9040caa068c2813bbe2cafdbad9470c 100644 (file)
@@ -24,7 +24,17 @@ macro_rules! pat_if {
     }}
 }
 
+macro_rules! pat_bar {
+    ($p:pat | $p2:pat) => {{
+        match Some(1u8) {
+            $p | $p2 => {},
+            _ => {}
+        }
+    }}
+}
+
 fn main() {
     pat_in!(Some(_) in 0..10);
     pat_if!(Some(x) if x > 0);
+    pat_bar!(Some(1u8) | None);
 }
diff --git a/src/test/run-pass/macro-seq-followed-by-seq.rs b/src/test/run-pass/macro-seq-followed-by-seq.rs
new file mode 100644 (file)
index 0000000..23c7d25
--- /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.
+
+// Test of allowing two sequences repetitions in a row,
+// functionality added as byproduct of RFC amendment #1384
+//   https://github.com/rust-lang/rfcs/pull/1384
+
+// Old version of Rust would reject this macro definition, even though
+// there are no local ambiguities (the initial `banana` and `orange`
+// tokens are enough for the expander to distinguish which case is
+// intended).
+macro_rules! foo {
+    ( $(banana $a:ident)* $(orange $b:tt)* ) => { };
+}
+
+fn main() {
+    foo!( banana id1 banana id2
+          orange hi  orange (hello world) );
+}
index 405a3549cf18c762522cc1638a1f188888f61fbd..c729f736115a9a2a81bcabc412ba817cd1c2233b 100644 (file)
 // type is `&mut [u8]`, passes in a pointer to the lvalue and not a
 // temporary. Issue #19147.
 
-
-#![feature(slice_bytes)]
-
 use std::slice;
+use std::cmp;
 
 trait MyWriter {
     fn my_write(&mut self, buf: &[u8]) -> Result<(), ()>;
@@ -23,7 +21,8 @@ trait MyWriter {
 
 impl<'a> MyWriter for &'a mut [u8] {
     fn my_write(&mut self, buf: &[u8]) -> Result<(), ()> {
-        slice::bytes::copy_memory(buf, *self);
+        let amt = cmp::min(self.len(), buf.len());
+        self[..amt].clone_from_slice(&buf[..amt]);
 
         let write_len = buf.len();
         unsafe {
diff --git a/src/test/run-pass/mir_adt_construction.rs b/src/test/run-pass/mir_adt_construction.rs
new file mode 100644 (file)
index 0000000..4526c40
--- /dev/null
@@ -0,0 +1,77 @@
+// 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.
+
+#![feature(rustc_attrs)]
+
+#[repr(C, u32)]
+enum CEnum {
+    Hello = 30,
+    World = 60
+}
+
+#[rustc_mir]
+fn test1(c: CEnum) -> i32 {
+  let c2 = CEnum::Hello;
+  match (c, c2) {
+    (CEnum::Hello, CEnum::Hello) => 42,
+    (CEnum::World, CEnum::Hello) => 0,
+    _ => 1
+  }
+}
+
+#[repr(packed)]
+#[derive(PartialEq, Debug)]
+struct Pakd {
+    a: u64,
+    b: u32,
+    c: u16,
+    d: u8,
+    e: ()
+}
+
+impl Drop for Pakd {
+    fn drop(&mut self) {}
+}
+
+#[rustc_mir]
+fn test2() -> Pakd {
+    Pakd { a: 42, b: 42, c: 42, d: 42, e: () }
+}
+
+#[derive(PartialEq, Debug)]
+struct TupleLike(u64, u32);
+
+#[rustc_mir]
+fn test3() -> TupleLike {
+    TupleLike(42, 42)
+}
+
+#[rustc_mir]
+fn test4(x: fn(u64, u32) -> TupleLike) -> (TupleLike, TupleLike) {
+    let y = TupleLike;
+    (x(42, 84), y(42, 84))
+}
+
+#[rustc_mir]
+fn test5(x: fn(u32) -> Option<u32>) -> (Option<u32>, Option<u32>) {
+    let y = Some;
+    (x(42), y(42))
+}
+
+fn main() {
+  assert_eq!(test1(CEnum::Hello), 42);
+  assert_eq!(test1(CEnum::World), 0);
+  assert_eq!(test2(), Pakd { a: 42, b: 42, c: 42, d: 42, e: () });
+  assert_eq!(test3(), TupleLike(42, 42));
+  let t4 = test4(TupleLike);
+  assert_eq!(t4.0, t4.1);
+  let t5 = test5(Some);
+  assert_eq!(t5.0, t5.1);
+}
diff --git a/src/test/run-pass/mir_build_match_comparisons.rs b/src/test/run-pass/mir_build_match_comparisons.rs
new file mode 100644 (file)
index 0000000..ad24e39
--- /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.
+
+#![feature(rustc_attrs)]
+
+#[rustc_mir]
+fn test1(x: i8) -> i32 {
+  match x {
+    1...10 => 0,
+    _ => 1,
+  }
+}
+
+const U: Option<i8> = Some(10);
+const S: &'static str = "hello";
+
+#[rustc_mir]
+fn test2(x: i8) -> i32 {
+  match Some(x) {
+    U => 0,
+    _ => 1,
+  }
+}
+
+#[rustc_mir]
+fn test3(x: &'static str) -> i32 {
+  match x {
+    S => 0,
+    _ => 1,
+  }
+}
+
+enum Opt<T> {
+    Some { v: T },
+    None
+}
+
+#[rustc_mir]
+fn test4(x: u64) -> i32 {
+  let opt = Opt::Some{ v: x };
+  match opt {
+    Opt::Some { v: 10 } => 0,
+    _ => 1,
+  }
+}
+
+
+fn main() {
+  assert_eq!(test1(0), 1);
+  assert_eq!(test1(1), 0);
+  assert_eq!(test1(2), 0);
+  assert_eq!(test1(5), 0);
+  assert_eq!(test1(9), 0);
+  assert_eq!(test1(10), 0);
+  assert_eq!(test1(11), 1);
+  assert_eq!(test1(20), 1);
+  assert_eq!(test2(10), 0);
+  assert_eq!(test2(0), 1);
+  assert_eq!(test2(20), 1);
+  assert_eq!(test3("hello"), 0);
+  assert_eq!(test3(""), 1);
+  assert_eq!(test3("world"), 1);
+  assert_eq!(test4(10), 0);
+  assert_eq!(test4(0), 1);
+  assert_eq!(test4(20), 1);
+}
diff --git a/src/test/run-pass/mir_constval_adts.rs b/src/test/run-pass/mir_constval_adts.rs
new file mode 100644 (file)
index 0000000..8a1f68d
--- /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.
+#![feature(rustc_attrs)]
+
+#[derive(PartialEq, Debug)]
+struct Point {
+    _x: i32,
+    _y: i32,
+}
+const STRUCT: Point = Point { _x: 42, _y: 42 };
+const TUPLE1: (i32, i32) = (42, 42);
+const TUPLE2: (&'static str, &'static str) = ("hello","world");
+
+#[rustc_mir]
+fn mir() -> (Point, (i32, i32), (&'static str, &'static str)){
+    let struct1 = STRUCT;
+    let tuple1 = TUPLE1;
+    let tuple2 = TUPLE2;
+    (struct1, tuple1, tuple2)
+}
+
+fn main(){
+    assert_eq!(mir(), (STRUCT, TUPLE1, TUPLE2));
+}
+
index 1916e608a8c79bdc2139f5ddbe95c09dc4d9e76d..9a4267bec925c88a500ddb7eb766ad8b61cc05a5 100644 (file)
@@ -49,6 +49,11 @@ fn fat_ptr_store_to<'a>(a: &'a [u8], b: &mut &'a [u8]) {
     *b = a;
 }
 
+#[rustc_mir]
+fn fat_ptr_constant() -> &'static str {
+    "HELLO"
+}
+
 fn main() {
     let a = Wrapper(4, [7,6,5]);
 
@@ -60,4 +65,6 @@ fn main() {
     let mut target : &[u8] = &[42];
     fat_ptr_store_to(p, &mut target);
     assert_eq!(target, &a.1);
+
+    assert_eq!(fat_ptr_constant(), "HELLO");
 }
diff --git a/src/test/run-pass/mir_match_arm_guard.rs b/src/test/run-pass/mir_match_arm_guard.rs
new file mode 100644 (file)
index 0000000..fb177ba
--- /dev/null
@@ -0,0 +1,28 @@
+// 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.
+
+// #30527 - We were not generating arms with guards in certain cases.
+
+#![feature(rustc_attrs)]
+
+#[rustc_mir]
+fn match_with_guard(x: Option<i8>) -> i8 {
+    match x {
+        Some(xyz) if xyz > 100 => 0,
+        Some(_) => -1,
+        None => -2
+    }
+}
+
+fn main() {
+    assert_eq!(match_with_guard(Some(111)), 0);
+    assert_eq!(match_with_guard(Some(2)), -1);
+    assert_eq!(match_with_guard(None), -2);
+}
diff --git a/src/test/run-pass/mir_misc_casts.rs b/src/test/run-pass/mir_misc_casts.rs
new file mode 100644 (file)
index 0000000..0799ffe
--- /dev/null
@@ -0,0 +1,349 @@
+// 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.
+
+#![feature(libc, rustc_attrs)]
+
+extern crate libc;
+
+fn func(){}
+
+const STR: &'static str = "hello";
+const BSTR: &'static [u8; 5] = b"hello";
+
+#[rustc_mir]
+fn from_ptr()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, *const ()) {
+    let f = 1_usize as *const libc::FILE;
+    let c1 = f as isize;
+    let c2 = f as usize;
+    let c3 = f as i8;
+    let c4 = f as i16;
+    let c5 = f as i32;
+    let c6 = f as i64;
+    let c7 = f as u8;
+    let c8 = f as u16;
+    let c9 = f as u32;
+    let c10 = f as u64;
+    let c11 = f as *const ();
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11)
+}
+
+#[rustc_mir]
+fn from_1()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) {
+    let c1 = 1 as isize;
+    let c2 = 1 as usize;
+    let c3 = 1 as i8;
+    let c4 = 1 as i16;
+    let c5 = 1 as i32;
+    let c6 = 1 as i64;
+    let c7 = 1 as u8;
+    let c8 = 1 as u16;
+    let c9 = 1 as u32;
+    let c10 = 1 as u64;
+    let c11 = 1 as f32;
+    let c12 = 1 as f64;
+    let c13 = 1 as *const libc::FILE;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13)
+}
+
+#[rustc_mir]
+fn from_1usize()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) {
+    let c1 = 1_usize as isize;
+    let c2 = 1_usize as usize;
+    let c3 = 1_usize as i8;
+    let c4 = 1_usize as i16;
+    let c5 = 1_usize as i32;
+    let c6 = 1_usize as i64;
+    let c7 = 1_usize as u8;
+    let c8 = 1_usize as u16;
+    let c9 = 1_usize as u32;
+    let c10 = 1_usize as u64;
+    let c11 = 1_usize as f32;
+    let c12 = 1_usize as f64;
+    let c13 = 1_usize as *const libc::FILE;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13)
+}
+
+#[rustc_mir]
+fn from_1isize()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) {
+    let c1 = 1_isize as isize;
+    let c2 = 1_isize as usize;
+    let c3 = 1_isize as i8;
+    let c4 = 1_isize as i16;
+    let c5 = 1_isize as i32;
+    let c6 = 1_isize as i64;
+    let c7 = 1_isize as u8;
+    let c8 = 1_isize as u16;
+    let c9 = 1_isize as u32;
+    let c10 = 1_isize as u64;
+    let c11 = 1_isize as f32;
+    let c12 = 1_isize as f64;
+    let c13 = 1_isize as *const libc::FILE;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13)
+}
+
+#[rustc_mir]
+fn from_1u8()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) {
+    let c1 = 1_u8 as isize;
+    let c2 = 1_u8 as usize;
+    let c3 = 1_u8 as i8;
+    let c4 = 1_u8 as i16;
+    let c5 = 1_u8 as i32;
+    let c6 = 1_u8 as i64;
+    let c7 = 1_u8 as u8;
+    let c8 = 1_u8 as u16;
+    let c9 = 1_u8 as u32;
+    let c10 = 1_u8 as u64;
+    let c11 = 1_u8 as f32;
+    let c12 = 1_u8 as f64;
+    let c13 = 1_u8 as *const libc::FILE;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13)
+}
+
+#[rustc_mir]
+fn from_1i8()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) {
+    let c1 = 1_i8 as isize;
+    let c2 = 1_i8 as usize;
+    let c3 = 1_i8 as i8;
+    let c4 = 1_i8 as i16;
+    let c5 = 1_i8 as i32;
+    let c6 = 1_i8 as i64;
+    let c7 = 1_i8 as u8;
+    let c8 = 1_i8 as u16;
+    let c9 = 1_i8 as u32;
+    let c10 = 1_i8 as u64;
+    let c11 = 1_i8 as f32;
+    let c12 = 1_i8 as f64;
+    let c13 = 1_i8 as *const libc::FILE;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13)
+}
+
+#[rustc_mir]
+fn from_1u16()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) {
+    let c1 = 1_u16 as isize;
+    let c2 = 1_u16 as usize;
+    let c3 = 1_u16 as i8;
+    let c4 = 1_u16 as i16;
+    let c5 = 1_u16 as i32;
+    let c6 = 1_u16 as i64;
+    let c7 = 1_u16 as u8;
+    let c8 = 1_u16 as u16;
+    let c9 = 1_u16 as u32;
+    let c10 = 1_u16 as u64;
+    let c11 = 1_u16 as f32;
+    let c12 = 1_u16 as f64;
+    let c13 = 1_u16 as *const libc::FILE;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13)
+}
+
+#[rustc_mir]
+fn from_1i16()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) {
+    let c1 = 1_i16 as isize;
+    let c2 = 1_i16 as usize;
+    let c3 = 1_i16 as i8;
+    let c4 = 1_i16 as i16;
+    let c5 = 1_i16 as i32;
+    let c6 = 1_i16 as i64;
+    let c7 = 1_i16 as u8;
+    let c8 = 1_i16 as u16;
+    let c9 = 1_i16 as u32;
+    let c10 = 1_i16 as u64;
+    let c11 = 1_i16 as f32;
+    let c12 = 1_i16 as f64;
+    let c13 = 1_i16 as *const libc::FILE;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13)
+}
+
+#[rustc_mir]
+fn from_1u32()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) {
+    let c1 = 1_u32 as isize;
+    let c2 = 1_u32 as usize;
+    let c3 = 1_u32 as i8;
+    let c4 = 1_u32 as i16;
+    let c5 = 1_u32 as i32;
+    let c6 = 1_u32 as i64;
+    let c7 = 1_u32 as u8;
+    let c8 = 1_u32 as u16;
+    let c9 = 1_u32 as u32;
+    let c10 = 1_u32 as u64;
+    let c11 = 1_u32 as f32;
+    let c12 = 1_u32 as f64;
+    let c13 = 1_u32 as *const libc::FILE;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13)
+}
+
+#[rustc_mir]
+fn from_1i32()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) {
+    let c1 = 1_i32 as isize;
+    let c2 = 1_i32 as usize;
+    let c3 = 1_i32 as i8;
+    let c4 = 1_i32 as i16;
+    let c5 = 1_i32 as i32;
+    let c6 = 1_i32 as i64;
+    let c7 = 1_i32 as u8;
+    let c8 = 1_i32 as u16;
+    let c9 = 1_i32 as u32;
+    let c10 = 1_i32 as u64;
+    let c11 = 1_i32 as f32;
+    let c12 = 1_i32 as f64;
+    let c13 = 1_i32 as *const libc::FILE;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13)
+}
+
+#[rustc_mir]
+fn from_1u64()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) {
+    let c1 = 1_u64 as isize;
+    let c2 = 1_u64 as usize;
+    let c3 = 1_u64 as i8;
+    let c4 = 1_u64 as i16;
+    let c5 = 1_u64 as i32;
+    let c6 = 1_u64 as i64;
+    let c7 = 1_u64 as u8;
+    let c8 = 1_u64 as u16;
+    let c9 = 1_u64 as u32;
+    let c10 = 1_u64 as u64;
+    let c11 = 1_u64 as f32;
+    let c12 = 1_u64 as f64;
+    let c13 = 1_u64 as *const libc::FILE;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13)
+}
+
+#[rustc_mir]
+fn from_1i64()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, *const libc::FILE) {
+    let c1 = 1_i64 as isize;
+    let c2 = 1_i64 as usize;
+    let c3 = 1_i64 as i8;
+    let c4 = 1_i64 as i16;
+    let c5 = 1_i64 as i32;
+    let c6 = 1_i64 as i64;
+    let c7 = 1_i64 as u8;
+    let c8 = 1_i64 as u16;
+    let c9 = 1_i64 as u32;
+    let c10 = 1_i64 as u64;
+    let c11 = 1_i64 as f32;
+    let c12 = 1_i64 as f64;
+    let c13 = 1_i64 as *const libc::FILE;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13)
+}
+
+#[rustc_mir]
+fn from_bool()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64) {
+    let c1 = true as isize;
+    let c2 = true as usize;
+    let c3 = true as i8;
+    let c4 = true as i16;
+    let c5 = true as i32;
+    let c6 = true as i64;
+    let c7 = true as u8;
+    let c8 = true as u16;
+    let c9 = true as u32;
+    let c10 = true as u64;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10)
+}
+
+#[rustc_mir]
+fn from_1f32()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64) {
+    let c1 = 1.0_f32 as isize;
+    let c2 = 1.0_f32 as usize;
+    let c3 = 1.0_f32 as i8;
+    let c4 = 1.0_f32 as i16;
+    let c5 = 1.0_f32 as i32;
+    let c6 = 1.0_f32 as i64;
+    let c7 = 1.0_f32 as u8;
+    let c8 = 1.0_f32 as u16;
+    let c9 = 1.0_f32 as u32;
+    let c10 = 1.0_f32 as u64;
+    let c11 = 1.0_f32 as f32;
+    let c12 = 1.0_f32 as f64;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12)
+}
+
+#[rustc_mir]
+fn from_1f64()
+-> (isize, usize, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64) {
+    let c1 = 1.0f64 as isize;
+    let c2 = 1.0f64 as usize;
+    let c3 = 1.0f64 as i8;
+    let c4 = 1.0f64 as i16;
+    let c5 = 1.0f64 as i32;
+    let c6 = 1.0f64 as i64;
+    let c7 = 1.0f64 as u8;
+    let c8 = 1.0f64 as u16;
+    let c9 = 1.0f64 as u32;
+    let c10 = 1.0f64 as u64;
+    let c11 = 1.0f64 as f32;
+    let c12 = 1.0f64 as f64;
+    (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12)
+}
+
+#[rustc_mir]
+fn other_casts()
+-> (*const u8, *const isize, *const u8, *const u8) {
+    let c1 = func as *const u8;
+    let c2 = c1 as *const isize;
+
+    let r = &42u32;
+    let _ = r as *const u32;
+
+    // fat-ptr -> fat-ptr -> fat-raw-ptr -> thin-ptr
+    let c3 = STR as &str as *const str as *const u8;
+
+    let c4 = BSTR as *const [u8] as *const [u16] as *const u8;
+    (c1, c2, c3, c4)
+}
+
+pub fn assert_eq_13(l: (isize, usize, i8, i16, i32, i64, u8,
+                        u16, u32, u64, f32, f64, *const libc::FILE),
+                    r: (isize, usize, i8, i16, i32, i64, u8,
+                        u16, u32, u64, f32, f64, *const libc::FILE)) -> bool {
+    let (l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13) = l;
+    let (r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13) = r;
+    l1 == r1 && l2 == r2 && l3 == r3 && l4 == r4 && l5 == r5 && l6 == r6 && l7 == r7 &&
+    l8 == r8 && l9 == r9 && l10 == r10 && l11 == r11 && l12 == r12 && l13 == r13
+}
+
+
+pub fn main() {
+    let f = 1_usize as *const libc::FILE;
+    let t13 = (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.0, 1.0, f);
+    let t12 = (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.0, 1.0);
+    assert_eq_13(from_1(), t13);
+    assert_eq_13(from_1usize(), t13);
+    assert_eq_13(from_1isize(), t13);
+    assert_eq_13(from_1u8(), t13);
+    assert_eq_13(from_1i8(), t13);
+    assert_eq_13(from_1u16(), t13);
+    assert_eq_13(from_1i16(), t13);
+    assert_eq_13(from_1u32(), t13);
+    assert_eq_13(from_1i32(), t13);
+    assert_eq_13(from_1u64(), t13);
+    assert_eq_13(from_1i64(), t13);
+    assert_eq!(from_1f32(), t12);
+    assert_eq!(from_1f64(), t12);
+
+    assert_eq!(from_ptr(), (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 as *const ()));
+    assert_eq!(from_bool(), (1, 1, 1, 1, 1, 1, 1, 1, 1, 1));
+
+    assert_eq!(other_casts(), (func as *const u8, func as *const isize,
+                               STR as *const str as *const u8, BSTR as *const [u8] as *const u8));
+}
diff --git a/src/test/run-pass/mir_refs_correct.rs b/src/test/run-pass/mir_refs_correct.rs
new file mode 100644 (file)
index 0000000..93953e3
--- /dev/null
@@ -0,0 +1,251 @@
+// 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.
+#![feature(rustc_attrs)]
+// aux-build:mir_external_refs.rs
+
+
+extern crate mir_external_refs as ext;
+
+struct S(u8);
+#[derive(Debug, PartialEq, Eq)]
+struct Unit;
+
+impl S {
+    fn hey() -> u8 { 42 }
+    fn hey2(&self) -> u8 { 44 }
+}
+
+trait X {
+    fn hoy(&self) -> u8 { 43 }
+    fn hoy2() -> u8 { 45 }
+}
+
+trait F<U> {
+    fn f(self, other: U) -> u64;
+}
+
+impl F<u32> for u32 {
+    fn f(self, other: u32) -> u64 { self as u64 + other as u64 }
+}
+
+impl F<u64> for u32 {
+    fn f(self, other: u64) -> u64 { self as u64 - other }
+}
+
+impl F<u64> for u64 {
+    fn f(self, other: u64) -> u64 { self * other }
+}
+
+impl F<u32> for u64 {
+    fn f(self, other: u32) -> u64 { self ^ other as u64 }
+}
+
+trait T<I, O> {
+    fn staticmeth(i: I, o: O) -> (I, O) { (i, o) }
+}
+
+impl<I, O> T<I, O> for O {}
+
+impl X for S {}
+
+enum E {
+    U(u8)
+}
+
+#[derive(PartialEq, Debug, Eq)]
+enum CEnum {
+    A = 0x321,
+    B = 0x123
+}
+
+const C: u8 = 84;
+const C2: [u8; 5] = [42; 5];
+const C3: [u8; 3] = [42, 41, 40];
+const C4: fn(u8) -> S = S;
+
+fn regular() -> u8 {
+    21
+}
+
+fn parametric<T>(u: T) -> T {
+    u
+}
+
+#[rustc_mir]
+fn t1() -> fn()->u8 {
+    regular
+}
+
+#[rustc_mir]
+fn t2() -> fn(u8)->E {
+    E::U
+}
+
+#[rustc_mir]
+fn t3() -> fn(u8)->S {
+    S
+}
+
+#[rustc_mir]
+fn t4() -> fn()->u8 {
+    S::hey
+}
+
+#[rustc_mir]
+fn t5() -> fn(&S)-> u8 {
+    <S as X>::hoy
+}
+
+
+#[rustc_mir]
+fn t6() -> fn()->u8{
+    ext::regular_fn
+}
+
+#[rustc_mir]
+fn t7() -> fn(u8)->ext::E {
+    ext::E::U
+}
+
+#[rustc_mir]
+fn t8() -> fn(u8)->ext::S {
+    ext::S
+}
+
+#[rustc_mir]
+fn t9() -> fn()->u8 {
+    ext::S::hey
+}
+
+#[rustc_mir]
+fn t10() -> fn(&ext::S)->u8 {
+    <ext::S as ext::X>::hoy
+}
+
+#[rustc_mir]
+fn t11() -> fn(u8)->u8 {
+    parametric
+}
+
+#[rustc_mir]
+fn t12() -> u8 {
+    C
+}
+
+#[rustc_mir]
+fn t13() -> [u8; 5] {
+    C2
+}
+
+#[rustc_mir]
+fn t13_2() -> [u8; 3] {
+    C3
+}
+
+#[rustc_mir]
+fn t14() -> fn()-> u8 {
+    <S as X>::hoy2
+}
+
+#[rustc_mir]
+fn t15() -> fn(&S)-> u8 {
+    S::hey2
+}
+
+#[rustc_mir]
+fn t16() -> fn(u32, u32)->u64 {
+    F::f
+}
+
+#[rustc_mir]
+fn t17() -> fn(u32, u64)->u64 {
+    F::f
+}
+
+#[rustc_mir]
+fn t18() -> fn(u64, u64)->u64 {
+    F::f
+}
+
+#[rustc_mir]
+fn t19() -> fn(u64, u32)->u64 {
+    F::f
+}
+
+#[rustc_mir]
+fn t20() -> fn(u64, u32)->(u64, u32) {
+    <u32 as T<_, _>>::staticmeth
+}
+
+#[rustc_mir]
+fn t21() -> Unit {
+    Unit
+}
+
+#[rustc_mir]
+fn t22() -> Option<u8> {
+    None
+}
+
+#[rustc_mir]
+fn t23() -> (CEnum, CEnum) {
+    (CEnum::A, CEnum::B)
+}
+
+#[rustc_mir]
+fn t24() -> fn(u8) -> S {
+    C4
+}
+
+fn main(){
+    unsafe {
+        assert_eq!(t1()(), regular());
+
+        assert!(::std::mem::transmute::<_, *mut ()>(t2()) ==
+                ::std::mem::transmute::<_, *mut ()>(E::U));
+        assert!(::std::mem::transmute::<_, *mut ()>(t3()) ==
+                ::std::mem::transmute::<_, *mut ()>(S));
+
+        assert_eq!(t4()(), S::hey());
+        let s = S(42);
+        assert_eq!(t5()(&s), <S as X>::hoy(&s));
+
+
+        assert_eq!(t6()(), ext::regular_fn());
+        assert!(::std::mem::transmute::<_, *mut ()>(t7()) ==
+                ::std::mem::transmute::<_, *mut ()>(ext::E::U));
+        assert!(::std::mem::transmute::<_, *mut ()>(t8()) ==
+                ::std::mem::transmute::<_, *mut ()>(ext::S));
+
+        assert_eq!(t9()(), ext::S::hey());
+        let sext = ext::S(6);
+        assert_eq!(t10()(&sext), <ext::S as ext::X>::hoy(&sext));
+
+        let p = parametric::<u8>;
+        assert!(::std::mem::transmute::<_, *mut ()>(t11()) ==
+                ::std::mem::transmute::<_, *mut ()>(p));
+
+        assert_eq!(t12(), C);
+        assert_eq!(t13(), C2);
+        assert_eq!(t13_2(), C3);
+
+        assert_eq!(t14()(), <S as X>::hoy2());
+        assert_eq!(t15()(&s), S::hey2(&s));
+        assert_eq!(t16()(10u32, 20u32), F::f(10u32, 20u32));
+        assert_eq!(t17()(30u32, 10u64), F::f(30u32, 10u64));
+        assert_eq!(t18()(50u64, 5u64), F::f(50u64, 5u64));
+        assert_eq!(t19()(322u64, 2u32), F::f(322u64, 2u32));
+        assert_eq!(t20()(123u64, 38u32), <u32 as T<_, _>>::staticmeth(123, 38));
+        assert_eq!(t21(), Unit);
+        assert_eq!(t22(), None);
+        assert_eq!(t23(), (CEnum::A, CEnum::B));
+        assert_eq!(t24(), C4);
+    }
+}
diff --git a/src/test/run-pass/mir_trans_call_converging.rs b/src/test/run-pass/mir_trans_call_converging.rs
new file mode 100644 (file)
index 0000000..d8acfec
--- /dev/null
@@ -0,0 +1,28 @@
+// 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.
+#![feature(rustc_attrs)]
+
+fn converging_fn() -> u64 {
+    43
+}
+
+#[rustc_mir]
+fn mir() -> u64 {
+    let x;
+    loop {
+        x = converging_fn();
+        break;
+    }
+    x
+}
+
+fn main() {
+    assert_eq!(mir(), 43);
+}
diff --git a/src/test/run-pass/mir_trans_calls.rs b/src/test/run-pass/mir_trans_calls.rs
new file mode 100644 (file)
index 0000000..8fdedb6
--- /dev/null
@@ -0,0 +1,136 @@
+// 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.
+
+#![feature(rustc_attrs)]
+
+#[rustc_mir]
+fn test1(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) {
+    // Test passing a number of arguments including a fat pointer.
+    // Also returning via an out pointer
+    fn callee(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) {
+        (a, b, c)
+    }
+    callee(a, b, c)
+}
+
+#[rustc_mir]
+fn test2(a: isize) -> isize {
+    // Test passing a single argument.
+    // Not using out pointer.
+    fn callee(a: isize) -> isize {
+        a
+    }
+    callee(a)
+}
+
+struct Foo;
+impl Foo {
+    fn inherent_method(&self, a: isize) -> isize { a }
+}
+
+#[rustc_mir]
+fn test3(x: &Foo, a: isize) -> isize {
+    // Test calling inherent method
+    x.inherent_method(a)
+}
+
+trait Bar {
+    fn extension_method(&self, a: isize) -> isize { a }
+}
+impl Bar for Foo {}
+
+#[rustc_mir]
+fn test4(x: &Foo, a: isize) -> isize {
+    // Test calling extension method
+    x.extension_method(a)
+}
+
+#[rustc_mir]
+fn test5(x: &Bar, a: isize) -> isize {
+    // Test calling method on trait object
+    x.extension_method(a)
+}
+
+#[rustc_mir]
+fn test6<T: Bar>(x: &T, a: isize) -> isize {
+    // Test calling extension method on generic callee
+    x.extension_method(a)
+}
+
+trait One<T = Self> {
+    fn one() -> T;
+}
+impl One for isize {
+    fn one() -> isize { 1 }
+}
+
+#[rustc_mir]
+fn test7() -> isize {
+    // Test calling trait static method
+    <isize as One>::one()
+}
+
+struct Two;
+impl Two {
+    fn two() -> isize { 2 }
+}
+
+#[rustc_mir]
+fn test8() -> isize {
+    // Test calling impl static method
+    Two::two()
+}
+
+extern fn simple_extern(x: u32, y: (u32, u32)) -> u32 {
+    x + y.0 * y.1
+}
+
+#[rustc_mir]
+fn test9() -> u32 {
+    simple_extern(41, (42, 43))
+}
+
+#[rustc_mir]
+fn test_closure<F>(f: &F, x: i32, y: i32) -> i32
+    where F: Fn(i32, i32) -> i32
+{
+    f(x, y)
+}
+
+#[rustc_mir]
+fn test_fn_object(f: &Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 {
+    f(x, y)
+}
+
+#[rustc_mir]
+fn test_fn_impl(f: &&Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 {
+    // This call goes through the Fn implementation for &Fn provided in
+    // core::ops::impls. It expands to a static Fn::call() that calls the
+    // Fn::call() implemenation of the object shim underneath.
+    f(x, y)
+}
+
+fn main() {
+    assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..]));
+    assert_eq!(test2(98), 98);
+    assert_eq!(test3(&Foo, 42), 42);
+    assert_eq!(test4(&Foo, 970), 970);
+    assert_eq!(test5(&Foo, 8576), 8576);
+    assert_eq!(test6(&Foo, 12367), 12367);
+    assert_eq!(test7(), 1);
+    assert_eq!(test8(), 2);
+    assert_eq!(test9(), 41 + 42 * 43);
+
+    let closure = |x: i32, y: i32| { x + y };
+    assert_eq!(test_closure(&closure, 100, 1), 101);
+    let function_object = &closure as &Fn(i32, i32) -> i32;
+    assert_eq!(test_fn_object(function_object, 100, 2), 102);
+    assert_eq!(test_fn_impl(&function_object, 100, 3), 103);
+}
diff --git a/src/test/run-pass/mir_trans_calls_variadic.rs b/src/test/run-pass/mir_trans_calls_variadic.rs
new file mode 100644 (file)
index 0000000..ff66daa
--- /dev/null
@@ -0,0 +1,32 @@
+// 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.
+
+#![feature(rustc_attrs)]
+
+#[link(name = "rust_test_helpers")]
+extern {
+    fn rust_interesting_average(_: i64, ...) -> f64;
+}
+
+#[rustc_mir]
+fn test(a: i64, b: i64, c: i64, d: i64, e: i64, f: i64) -> i64 {
+    unsafe {
+        rust_interesting_average(6, a, a as f64,
+                                    b, b as f64,
+                                    c, c as f64,
+                                    d, d as f64,
+                                    e, e as f64,
+                                    f, f as f64) as i64
+    }
+}
+
+fn main(){
+    assert_eq!(test(10, 20, 30, 40, 50, 60), 70);
+}
diff --git a/src/test/run-pass/mir_trans_switch.rs b/src/test/run-pass/mir_trans_switch.rs
new file mode 100644 (file)
index 0000000..c32d9da
--- /dev/null
@@ -0,0 +1,48 @@
+// 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.
+
+#![feature(rustc_attrs)]
+
+enum Abc {
+    A(u8),
+    B(i8),
+    C,
+    D,
+}
+
+#[rustc_mir]
+fn foo(x: Abc) -> i32 {
+    match x {
+        Abc::C => 3,
+        Abc::D => 4,
+        Abc::B(_) => 2,
+        Abc::A(_) => 1,
+    }
+}
+
+#[rustc_mir]
+fn foo2(x: Abc) -> bool {
+    match x {
+        Abc::D => true,
+        _ => false
+    }
+}
+
+fn main() {
+    assert_eq!(1, foo(Abc::A(42)));
+    assert_eq!(2, foo(Abc::B(-100)));
+    assert_eq!(3, foo(Abc::C));
+    assert_eq!(4, foo(Abc::D));
+
+    assert_eq!(false, foo2(Abc::A(1)));
+    assert_eq!(false, foo2(Abc::B(2)));
+    assert_eq!(false, foo2(Abc::C));
+    assert_eq!(true, foo2(Abc::D));
+}
diff --git a/src/test/run-pass/mir_void_return.rs b/src/test/run-pass/mir_void_return.rs
new file mode 100644 (file)
index 0000000..8b07449
--- /dev/null
@@ -0,0 +1,24 @@
+// 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.
+
+#![feature(rustc_attrs)]
+
+#[rustc_mir]
+fn mir() -> (){
+    let x = 1;
+    let mut y = 0;
+    while  y < x {
+        y += 1
+    }
+}
+
+pub fn main() {
+    mir();
+}
diff --git a/src/test/run-pass/mir_void_return_2.rs b/src/test/run-pass/mir_void_return_2.rs
new file mode 100644 (file)
index 0000000..a3ad343
--- /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.
+
+#![feature(rustc_attrs)]
+
+fn nil() {}
+
+#[rustc_mir]
+fn mir(){
+    nil()
+}
+
+pub fn main() {
+    mir();
+}
diff --git a/src/test/run-pass/num-wrapping.rs b/src/test/run-pass/num-wrapping.rs
new file mode 100644 (file)
index 0000000..33f7b97
--- /dev/null
@@ -0,0 +1,414 @@
+// 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.
+//
+// compile-flags: -C debug-assertions
+//
+// Test std::num::Wrapping<T> for {uN, iN, usize, isize}
+
+#![feature(op_assign_traits, num_bits_bytes, test)]
+
+extern crate test;
+
+use std::num::Wrapping;
+use std::ops::{
+    Add, Sub, Mul, Div, Rem, BitXor, BitOr, BitAnd,
+    AddAssign, SubAssign, MulAssign, DivAssign, RemAssign, BitXorAssign, BitOrAssign, BitAndAssign,
+    Shl, Shr, ShlAssign, ShrAssign
+};
+use std::{i8, i16, i32, i64, isize, u8, u16, u32, u64, usize};
+use test::black_box;
+
+fn main() {
+    test_ops();
+    test_op_assigns();
+    test_sh_ops();
+    test_sh_op_assigns();
+}
+
+fn test_ops() {
+    macro_rules! op_test {
+        ($op:ident ($lhs:expr, $rhs:expr) == $ans:expr) => {
+            assert_eq!(black_box(Wrapping($lhs).$op(Wrapping($rhs))), Wrapping($ans));
+            // FIXME(30524): uncomment this test when it's implemented
+            // assert_eq!(black_box(Wrapping($lhs).$op($rhs)), Wrapping($ans));
+        }
+    }
+
+    op_test!(add(i8::MAX, 1) == i8::MIN);
+    op_test!(add(i16::MAX, 1) == i16::MIN);
+    op_test!(add(i32::MAX, 1) == i32::MIN);
+    op_test!(add(i64::MAX, 1) == i64::MIN);
+    op_test!(add(isize::MAX, 1) == isize::MIN);
+
+    op_test!(add(u8::MAX, 1) == 0);
+    op_test!(add(u16::MAX, 1) == 0);
+    op_test!(add(u32::MAX, 1) == 0);
+    op_test!(add(u64::MAX, 1) == 0);
+    op_test!(add(usize::MAX, 1) == 0);
+
+
+    op_test!(sub(i8::MIN, 1) == i8::MAX);
+    op_test!(sub(i16::MIN, 1) == i16::MAX);
+    op_test!(sub(i32::MIN, 1) == i32::MAX);
+    op_test!(sub(i64::MIN, 1) == i64::MAX);
+    op_test!(sub(isize::MIN, 1) == isize::MAX);
+
+    op_test!(sub(0u8, 1) == u8::MAX);
+    op_test!(sub(0u16, 1) == u16::MAX);
+    op_test!(sub(0u32, 1) == u32::MAX);
+    op_test!(sub(0u64, 1) == u64::MAX);
+    op_test!(sub(0usize, 1) == usize::MAX);
+
+
+    op_test!(mul(i8::MAX, 2) == -2);
+    op_test!(mul(i16::MAX, 2) == -2);
+    op_test!(mul(i32::MAX, 2) == -2);
+    op_test!(mul(i64::MAX, 2) == -2);
+    op_test!(mul(isize::MAX, 2) == -2);
+
+    op_test!(mul(u8::MAX, 2) == u8::MAX - 1);
+    op_test!(mul(u16::MAX, 2) == u16::MAX - 1);
+    op_test!(mul(u32::MAX, 2) == u32::MAX - 1);
+    op_test!(mul(u64::MAX, 2) == u64::MAX - 1);
+    op_test!(mul(usize::MAX, 2) == usize::MAX - 1);
+
+
+    op_test!(div(i8::MIN, -1) == i8::MIN);
+    op_test!(div(i16::MIN, -1) == i16::MIN);
+    op_test!(div(i32::MIN, -1) == i32::MIN);
+    op_test!(div(i64::MIN, -1) == i64::MIN);
+    op_test!(div(isize::MIN, -1) == isize::MIN);
+
+
+    op_test!(rem(i8::MIN, -1) == 0);
+    op_test!(rem(i16::MIN, -1) == 0);
+    op_test!(rem(i32::MIN, -1) == 0);
+    op_test!(rem(i64::MIN, -1) == 0);
+    op_test!(rem(isize::MIN, -1) == 0);
+
+    // these are not that interesting, just testing to make sure they are implemented correctly
+    op_test!(bitxor(0b101010i8, 0b100110) == 0b001100);
+    op_test!(bitxor(0b101010i16, 0b100110) == 0b001100);
+    op_test!(bitxor(0b101010i32, 0b100110) == 0b001100);
+    op_test!(bitxor(0b101010i64, 0b100110) == 0b001100);
+    op_test!(bitxor(0b101010isize, 0b100110) == 0b001100);
+
+    op_test!(bitxor(0b101010u8, 0b100110) == 0b001100);
+    op_test!(bitxor(0b101010u16, 0b100110) == 0b001100);
+    op_test!(bitxor(0b101010u32, 0b100110) == 0b001100);
+    op_test!(bitxor(0b101010u64, 0b100110) == 0b001100);
+    op_test!(bitxor(0b101010usize, 0b100110) == 0b001100);
+
+
+    op_test!(bitor(0b101010i8, 0b100110) == 0b101110);
+    op_test!(bitor(0b101010i16, 0b100110) == 0b101110);
+    op_test!(bitor(0b101010i32, 0b100110) == 0b101110);
+    op_test!(bitor(0b101010i64, 0b100110) == 0b101110);
+    op_test!(bitor(0b101010isize, 0b100110) == 0b101110);
+
+    op_test!(bitor(0b101010u8, 0b100110) == 0b101110);
+    op_test!(bitor(0b101010u16, 0b100110) == 0b101110);
+    op_test!(bitor(0b101010u32, 0b100110) == 0b101110);
+    op_test!(bitor(0b101010u64, 0b100110) == 0b101110);
+    op_test!(bitor(0b101010usize, 0b100110) == 0b101110);
+
+
+    op_test!(bitand(0b101010i8, 0b100110) == 0b100010);
+    op_test!(bitand(0b101010i16, 0b100110) == 0b100010);
+    op_test!(bitand(0b101010i32, 0b100110) == 0b100010);
+    op_test!(bitand(0b101010i64, 0b100110) == 0b100010);
+    op_test!(bitand(0b101010isize, 0b100110) == 0b100010);
+
+    op_test!(bitand(0b101010u8, 0b100110) == 0b100010);
+    op_test!(bitand(0b101010u16, 0b100110) == 0b100010);
+    op_test!(bitand(0b101010u32, 0b100110) == 0b100010);
+    op_test!(bitand(0b101010u64, 0b100110) == 0b100010);
+    op_test!(bitand(0b101010usize, 0b100110) == 0b100010);
+}
+
+fn test_op_assigns() {
+    macro_rules! op_assign_test {
+        ($op:ident ($initial:expr, $rhs:expr) == $ans:expr) => {
+            {
+                let mut tmp = Wrapping($initial);
+                tmp = black_box(tmp);
+                tmp.$op(Wrapping($rhs));
+                assert_eq!(black_box(tmp), Wrapping($ans));
+            }
+            // FIXME(30524): Uncomment this test
+            /*
+            {
+                let mut tmp = Wrapping($initial);
+                tmp = black_box(tmp);
+                tmp.$op($rhs);
+                assert_eq!(black_box(tmp), Wrapping($ans));
+            }
+            */
+        }
+    }
+    op_assign_test!(add_assign(i8::MAX, 1) == i8::MIN);
+    op_assign_test!(add_assign(i16::MAX, 1) == i16::MIN);
+    op_assign_test!(add_assign(i32::MAX, 1) == i32::MIN);
+    op_assign_test!(add_assign(i64::MAX, 1) == i64::MIN);
+    op_assign_test!(add_assign(isize::MAX, 1) == isize::MIN);
+
+    op_assign_test!(add_assign(u8::MAX, 1) == u8::MIN);
+    op_assign_test!(add_assign(u16::MAX, 1) == u16::MIN);
+    op_assign_test!(add_assign(u32::MAX, 1) == u32::MIN);
+    op_assign_test!(add_assign(u64::MAX, 1) == u64::MIN);
+    op_assign_test!(add_assign(usize::MAX, 1) == usize::MIN);
+
+
+    op_assign_test!(sub_assign(i8::MIN, 1) == i8::MAX);
+    op_assign_test!(sub_assign(i16::MIN, 1) == i16::MAX);
+    op_assign_test!(sub_assign(i32::MIN, 1) == i32::MAX);
+    op_assign_test!(sub_assign(i64::MIN, 1) == i64::MAX);
+    op_assign_test!(sub_assign(isize::MIN, 1) == isize::MAX);
+
+    op_assign_test!(sub_assign(u8::MIN, 1) == u8::MAX);
+    op_assign_test!(sub_assign(u16::MIN, 1) == u16::MAX);
+    op_assign_test!(sub_assign(u32::MIN, 1) == u32::MAX);
+    op_assign_test!(sub_assign(u64::MIN, 1) == u64::MAX);
+    op_assign_test!(sub_assign(usize::MIN, 1) == usize::MAX);
+
+
+    op_assign_test!(mul_assign(i8::MAX, 2) == -2);
+    op_assign_test!(mul_assign(i16::MAX, 2) == -2);
+    op_assign_test!(mul_assign(i32::MAX, 2) == -2);
+    op_assign_test!(mul_assign(i64::MAX, 2) == -2);
+    op_assign_test!(mul_assign(isize::MAX, 2) == -2);
+
+    op_assign_test!(mul_assign(u8::MAX, 2) == u8::MAX - 1);
+    op_assign_test!(mul_assign(u16::MAX, 2) == u16::MAX - 1);
+    op_assign_test!(mul_assign(u32::MAX, 2) == u32::MAX - 1);
+    op_assign_test!(mul_assign(u64::MAX, 2) == u64::MAX - 1);
+    op_assign_test!(mul_assign(usize::MAX, 2) == usize::MAX - 1);
+
+
+    op_assign_test!(div_assign(i8::MIN, -1) == i8::MIN);
+    op_assign_test!(div_assign(i16::MIN, -1) == i16::MIN);
+    op_assign_test!(div_assign(i32::MIN, -1) == i32::MIN);
+    op_assign_test!(div_assign(i64::MIN, -1) == i64::MIN);
+    op_assign_test!(div_assign(isize::MIN, -1) == isize::MIN);
+
+
+    op_assign_test!(rem_assign(i8::MIN, -1) == 0);
+    op_assign_test!(rem_assign(i16::MIN, -1) == 0);
+    op_assign_test!(rem_assign(i32::MIN, -1) == 0);
+    op_assign_test!(rem_assign(i64::MIN, -1) == 0);
+    op_assign_test!(rem_assign(isize::MIN, -1) == 0);
+
+
+    // these are not that interesting, just testing to make sure they are implemented correctly
+    op_assign_test!(bitxor_assign(0b101010i8, 0b100110) == 0b001100);
+    op_assign_test!(bitxor_assign(0b101010i16, 0b100110) == 0b001100);
+    op_assign_test!(bitxor_assign(0b101010i32, 0b100110) == 0b001100);
+    op_assign_test!(bitxor_assign(0b101010i64, 0b100110) == 0b001100);
+    op_assign_test!(bitxor_assign(0b101010isize, 0b100110) == 0b001100);
+
+    op_assign_test!(bitxor_assign(0b101010u8, 0b100110) == 0b001100);
+    op_assign_test!(bitxor_assign(0b101010u16, 0b100110) == 0b001100);
+    op_assign_test!(bitxor_assign(0b101010u32, 0b100110) == 0b001100);
+    op_assign_test!(bitxor_assign(0b101010u64, 0b100110) == 0b001100);
+    op_assign_test!(bitxor_assign(0b101010usize, 0b100110) == 0b001100);
+
+
+    op_assign_test!(bitor_assign(0b101010i8, 0b100110) == 0b101110);
+    op_assign_test!(bitor_assign(0b101010i16, 0b100110) == 0b101110);
+    op_assign_test!(bitor_assign(0b101010i32, 0b100110) == 0b101110);
+    op_assign_test!(bitor_assign(0b101010i64, 0b100110) == 0b101110);
+    op_assign_test!(bitor_assign(0b101010isize, 0b100110) == 0b101110);
+
+    op_assign_test!(bitor_assign(0b101010u8, 0b100110) == 0b101110);
+    op_assign_test!(bitor_assign(0b101010u16, 0b100110) == 0b101110);
+    op_assign_test!(bitor_assign(0b101010u32, 0b100110) == 0b101110);
+    op_assign_test!(bitor_assign(0b101010u64, 0b100110) == 0b101110);
+    op_assign_test!(bitor_assign(0b101010usize, 0b100110) == 0b101110);
+
+
+    op_assign_test!(bitand_assign(0b101010i8, 0b100110) == 0b100010);
+    op_assign_test!(bitand_assign(0b101010i16, 0b100110) == 0b100010);
+    op_assign_test!(bitand_assign(0b101010i32, 0b100110) == 0b100010);
+    op_assign_test!(bitand_assign(0b101010i64, 0b100110) == 0b100010);
+    op_assign_test!(bitand_assign(0b101010isize, 0b100110) == 0b100010);
+
+    op_assign_test!(bitand_assign(0b101010u8, 0b100110) == 0b100010);
+    op_assign_test!(bitand_assign(0b101010u16, 0b100110) == 0b100010);
+    op_assign_test!(bitand_assign(0b101010u32, 0b100110) == 0b100010);
+    op_assign_test!(bitand_assign(0b101010u64, 0b100110) == 0b100010);
+    op_assign_test!(bitand_assign(0b101010usize, 0b100110) == 0b100010);
+}
+
+fn test_sh_ops() {
+    macro_rules! sh_test {
+        ($op:ident ($lhs:expr, $rhs:expr) == $ans:expr) => {
+            assert_eq!(black_box(Wrapping($lhs).$op($rhs)), Wrapping($ans));
+        }
+    }
+    // NOTE: This will break for i8 if we ever get i/u128
+    macro_rules! sh_test_all {
+        ($t:ty) => {
+            sh_test!(shl(i8::MAX, (i8::BITS + 1) as $t) == -2);
+            sh_test!(shl(i16::MAX, (i16::BITS + 1) as $t) == -2);
+            sh_test!(shl(i32::MAX, (i32::BITS + 1) as $t) == -2);
+            sh_test!(shl(i64::MAX, (i64::BITS + 1) as $t) == -2);
+            sh_test!(shl(isize::MAX, (isize::BITS + 1) as $t) == -2);
+
+            sh_test!(shl(u8::MAX, (u8::BITS + 1) as $t) == u8::MAX - 1);
+            sh_test!(shl(u16::MAX, (u16::BITS + 1) as $t) == u16::MAX - 1);
+            sh_test!(shl(u32::MAX, (u32::BITS + 1) as $t) == u32::MAX - 1);
+            sh_test!(shl(u64::MAX, (u64::BITS + 1) as $t) == u64::MAX - 1);
+            sh_test!(shl(usize::MAX, (usize::BITS + 1) as $t) == usize::MAX - 1);
+
+
+            sh_test!(shr(i8::MAX, (i8::BITS + 1) as $t) == i8::MAX / 2);
+            sh_test!(shr(i16::MAX, (i16::BITS + 1) as $t) == i16::MAX / 2);
+            sh_test!(shr(i32::MAX, (i32::BITS + 1) as $t) == i32::MAX / 2);
+            sh_test!(shr(i64::MAX, (i64::BITS + 1) as $t) == i64::MAX / 2);
+            sh_test!(shr(isize::MAX, (isize::BITS + 1) as $t) == isize::MAX / 2);
+
+            sh_test!(shr(u8::MAX, (u8::BITS + 1) as $t) == u8::MAX / 2);
+            sh_test!(shr(u16::MAX, (u16::BITS + 1) as $t) == u16::MAX / 2);
+            sh_test!(shr(u32::MAX, (u32::BITS + 1) as $t) == u32::MAX / 2);
+            sh_test!(shr(u64::MAX, (u64::BITS + 1) as $t) == u64::MAX / 2);
+            sh_test!(shr(usize::MAX, (usize::BITS + 1) as $t) == usize::MAX / 2);
+        }
+    }
+    macro_rules! sh_test_negative_all {
+        ($t:ty) => {
+            sh_test!(shr(i8::MAX, -((i8::BITS + 1) as $t)) == -2);
+            sh_test!(shr(i16::MAX, -((i16::BITS + 1) as $t)) == -2);
+            sh_test!(shr(i32::MAX, -((i32::BITS + 1) as $t)) == -2);
+            sh_test!(shr(i64::MAX, -((i64::BITS + 1) as $t)) == -2);
+            sh_test!(shr(isize::MAX, -((isize::BITS + 1) as $t)) == -2);
+
+            sh_test!(shr(u8::MAX, -((u8::BITS + 1) as $t)) == u8::MAX - 1);
+            sh_test!(shr(u16::MAX, -((u16::BITS + 1) as $t)) == u16::MAX - 1);
+            sh_test!(shr(u32::MAX, -((u32::BITS + 1) as $t)) == u32::MAX - 1);
+            sh_test!(shr(u64::MAX, -((u64::BITS + 1) as $t)) == u64::MAX - 1);
+            sh_test!(shr(usize::MAX, -((usize::BITS + 1) as $t)) == usize::MAX - 1);
+
+
+            sh_test!(shl(i8::MAX, -((i8::BITS + 1) as $t)) == i8::MAX / 2);
+            sh_test!(shl(i16::MAX, -((i16::BITS + 1) as $t)) == i16::MAX / 2);
+            sh_test!(shl(i32::MAX, -((i32::BITS + 1) as $t)) == i32::MAX / 2);
+            sh_test!(shl(i64::MAX, -((i64::BITS + 1) as $t)) == i64::MAX / 2);
+            sh_test!(shl(isize::MAX, -((isize::BITS + 1) as $t)) == isize::MAX / 2);
+
+            sh_test!(shl(u8::MAX, -((u8::BITS + 1) as $t)) == u8::MAX / 2);
+            sh_test!(shl(u16::MAX, -((u16::BITS + 1) as $t)) == u16::MAX / 2);
+            sh_test!(shl(u32::MAX, -((u32::BITS + 1) as $t)) == u32::MAX / 2);
+            sh_test!(shl(u64::MAX, -((u64::BITS + 1) as $t)) == u64::MAX / 2);
+            sh_test!(shl(usize::MAX, -((usize::BITS + 1) as $t)) == usize::MAX / 2);
+        }
+    }
+    // FIXME(#23545): Uncomment the remaining tests
+    //sh_test_all!(i8);
+    //sh_test_all!(u8);
+    //sh_test_all!(i16);
+    //sh_test_all!(u16);
+    //sh_test_all!(i32);
+    //sh_test_all!(u32);
+    //sh_test_all!(i64);
+    //sh_test_all!(u64);
+    //sh_test_all!(isize);
+    sh_test_all!(usize);
+
+    //sh_test_negative_all!(i8);
+    //sh_test_negative_all!(i16);
+    //sh_test_negative_all!(i32);
+    //sh_test_negative_all!(i64);
+    //sh_test_negative_all!(isize);
+}
+
+fn test_sh_op_assigns() {
+    macro_rules! sh_assign_test {
+        ($op:ident ($initial:expr, $rhs:expr) == $ans:expr) => {{
+            let mut tmp = Wrapping($initial);
+            tmp = black_box(tmp);
+            tmp.$op($rhs);
+            assert_eq!(black_box(tmp), Wrapping($ans));
+        }}
+    }
+    macro_rules! sh_assign_test_all {
+        ($t:ty) => {
+            sh_assign_test!(shl_assign(i8::MAX, (i8::BITS + 1) as $t) == -2);
+            sh_assign_test!(shl_assign(i16::MAX, (i16::BITS + 1) as $t) == -2);
+            sh_assign_test!(shl_assign(i32::MAX, (i32::BITS + 1) as $t) == -2);
+            sh_assign_test!(shl_assign(i64::MAX, (i64::BITS + 1) as $t) == -2);
+            sh_assign_test!(shl_assign(isize::MAX, (isize::BITS + 1) as $t) == -2);
+
+            sh_assign_test!(shl_assign(u8::MAX, (u8::BITS + 1) as $t) == u8::MAX - 1);
+            sh_assign_test!(shl_assign(u16::MAX, (u16::BITS + 1) as $t) == u16::MAX - 1);
+            sh_assign_test!(shl_assign(u32::MAX, (u32::BITS + 1) as $t) == u32::MAX - 1);
+            sh_assign_test!(shl_assign(u64::MAX, (u64::BITS + 1) as $t) == u64::MAX - 1);
+            sh_assign_test!(shl_assign(usize::MAX, (usize::BITS + 1) as $t) == usize::MAX - 1);
+
+
+            sh_assign_test!(shr_assign(i8::MAX, (i8::BITS + 1) as $t) == i8::MAX / 2);
+            sh_assign_test!(shr_assign(i16::MAX, (i16::BITS + 1) as $t) == i16::MAX / 2);
+            sh_assign_test!(shr_assign(i32::MAX, (i32::BITS + 1) as $t) == i32::MAX / 2);
+            sh_assign_test!(shr_assign(i64::MAX, (i64::BITS + 1) as $t) == i64::MAX / 2);
+            sh_assign_test!(shr_assign(isize::MAX, (isize::BITS + 1) as $t) == isize::MAX / 2);
+
+            sh_assign_test!(shr_assign(u8::MAX, (u8::BITS + 1) as $t) == u8::MAX / 2);
+            sh_assign_test!(shr_assign(u16::MAX, (u16::BITS + 1) as $t) == u16::MAX / 2);
+            sh_assign_test!(shr_assign(u32::MAX, (u32::BITS + 1) as $t) == u32::MAX / 2);
+            sh_assign_test!(shr_assign(u64::MAX, (u64::BITS + 1) as $t) == u64::MAX / 2);
+            sh_assign_test!(shr_assign(usize::MAX, (usize::BITS + 1) as $t) == usize::MAX / 2);
+        }
+    }
+    macro_rules! sh_assign_test_negative_all {
+        ($t:ty) => {
+            sh_assign_test!(shr_assign(i8::MAX, -((i8::BITS + 1) as $t)) == -2);
+            sh_assign_test!(shr_assign(i16::MAX, -((i16::BITS + 1) as $t)) == -2);
+            sh_assign_test!(shr_assign(i32::MAX, -((i32::BITS + 1) as $t)) == -2);
+            sh_assign_test!(shr_assign(i64::MAX, -((i64::BITS + 1) as $t)) == -2);
+            sh_assign_test!(shr_assign(isize::MAX, -((isize::BITS + 1) as $t)) == -2);
+
+            sh_assign_test!(shr_assign(u8::MAX, -((u8::BITS + 1) as $t)) == u8::MAX - 1);
+            sh_assign_test!(shr_assign(u16::MAX, -((u16::BITS + 1) as $t)) == u16::MAX - 1);
+            sh_assign_test!(shr_assign(u32::MAX, -((u32::BITS + 1) as $t)) == u32::MAX - 1);
+            sh_assign_test!(shr_assign(u64::MAX, -((u64::BITS + 1) as $t)) == u64::MAX - 1);
+            sh_assign_test!(shr_assign(usize::MAX, -((usize::BITS + 1) as $t)) == usize::MAX - 1);
+
+
+            sh_assign_test!(shl_assign(i8::MAX, -((i8::BITS + 1) as $t)) == i8::MAX / 2);
+            sh_assign_test!(shl_assign(i16::MAX, -((i16::BITS + 1) as $t)) == i16::MAX / 2);
+            sh_assign_test!(shl_assign(i32::MAX, -((i32::BITS + 1) as $t)) == i32::MAX / 2);
+            sh_assign_test!(shl_assign(i64::MAX, -((i64::BITS + 1) as $t)) == i64::MAX / 2);
+            sh_assign_test!(shl_assign(isize::MAX, -((isize::BITS + 1) as $t)) == isize::MAX / 2);
+
+            sh_assign_test!(shl_assign(u8::MAX, -((u8::BITS + 1) as $t)) == u8::MAX / 2);
+            sh_assign_test!(shl_assign(u16::MAX, -((u16::BITS + 1) as $t)) == u16::MAX / 2);
+            sh_assign_test!(shl_assign(u32::MAX, -((u32::BITS + 1) as $t)) == u32::MAX / 2);
+            sh_assign_test!(shl_assign(u64::MAX, -((u64::BITS + 1) as $t)) == u64::MAX / 2);
+            sh_assign_test!(shl_assign(usize::MAX, -((usize::BITS + 1) as $t)) == usize::MAX / 2);
+        }
+    }
+
+    // FIXME(#23545): Uncomment the remaining tests
+    //sh_assign_test_all!(i8);
+    //sh_assign_test_all!(u8);
+    //sh_assign_test_all!(i16);
+    //sh_assign_test_all!(u16);
+    //sh_assign_test_all!(i32);
+    //sh_assign_test_all!(u32);
+    //sh_assign_test_all!(i64);
+    //sh_assign_test_all!(u64);
+    //sh_assign_test_all!(isize);
+    sh_assign_test_all!(usize);
+
+    //sh_assign_test_negative_all!(i8);
+    //sh_assign_test_negative_all!(i16);
+    //sh_assign_test_negative_all!(i32);
+    //sh_assign_test_negative_all!(i64);
+    //sh_assign_test_negative_all!(isize);
+}
diff --git a/src/test/run-pass/panic-handler-chain.rs b/src/test/run-pass/panic-handler-chain.rs
new file mode 100644 (file)
index 0000000..1ed592d
--- /dev/null
@@ -0,0 +1,33 @@
+// 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.
+#![feature(panic_handler, const_fn, std_panic)]
+
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::panic;
+use std::thread;
+
+static A: AtomicUsize = AtomicUsize::new(0);
+static B: AtomicUsize = AtomicUsize::new(0);
+
+fn main() {
+    panic::set_handler(|_| { A.fetch_add(1, Ordering::SeqCst); });
+    let handler = panic::take_handler();
+    panic::set_handler(move |info| {
+        B.fetch_add(1, Ordering::SeqCst);
+        handler(info);
+    });
+
+    let _ = thread::spawn(|| {
+        panic!();
+    }).join();
+
+    assert_eq!(1, A.load(Ordering::SeqCst));
+    assert_eq!(1, B.load(Ordering::SeqCst));
+}
diff --git a/src/test/run-pass/panic-handler-flail-wildly.rs b/src/test/run-pass/panic-handler-flail-wildly.rs
new file mode 100644 (file)
index 0000000..783a44b
--- /dev/null
@@ -0,0 +1,57 @@
+// 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.
+#![feature(panic_handler, std_panic)]
+
+use std::panic;
+use std::thread;
+
+fn a() {
+    panic::set_handler(|_| println!("hello yes this is a"));
+    panic::take_handler();
+    panic::set_handler(|_| println!("hello yes this is a part 2"));
+    panic::take_handler();
+}
+
+fn b() {
+    panic::take_handler();
+    panic::take_handler();
+    panic::take_handler();
+    panic::take_handler();
+    panic::take_handler();
+    panic!();
+}
+
+fn c() {
+    panic::set_handler(|_| ());
+    panic::set_handler(|_| ());
+    panic::set_handler(|_| ());
+    panic::set_handler(|_| ());
+    panic::set_handler(|_| ());
+    panic::set_handler(|_| ());
+    panic!();
+}
+
+fn main() {
+    for _ in 0..10 {
+        let mut handles = vec![];
+        for _ in 0..10 {
+            handles.push(thread::spawn(a));
+        }
+        for _ in 0..10 {
+            handles.push(thread::spawn(b));
+        }
+        for _ in 0..10 {
+            handles.push(thread::spawn(c));
+        }
+        for handle in handles {
+            let _ = handle.join();
+        }
+    }
+}
diff --git a/src/test/run-pass/panic-handler-set-twice.rs b/src/test/run-pass/panic-handler-set-twice.rs
new file mode 100644 (file)
index 0000000..edf65e8
--- /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.
+#![feature(panic_handler, const_fn, std_panic)]
+
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::panic;
+use std::thread;
+
+static A: AtomicUsize = AtomicUsize::new(0);
+
+fn main() {
+    panic::set_handler(|_| ());
+    panic::set_handler(|info| { A.fetch_add(1, Ordering::SeqCst); });
+
+    let _ = thread::spawn(|| {
+        panic!();
+    }).join();
+
+    assert_eq!(1, A.load(Ordering::SeqCst));
+}
diff --git a/src/test/run-pass/panic-recover-propagate.rs b/src/test/run-pass/panic-recover-propagate.rs
new file mode 100644 (file)
index 0000000..c0b2f25
--- /dev/null
@@ -0,0 +1,35 @@
+// 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.
+
+#![feature(std_panic, recover, panic_propagate, panic_handler, const_fn)]
+
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::panic;
+use std::thread;
+
+static A: AtomicUsize = AtomicUsize::new(0);
+
+fn main() {
+    panic::set_handler(|_| {
+        A.fetch_add(1, Ordering::SeqCst);
+    });
+
+    let result = thread::spawn(|| {
+        let result = panic::recover(|| {
+            panic!("hi there");
+        });
+
+        panic::propagate(result.err().unwrap());
+    }).join();
+
+    let msg = *result.err().unwrap().downcast::<&'static str>().unwrap();
+    assert_eq!("hi there", msg);
+    assert_eq!(1, A.load(Ordering::SeqCst));
+}
diff --git a/src/test/run-pass/panic-safe.rs b/src/test/run-pass/panic-safe.rs
new file mode 100644 (file)
index 0000000..9949b79
--- /dev/null
@@ -0,0 +1,55 @@
+// 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.
+
+#![allow(dead_code)]
+#![feature(recover)]
+
+use std::panic::{RecoverSafe, AssertRecoverSafe};
+use std::cell::RefCell;
+use std::sync::{Mutex, RwLock, Arc};
+use std::rc::Rc;
+
+struct Foo { a: i32 }
+
+fn assert<T: RecoverSafe + ?Sized>() {}
+
+fn main() {
+    assert::<i32>();
+    assert::<&i32>();
+    assert::<*mut i32>();
+    assert::<*const i32>();
+    assert::<usize>();
+    assert::<str>();
+    assert::<&str>();
+    assert::<Foo>();
+    assert::<&Foo>();
+    assert::<Vec<i32>>();
+    assert::<String>();
+    assert::<RefCell<i32>>();
+    assert::<Box<i32>>();
+    assert::<Mutex<i32>>();
+    assert::<RwLock<i32>>();
+    assert::<Rc<i32>>();
+    assert::<Arc<i32>>();
+
+    fn bar<T>() {
+        assert::<Mutex<T>>();
+        assert::<RwLock<T>>();
+    }
+    fn baz<T: RecoverSafe>() {
+        assert::<Box<T>>();
+        assert::<Vec<T>>();
+        assert::<RefCell<T>>();
+        assert::<AssertRecoverSafe<T>>();
+        assert::<&AssertRecoverSafe<T>>();
+        assert::<Rc<AssertRecoverSafe<T>>>();
+        assert::<Arc<AssertRecoverSafe<T>>>();
+    }
+}
index e5d76c3e67abd5f6c76dfde5afc00b35f04f6863..4a115c737da3a5752e82873a5c473475551dc274 100644 (file)
@@ -35,14 +35,6 @@ struct Outer {
     t: Inner
 }
 
-
-#[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "aarch64"))]
-mod m {
-    pub fn align() -> usize { 4 }
-    pub fn size() -> usize { 8 }
-}
-
-#[cfg(target_arch = "x86_64")]
 mod m {
     pub fn align() -> usize { 4 }
     pub fn size() -> usize { 8 }
index fc032aa3ff0cdfd45fa9642c7727d8e8e8b40b09..25cd77845ea03bf9ef8389ce4265614b549ecad6 100644 (file)
@@ -49,7 +49,7 @@ mod m {
         pub fn size() -> usize { 12 }
     }
 
-    #[cfg(any(target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64"))]
+    #[cfg(not(target_arch = "x86"))]
     pub mod m {
         pub fn align() -> usize { 8 }
         pub fn size() -> usize { 16 }
index 16236f94655948c0b1c3a62900573685f0bac694..b6815d929678a6bd0c7cd1b73fde3cf26429df1b 100644 (file)
@@ -17,7 +17,7 @@
 
 use std::ops::Deref;
 
-pub trait ToOwned {
+pub trait ToOwned: Sized {
     type Owned: Borrow<Self>;
     fn to_owned(&self) -> Self::Owned;
 }
index d385804da5790c281113b4df2ab4cea01111269e..c339be25f8bb3fb35f2114765709b26ca4db23ed 100644 (file)
@@ -22,7 +22,7 @@ trait UseLife01 {
 }
 
 trait UseLife02 {
-    fn refs<'a, T, H: HasType<&'a T>>(&'a self) -> H;
+    fn refs<'a, T: 'a, H: HasType<&'a T>>(&'a self) -> H;
 }
 
 
@@ -33,7 +33,7 @@ pub trait HasType<T>
 
 
 trait UseLife03<T> {
-    fn refs<'a, H: HasType<&'a T>>(&'a self) -> H;
+    fn refs<'a, H: HasType<&'a T>>(&'a self) -> H where T: 'a;
 }
 
 
@@ -45,7 +45,7 @@ pub fn top_refs_1<'a, H: HasLife<'a>>(_s: &'a ()) -> H {
     unimplemented!()
 }
 
-pub fn top_refs_2<'a, T, H: HasType<&'a T>>(_s: &'a ()) -> H {
+pub fn top_refs_2<'a, T: 'a, H: HasType<&'a T>>(_s: &'a ()) -> H {
     unimplemented!()
 }
 
index 2b2dcb6efb5d46af99a7c659d87b69e2e2085f56..c67bc8c8368e8738191e24ed30e0fac935892095 100644 (file)
@@ -8,11 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(catch_panic, start)]
+#![feature(std_panic, recover, start)]
 
 use std::ffi::CStr;
 use std::process::{Command, Output};
-use std::thread;
+use std::panic;
 use std::str;
 
 #[start]
@@ -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!(thread::catch_panic(|| {}).is_ok()),
-                '4' => assert!(thread::catch_panic(|| panic!()).is_err()),
+                '3' => assert!(panic::recover(|| {}).is_ok()),
+                '4' => assert!(panic::recover(|| panic!()).is_err()),
                 '5' => assert!(Command::new("test").spawn().is_err()),
                 _ => panic!()
             }
diff --git a/src/test/run-pass/shadowed-use-visibility.rs b/src/test/run-pass/shadowed-use-visibility.rs
new file mode 100644 (file)
index 0000000..d2a32da
--- /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.
+
+mod foo {
+    pub fn f() {}
+
+    pub use self::f as bar;
+    use foo as bar;
+}
+
+fn main() {
+    foo::bar();
+}
diff --git a/src/test/run-pass/simd-issue-10604.rs b/src/test/run-pass/simd-issue-10604.rs
deleted file mode 100644 (file)
index c3eef0f..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2013-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.
-
-// pretty-expanded FIXME #23616
-
-#![feature(core_simd)]
-
-pub fn main() {
-    let _o = None::<std::simd::i32x4>;
-}
index 109287a83b16a88e93ced69fd83047aac5ad4188..6f23263790cb777ff0c0f8e4c56122f85598a467 100644 (file)
@@ -43,7 +43,7 @@ fn test1() {
     }
 }
 
-#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
+#[cfg(target_pointer_width = "64")]
 fn test2() {
     unsafe {
         let f = Floats { a: 1.234567890e-15_f64,
@@ -59,7 +59,7 @@ fn test2() {
     }
 }
 
-#[cfg(any(target_arch = "x86", target_arch = "arm"))]
+#[cfg(target_pointer_width = "32")]
 fn test2() {
 }
 
index c4d070f8bfed2c49b3ae8816eb8961862c39f064..931789948159d79cead4c66e0c8c859d6e563dff 100644 (file)
 #![feature(iter_empty)]
 #![feature(iter_once)]
 #![feature(iter_unfold)]
-#![feature(range_inclusive)]
 #![feature(step_by)]
 #![feature(str_escape)]
 
-use std::iter::{empty, once, range_inclusive, repeat};
+use std::iter::{empty, once, repeat};
 
 fn is_sync<T>(_: T) where T: Sync {}
 fn is_send<T>(_: T) where T: Send {}
@@ -98,7 +97,6 @@ fn main() {
                    inspect(|_| ()));
 
     is_sync_send!((1..).step_by(2));
-    is_sync_send!(range_inclusive(1, 1));
     is_sync_send!((1..2).step_by(2));
     is_sync_send!((1..2));
     is_sync_send!((1..));
diff --git a/src/test/run-pass/tls-init-on-init.rs b/src/test/run-pass/tls-init-on-init.rs
new file mode 100644 (file)
index 0000000..195b814
--- /dev/null
@@ -0,0 +1,51 @@
+// 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.
+
+#![feature(thread_local_state)]
+
+use std::thread::{self, LocalKeyState};
+use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
+
+struct Foo { cnt: usize }
+
+thread_local!(static FOO: Foo = Foo::init());
+
+static CNT: AtomicUsize = ATOMIC_USIZE_INIT;
+
+impl Foo {
+    fn init() -> Foo {
+        let cnt = CNT.fetch_add(1, Ordering::SeqCst);
+        if cnt == 0 {
+            FOO.with(|_| {});
+        }
+        Foo { cnt: cnt }
+    }
+}
+
+impl Drop for Foo {
+    fn drop(&mut self) {
+        if self.cnt == 1 {
+            FOO.with(|foo| assert_eq!(foo.cnt, 0));
+        } else {
+            assert_eq!(self.cnt, 0);
+            match FOO.state() {
+                LocalKeyState::Valid => panic!("should not be in valid state"),
+                LocalKeyState::Uninitialized |
+                LocalKeyState::Destroyed => {}
+            }
+        }
+    }
+}
+
+fn main() {
+    thread::spawn(|| {
+        FOO.with(|_| {});
+    }).join().unwrap();
+}
index 83c2a9ad33926281c369c1729ded5099dc684254..70515a088e2de597ba2589d16806f43c702671c2 100644 (file)
@@ -16,7 +16,7 @@
 
 use std::cmp::PartialOrd;
 
-pub trait NumCast {
+pub trait NumCast: Sized {
     fn from(i: i32) -> Option<Self>;
 }
 
index 14a6a9a0c664ce1fd1c5f61526db25c0ced5e6b3..e21abdae730bcc433c80488a44779fcbd5127bb5 100644 (file)
@@ -10,7 +10,7 @@
 
 // pretty-expanded FIXME #23616
 
-pub trait NumCast {
+pub trait NumCast: Sized {
     fn from(i: i32) -> Option<Self>;
 }
 
index abf8d2baf8732154115bbe5e674c73c4e22bf512..fb56ae82b303ee7fdc3ef20702e93ec89230d8cc 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub trait NumCast {
+pub trait NumCast: Sized {
     fn from(i: i32) -> Option<Self>;
 }
 
index c6f8a5d4f1d99f98bda8a384088d0f6123528326..bd442629243ee03e8e02a70052f6e719454dfc25 100644 (file)
@@ -10,7 +10,7 @@
 
 // pretty-expanded FIXME #23616
 
-pub trait NumCast {
+pub trait NumCast: Sized {
     fn from(i: i32) -> Option<Self>;
 }
 
index c7e206cb474b86d81d0dbf3f66484972faf09b26..e353be16b45e515e011b7862609277e895a90692 100644 (file)
@@ -17,7 +17,7 @@ pub trait FuzzyEq<Eps> {
     fn fuzzy_eq_eps(&self, other: &Self, epsilon: &Eps) -> bool;
 }
 
-trait Float: FuzzyEq<Self> {
+trait Float: Sized+FuzzyEq<Self> {
     fn two_pi() -> Self;
 }
 
index 7d975da4a2491b204648192c6ab5f1f76b27bf1f..a025be5d651bc1f414b27504cc74c734c8722734 100644 (file)
@@ -12,7 +12,7 @@ trait Foo<T> {
     fn f(&self, x: &T);
 }
 
-trait Bar : Foo<Self> {
+trait Bar : Sized + Foo<Self> {
     fn g(&self);
 }
 
index 3d82ee67925043e03376c4dae4c1043a63150ac8..73bb4bacf64e1b5b0ba18e6874b83db5c6b53c5b 100644 (file)
@@ -13,7 +13,7 @@ pub trait Add<RHS,Result> {
     fn add(&self, rhs: &RHS) -> Result;
 }
 
-trait MyNum : Add<Self,Self> { }
+trait MyNum : Sized + Add<Self,Self> { }
 
 struct MyInt { val: isize }
 
index 6a3639954350fb44743d43d0f2820f444e5be2f9..7d3ebc19e8f833663a12b99db5f71726080e943d 100644 (file)
@@ -17,7 +17,7 @@ trait Add<RHS,Result>: Panda<RHS> {
     fn add(&self, rhs: &RHS) -> Result;
 }
 
-trait MyNum : Add<Self,Self> { }
+trait MyNum : Sized + Add<Self,Self> { }
 
 struct MyInt { val: isize }
 
diff --git a/src/test/run-pass/type-ascription.rs b/src/test/run-pass/type-ascription.rs
new file mode 100644 (file)
index 0000000..bca384c
--- /dev/null
@@ -0,0 +1,45 @@
+// 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.
+
+// Type ascription doesn't lead to unsoundness
+
+#![feature(type_ascription)]
+
+use std::mem;
+
+const C1: u8 = 10: u8;
+const C2: [u8; 1: usize] = [1];
+
+struct S {
+    a: u8
+}
+
+fn main() {
+    assert_eq!(C1.into(): i32, 10);
+    assert_eq!(C2[0], 1);
+
+    let s = S { a: 10: u8 };
+    let arr = &[1u8, 2, 3];
+
+    let mut v = arr.iter().cloned().collect(): Vec<_>;
+    v.push(4);
+    assert_eq!(v, [1, 2, 3, 4]);
+
+    let a = 1: u8;
+    let b = a.into(): u16;
+    assert_eq!(v[a.into(): usize], 2);
+    assert_eq!(mem::size_of_val(&a), 1);
+    assert_eq!(mem::size_of_val(&b), 2);
+    assert_eq!(b, 1: u16);
+
+    let mut v = Vec::new();
+    v: Vec<u8> = vec![1, 2, 3]; // Lvalue type ascription
+    assert_eq!(v, [1u8, 2, 3]);
+}
index fdb70fe248eff1bc456ebbf60557582488d985b0..cff260c3ba63e01f9cafd8dbeffb53d5fe6122f8 100644 (file)
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(negate_unsigned)]
-
 pub fn main() {
     let a = 1;
     let a_neg: i8 = -a;
@@ -30,26 +28,4 @@ pub fn main() {
     let e = 1;
     let e_neg: isize = -e;
     println!("{}", e_neg);
-
-    // intentional overflows
-
-    let f = 1;
-    let f_neg: u8 = -f;
-    println!("{}", f_neg);
-
-    let g = 1;
-    let g_neg: u16 = -g;
-    println!("{}", g_neg);
-
-    let h = 1;
-    let h_neg: u32 = -h;
-    println!("{}", h_neg);
-
-    let i = 1;
-    let i_neg: u64 = -i;
-    println!("{}", i_neg);
-
-    let j = 1;
-    let j_neg: usize = -j;
-    println!("{}", j_neg);
 }
index 1cce98ae6b7258ed3efa814a353b6ff53125dd35..5b9fa5230d1e0289f1df1aeb24b8562a3811d444 100644 (file)
@@ -67,26 +67,26 @@ fn f7<X: ?Sized+T3>(x: &X) {
 
 trait T4<X> {
     fn dummy(&self) { }
-    fn m1(x: &T4<X>, y: X);
-    fn m2(x: &T5<X>, y: X);
+    fn m1(&self, x: &T4<X>, y: X);
+    fn m2(&self, x: &T5<X>, y: X);
 }
 trait T5<X: ?Sized> {
     fn dummy(&self) { }
     // not an error (for now)
-    fn m1(x: &T4<X>);
-    fn m2(x: &T5<X>);
+    fn m1(&self, x: &T4<X>);
+    fn m2(&self, x: &T5<X>);
 }
 
 trait T6<X: T> {
     fn dummy(&self) { }
-    fn m1(x: &T4<X>);
-    fn m2(x: &T5<X>);
+    fn m1(&self, x: &T4<X>);
+    fn m2(&self, x: &T5<X>);
 }
 trait T7<X: ?Sized+T> {
     fn dummy(&self) { }
     // not an error (for now)
-    fn m1(x: &T4<X>);
-    fn m2(x: &T5<X>);
+    fn m1(&self, x: &T4<X>);
+    fn m2(&self, x: &T5<X>);
 }
 
 // The last field in a struct or variant may be unsized
diff --git a/src/test/run-pass/visible-private-types-feature-gate.rs b/src/test/run-pass/visible-private-types-feature-gate.rs
deleted file mode 100644 (file)
index 4aa0867..0000000
+++ /dev/null
@@ -1,23 +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.
-
-// pretty-expanded FIXME #23616
-
-#![feature(visible_private_types)]
-
-trait Foo { fn dummy(&self) { } }
-
-pub trait Bar : Foo {}
-
-struct Baz;
-
-pub fn f(_: Baz) {}
-
-fn main() {}
index efccff75a49dd080ea0992ee7ef14b93bda85692..9ffba2c7999f1770966643689706329ec802a9eb 100644 (file)
@@ -14,7 +14,7 @@
 use std::collections::BinaryHeap;
 
 fn make_pq() -> BinaryHeap<isize> {
-    BinaryHeap::from_vec(vec![1,2,3])
+    BinaryHeap::from(vec![1,2,3])
 }
 
 pub fn main() {
diff --git a/src/test/run-pass/zero-sized-vec-deque-push.rs b/src/test/run-pass/zero-sized-vec-deque-push.rs
new file mode 100644 (file)
index 0000000..d7c7cd2
--- /dev/null
@@ -0,0 +1,41 @@
+// 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.
+
+use std::collections::VecDeque;
+use std::iter::Iterator;
+
+fn main() {
+    const N: usize = 8;
+
+    // Zero sized type
+    struct Zst;
+
+    // Test that for all possible sequences of push_front / push_back,
+    // we end up with a deque of the correct size
+
+    for len in 0..N {
+        let mut tester = VecDeque::with_capacity(len);
+        assert_eq!(tester.len(), 0);
+        assert!(tester.capacity() >= len);
+        for case in 0..(1 << len) {
+            assert_eq!(tester.len(), 0);
+            for bit in 0..len {
+                if case & (1 << bit) != 0 {
+                    tester.push_front(Zst);
+                } else {
+                    tester.push_back(Zst);
+                }
+            }
+            assert_eq!(tester.len(), len);
+            assert_eq!(tester.iter().count(), len);
+            tester.clear();
+        }
+    }
+}
diff --git a/src/test/rustdoc/deprecated.rs b/src/test/rustdoc/deprecated.rs
new file mode 100644 (file)
index 0000000..744304a
--- /dev/null
@@ -0,0 +1,16 @@
+// 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.
+
+#![feature(deprecated)]
+
+// @has deprecated/struct.S.html '//*[@class="stab deprecated"]' \
+//      'Deprecated since 1.0.0: text'
+#[deprecated(since = "1.0.0", note = "text")]
+pub struct S;